Merge "Allow hover tooltips for disabled views."
diff --git a/Android.bp b/Android.bp
index 40757a9..e23d9c2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -99,6 +99,7 @@
"core/java/android/app/backup/IRestoreSession.aidl",
"core/java/android/app/backup/ISelectBackupTransportCallback.aidl",
"core/java/android/app/slice/ISliceManager.aidl",
+ "core/java/android/app/slice/ISliceListener.aidl",
"core/java/android/app/timezone/ICallback.aidl",
"core/java/android/app/timezone/IRulesManager.aidl",
"core/java/android/app/usage/ICacheQuotaService.aidl",
@@ -214,7 +215,6 @@
"core/java/android/os/IDeviceIdleController.aidl",
"core/java/android/os/IHardwarePropertiesManager.aidl",
"core/java/android/os/IIncidentManager.aidl",
- "core/java/android/os/IIncidentReportCompletedListener.aidl",
"core/java/android/os/IIncidentReportStatusListener.aidl",
"core/java/android/os/IMaintenanceActivityListener.aidl",
"core/java/android/os/IMessenger.aidl",
@@ -462,6 +462,17 @@
"telecomm/java/com/android/internal/telecom/IInCallService.aidl",
"telecomm/java/com/android/internal/telecom/ITelecomService.aidl",
"telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl",
+ "telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl",
+ "telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl",
+ "telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl",
+ "telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl",
+ "telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl",
+ "telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl",
+ "telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl",
+ "telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl",
+ "telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl",
+ "telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl",
+ "telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl",
"telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
"telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl",
@@ -546,12 +557,13 @@
"core/java/android/webkit/EventLogTags.logtags",
"core/java/com/android/internal/logging/EventLogTags.logtags",
"core/java/com/android/server/DropboxLogTags.logtags",
+ "core/java/org/chromium/arc/EventLogTags.logtags",
":framework-statslog-gen",
],
aidl: {
- local_include_dirs: [
+ export_include_dirs: [
// From build/make/core/pathmap.mk FRAMEWORK_BASE_SUBDIRS
"core/java",
"graphics/java",
@@ -622,7 +634,7 @@
name: "framework-statslog-gen",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --java $(out)",
- out: ["android/util/StatsLog.java"],
+ out: ["android/util/StatsLogInternal.java"],
}
gensrcs {
@@ -658,6 +670,7 @@
"libphonenumber-platform",
"nist-sip",
"tagsoup",
+ "rappor",
],
dxflags: ["--core-library"],
}
@@ -676,35 +689,22 @@
"-Werror",
"-Wno-unused-parameter",
],
+
+ srcs: [
+ "core/proto/**/*.proto",
+ "libs/incident/**/*.proto",
+ ],
+
target: {
host: {
proto: {
type: "full",
},
- srcs: [
- "core/proto/**/*.proto",
- "libs/incident/**/*.proto",
- "tools/streaming_proto/stream.proto",
- ],
},
android: {
proto: {
type: "lite",
},
- // We only build the protos that are optimized for the lite
- // runtime, as well as the only protos that are actually
- // needed by the device.
- srcs: [
- "core/proto/android/os/cpufreq.proto",
- "core/proto/android/os/cpuinfo.proto",
- "core/proto/android/os/kernelwake.proto",
- "core/proto/android/os/pagetypeinfo.proto",
- "core/proto/android/os/procrank.proto",
- "core/proto/android/os/system_properties.proto",
- "core/proto/android/service/graphicsstats.proto",
- "libs/incident/proto/android/privacy.proto",
- "tools/streaming_proto/stream.proto",
- ],
shared: {
enabled: false,
},
diff --git a/Android.mk b/Android.mk
index 9676958..995630b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -97,6 +97,7 @@
frameworks/base/core/java/android/app/admin/SystemUpdatePolicy.aidl \
frameworks/base/core/java/android/app/admin/PasswordMetrics.aidl \
frameworks/base/core/java/android/app/slice/ISliceManager.aidl \
+ frameworks/base/core/java/android/app/slice/ISliceListener.aidl \
frameworks/base/core/java/android/print/PrintDocumentInfo.aidl \
frameworks/base/core/java/android/print/PageRange.aidl \
frameworks/base/core/java/android/print/PrintAttributes.aidl \
@@ -273,17 +274,19 @@
../opt/net/voip/src/java/android/net/rtp \
../opt/net/voip/src/java/android/net/sip \
+framework_base_android_test_base_src_files := \
+ $(call all-java-files-under, test-base/src/junit)
+
framework_base_android_test_mock_src_files := \
$(call all-java-files-under, test-mock/src/android/test/mock)
-framework_base_android_test_runner_excluding_mock_src_files := \
- $(filter-out $(framework_base_android_test_mock_src_files), $(call all-java-files-under, test-runner/src))
+framework_base_android_test_runner_src_files := \
+ $(call all-java-files-under, test-runner/src/junit)
# Find all files in specific directories (relative to frameworks/base)
# to document and check apis
files_to_check_apis := \
$(call find-other-java-files, \
- test-base/src \
$(non_base_dirs) \
)
@@ -307,7 +310,7 @@
files_to_document := \
$(files_to_check_apis) \
$(call find-other-java-files,\
- $(addprefix ../../, $(FRAMEWORKS_DATA_BINDING_JAVA_SRC_DIRS)) \
+ test-base/src \
test-runner/src)
# These are relative to frameworks/base
@@ -327,8 +330,9 @@
# These are relative to frameworks/base
framework_docs_LOCAL_API_CHECK_SRC_FILES := \
+ $(framework_base_android_test_base_src_files) \
$(framework_base_android_test_mock_src_files) \
- $(framework_base_android_test_runner_excluding_mock_src_files) \
+ $(framework_base_android_test_runner_src_files) \
$(files_to_check_apis) \
$(common_src_files) \
@@ -999,7 +1003,6 @@
-Iexternal/protobuf/src
LOCAL_SOURCE_FILES_ALL_GENERATED := true
LOCAL_SRC_FILES := \
- tools/streaming_proto/stream.proto \
cmds/am/proto/instrumentation_data.proto \
$(call all-proto-files-under, core/proto) \
$(call all-proto-files-under, libs/incident/proto) \
@@ -1009,7 +1012,7 @@
# ==== java proto device library (for test only) ==============================
include $(CLEAR_VARS)
LOCAL_MODULE := platformprotosnano
-LOCAL_MODULE_TAGS := tests optional
+LOCAL_MODULE_TAGS := tests
LOCAL_PROTOC_OPTIMIZE_TYPE := nano
LOCAL_PROTOC_FLAGS := \
-Iexternal/protobuf/src
@@ -1021,6 +1024,17 @@
include $(BUILD_STATIC_JAVA_LIBRARY)
+# ==== java proto device library (for test only) ==============================
+include $(CLEAR_VARS)
+LOCAL_MODULE := platformprotoslite
+LOCAL_MODULE_TAGS := tests
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite
+LOCAL_PROTOC_FLAGS := \
+ -Iexternal/protobuf/src
+LOCAL_SRC_FILES := \
+ $(call all-proto-files-under, core/proto)
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
# Include subdirectory makefiles
# ============================================================
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index 92ee7cc..5653a03 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -16,6 +16,8 @@
package android.text;
+import static android.text.TextDirectionHeuristics.LTR;
+
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
@@ -182,4 +184,84 @@
.build();
}
}
+
+ @Test
+ public void testCreate_MeasuredText_NoStyled_Greedy_NoHyphenation() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final PremeasuredText text = PremeasuredText.build(
+ generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
+ state.resumeTiming();
+
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .build();
+ }
+ }
+
+ @Test
+ public void testCreate_MeasuredText_NoStyled_Greedy_Hyphenation() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final PremeasuredText text = PremeasuredText.build(
+ generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
+ state.resumeTiming();
+
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .build();
+ }
+ }
+
+ @Test
+ public void testCreate_MeasuredText_NoStyled_Balanced_NoHyphenation() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final PremeasuredText text = PremeasuredText.build(
+ generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
+ state.resumeTiming();
+
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+ .build();
+ }
+ }
+
+ @Test
+ public void testCreate_MeasuredText_NoStyled_Balanced_Hyphenation() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final PremeasuredText text = PremeasuredText.build(
+ generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
+ state.resumeTiming();
+
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+ .build();
+ }
+ }
+
+ @Test
+ public void testCreate_MeasuredText_Styled_Greedy_NoHyphenation() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final PremeasuredText text = PremeasuredText.build(
+ generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT, LTR);
+ state.resumeTiming();
+
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .build();
+ }
+ }
}
diff --git a/api/current.txt b/api/current.txt
index eec7b53..ced8354 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -292,6 +292,7 @@
field public static final int apduServiceBanner = 16843757; // 0x10103ed
field public static final int apiKey = 16843281; // 0x1010211
field public static final int appCategory = 16844101; // 0x1010545
+ field public static final int appComponentFactory = 16844154; // 0x101057a
field public static final int author = 16843444; // 0x10102b4
field public static final int authorities = 16842776; // 0x1010018
field public static final int autoAdvanceViewId = 16843535; // 0x101030f
@@ -3952,7 +3953,7 @@
field public static final android.os.Parcelable.Creator<android.app.ActivityManager.RunningAppProcessInfo> CREATOR;
field public static final deprecated int IMPORTANCE_BACKGROUND = 400; // 0x190
field public static final int IMPORTANCE_CACHED = 400; // 0x190
- field public static final int IMPORTANCE_CANT_SAVE_STATE = 270; // 0x10e
+ field public static final int IMPORTANCE_CANT_SAVE_STATE = 350; // 0x15e
field public static final deprecated int IMPORTANCE_EMPTY = 500; // 0x1f4
field public static final int IMPORTANCE_FOREGROUND = 100; // 0x64
field public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; // 0x7d
@@ -3960,7 +3961,8 @@
field public static final int IMPORTANCE_PERCEPTIBLE = 230; // 0xe6
field public static final int IMPORTANCE_PERCEPTIBLE_PRE_26 = 130; // 0x82
field public static final int IMPORTANCE_SERVICE = 300; // 0x12c
- field public static final int IMPORTANCE_TOP_SLEEPING = 150; // 0x96
+ field public static final int IMPORTANCE_TOP_SLEEPING = 325; // 0x145
+ field public static final deprecated int IMPORTANCE_TOP_SLEEPING_PRE_28 = 150; // 0x96
field public static final int IMPORTANCE_VISIBLE = 200; // 0xc8
field public static final int REASON_PROVIDER_IN_USE = 1; // 0x1
field public static final int REASON_SERVICE_IN_USE = 2; // 0x2
@@ -4174,6 +4176,15 @@
ctor public AliasActivity();
}
+ public class AppComponentFactory {
+ ctor public AppComponentFactory();
+ method public android.app.Activity instantiateActivity(java.lang.ClassLoader, java.lang.String, android.content.Intent) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+ method public android.app.Application instantiateApplication(java.lang.ClassLoader, java.lang.String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+ method public android.content.ContentProvider instantiateProvider(java.lang.ClassLoader, java.lang.String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+ method public android.content.BroadcastReceiver instantiateReceiver(java.lang.ClassLoader, java.lang.String, android.content.Intent) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+ method public android.app.Service instantiateService(java.lang.ClassLoader, java.lang.String, android.content.Intent) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException;
+ }
+
public class AppOpsManager {
method public int checkOp(java.lang.String, int, java.lang.String);
method public int checkOpNoThrow(java.lang.String, int, java.lang.String);
@@ -5189,6 +5200,7 @@
field public static final java.lang.String EXTRA_CONVERSATION_TITLE = "android.conversationTitle";
field public static final java.lang.String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
+ field public static final java.lang.String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
field public static final deprecated java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
@@ -5472,7 +5484,9 @@
method public java.util.List<android.app.Notification.MessagingStyle.Message> getHistoricMessages();
method public java.util.List<android.app.Notification.MessagingStyle.Message> getMessages();
method public java.lang.CharSequence getUserDisplayName();
+ method public boolean isGroupConversation();
method public android.app.Notification.MessagingStyle setConversationTitle(java.lang.CharSequence);
+ method public android.app.Notification.MessagingStyle setGroupConversation(boolean);
field public static final int MAXIMUM_RETAINED_MESSAGES = 25; // 0x19
}
@@ -6313,7 +6327,7 @@
method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
method public void addUserRestriction(android.content.ComponentName, java.lang.String);
method public boolean bindDeviceAdminServiceAsUser(android.content.ComponentName, android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
- method public boolean clearApplicationUserData(android.content.ComponentName, java.lang.String, android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener, android.os.Handler);
+ method public boolean clearApplicationUserData(android.content.ComponentName, java.lang.String, android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener, java.util.concurrent.Executor);
method public void clearCrossProfileIntentFilters(android.content.ComponentName);
method public deprecated void clearDeviceOwnerApp(java.lang.String);
method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
@@ -6344,6 +6358,7 @@
method public java.util.List<java.lang.String> getDelegatedScopes(android.content.ComponentName, java.lang.String);
method public java.lang.CharSequence getDeviceOwnerLockScreenInfo();
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
+ method public java.util.List<java.lang.String> getKeepUninstalledPackages(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
method public int getLockTaskFeatures(android.content.ComponentName);
method public java.lang.String[] getLockTaskPackages(android.content.ComponentName);
@@ -6384,15 +6399,18 @@
method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]);
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean installCaCert(android.content.ComponentName, byte[]);
+ method public boolean installExistingPackage(android.content.ComponentName, java.lang.String);
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean);
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean, boolean);
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(android.content.ComponentName);
+ method public boolean isAffiliatedUser();
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
method public boolean isBackupServiceEnabled(android.content.ComponentName);
method public deprecated boolean isCallerApplicationRestrictionsManagingPackage();
method public boolean isDeviceOwnerApp(java.lang.String);
+ method public boolean isEphemeralUser(android.content.ComponentName);
method public boolean isLockTaskPermitted(java.lang.String);
method public boolean isLogoutEnabled();
method public boolean isManagedProfile(android.content.ComponentName);
@@ -6404,6 +6422,7 @@
method public boolean isResetPasswordTokenActive(android.content.ComponentName);
method public boolean isSecurityLoggingEnabled(android.content.ComponentName);
method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String);
+ method public boolean isUsingUnifiedPassword(android.content.ComponentName);
method public void lockNow();
method public void lockNow(int);
method public boolean logoutUser(android.content.ComponentName);
@@ -6434,6 +6453,8 @@
method public void setDelegatedScopes(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
+ method public void setKeepUninstalledPackages(android.content.ComponentName, java.util.List<java.lang.String>);
+ method public boolean setKeyPairCertificate(android.content.ComponentName, java.lang.String, java.util.List<java.security.cert.Certificate>, boolean);
method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
method public void setLockTaskFeatures(android.content.ComponentName, int);
@@ -6503,6 +6524,8 @@
field public static final java.lang.String DELEGATION_BLOCK_UNINSTALL = "delegation-block-uninstall";
field public static final java.lang.String DELEGATION_CERT_INSTALL = "delegation-cert-install";
field public static final java.lang.String DELEGATION_ENABLE_SYSTEM_APP = "delegation-enable-system-app";
+ field public static final java.lang.String DELEGATION_INSTALL_EXISTING_PACKAGE = "delegation-install-existing-package";
+ field public static final java.lang.String DELEGATION_KEEP_UNINSTALLED_PACKAGES = "delegation-keep-uninstalled-packages";
field public static final java.lang.String DELEGATION_PACKAGE_ACCESS = "delegation-package-access";
field public static final java.lang.String DELEGATION_PERMISSION_GRANT = "delegation-permission-grant";
field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
@@ -6556,6 +6579,7 @@
field public static final int KEYGUARD_DISABLE_TRUST_AGENTS = 16; // 0x10
field public static final int KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS = 8; // 0x8
field public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1; // 0x1
+ field public static final int LEAVE_ALL_SYSTEM_APPS_ENABLED = 16; // 0x10
field public static final int LOCK_TASK_FEATURE_GLOBAL_ACTIONS = 16; // 0x10
field public static final int LOCK_TASK_FEATURE_HOME = 4; // 0x4
field public static final int LOCK_TASK_FEATURE_KEYGUARD = 32; // 0x20
@@ -7001,6 +7025,7 @@
method public android.net.Uri getUri();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR;
+ field public static final java.lang.String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
field public static final java.lang.String HINT_ACTIONS = "actions";
field public static final java.lang.String HINT_HORIZONTAL = "horizontal";
field public static final java.lang.String HINT_LARGE = "large";
@@ -7010,11 +7035,15 @@
field public static final java.lang.String HINT_NO_TINT = "no_tint";
field public static final java.lang.String HINT_PARTIAL = "partial";
field public static final java.lang.String HINT_SELECTED = "selected";
+ field public static final java.lang.String HINT_SHORTCUT = "shortcut";
+ field public static final java.lang.String HINT_SUMMARY = "summary";
field public static final java.lang.String HINT_TITLE = "title";
field public static final java.lang.String SUBTYPE_COLOR = "color";
field public static final java.lang.String SUBTYPE_MESSAGE = "message";
+ field public static final java.lang.String SUBTYPE_PRIORITY = "priority";
field public static final java.lang.String SUBTYPE_SLIDER = "slider";
field public static final java.lang.String SUBTYPE_SOURCE = "source";
+ field public static final java.lang.String SUBTYPE_TOGGLE = "toggle";
}
public static class Slice.Builder {
@@ -7022,6 +7051,8 @@
ctor public Slice.Builder(android.app.slice.Slice.Builder);
method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice);
method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice, java.lang.String);
+ method public android.app.slice.Slice.Builder addBundle(android.os.Bundle, java.lang.String, java.lang.String...);
+ method public android.app.slice.Slice.Builder addBundle(android.os.Bundle, java.lang.String, java.util.List<java.lang.String>);
method public deprecated android.app.slice.Slice.Builder addColor(int, java.lang.String, java.lang.String...);
method public deprecated android.app.slice.Slice.Builder addColor(int, java.lang.String, java.util.List<java.lang.String>);
method public android.app.slice.Slice.Builder addHints(java.lang.String...);
@@ -7045,6 +7076,7 @@
public final class SliceItem implements android.os.Parcelable {
method public int describeContents();
method public android.app.PendingIntent getAction();
+ method public android.os.Bundle getBundle();
method public deprecated int getColor();
method public java.lang.String getFormat();
method public java.util.List<java.lang.String> getHints();
@@ -7059,6 +7091,7 @@
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.slice.SliceItem> CREATOR;
field public static final java.lang.String FORMAT_ACTION = "action";
+ field public static final java.lang.String FORMAT_BUNDLE = "bundle";
field public static final deprecated java.lang.String FORMAT_COLOR = "color";
field public static final java.lang.String FORMAT_IMAGE = "image";
field public static final java.lang.String FORMAT_INT = "int";
@@ -7068,6 +7101,20 @@
field public static final java.lang.String FORMAT_TIMESTAMP = "timestamp";
}
+ public class SliceManager {
+ method public java.util.List<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri);
+ method public void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>);
+ method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, android.os.Handler);
+ method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, java.util.concurrent.Executor);
+ method public void unpinSlice(android.net.Uri);
+ method public void unregisterSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback);
+ }
+
+ public static abstract interface SliceManager.SliceCallback {
+ method public abstract void onSliceUpdated(android.app.slice.Slice);
+ }
+
public abstract class SliceProvider extends android.content.ContentProvider {
ctor public SliceProvider();
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
@@ -7076,6 +7123,8 @@
method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri);
method public android.net.Uri onMapIntentToUri(android.content.Intent);
+ method public void onSlicePinned(android.net.Uri);
+ method public void onSliceUnpinned(android.net.Uri);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
@@ -8097,6 +8146,94 @@
method public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int);
}
+ public final class BluetoothHidDevice implements android.bluetooth.BluetoothProfile {
+ method public boolean connect(android.bluetooth.BluetoothDevice);
+ method public boolean disconnect(android.bluetooth.BluetoothDevice);
+ method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method public int getConnectionState(android.bluetooth.BluetoothDevice);
+ method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+ method public boolean registerApp(android.bluetooth.BluetoothHidDeviceAppSdpSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, android.bluetooth.BluetoothHidDeviceCallback);
+ method public boolean replyReport(android.bluetooth.BluetoothDevice, byte, byte, byte[]);
+ method public boolean reportError(android.bluetooth.BluetoothDevice, byte);
+ method public boolean sendReport(android.bluetooth.BluetoothDevice, int, byte[]);
+ method public boolean unregisterApp();
+ field public static final java.lang.String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED";
+ field public static final byte ERROR_RSP_INVALID_PARAM = 4; // 0x4
+ field public static final byte ERROR_RSP_INVALID_RPT_ID = 2; // 0x2
+ field public static final byte ERROR_RSP_NOT_READY = 1; // 0x1
+ field public static final byte ERROR_RSP_SUCCESS = 0; // 0x0
+ field public static final byte ERROR_RSP_UNKNOWN = 14; // 0xe
+ field public static final byte ERROR_RSP_UNSUPPORTED_REQ = 3; // 0x3
+ field public static final byte PROTOCOL_BOOT_MODE = 0; // 0x0
+ field public static final byte PROTOCOL_REPORT_MODE = 1; // 0x1
+ field public static final byte REPORT_TYPE_FEATURE = 3; // 0x3
+ field public static final byte REPORT_TYPE_INPUT = 1; // 0x1
+ field public static final byte REPORT_TYPE_OUTPUT = 2; // 0x2
+ field public static final byte SUBCLASS1_COMBO = -64; // 0xffffffc0
+ field public static final byte SUBCLASS1_KEYBOARD = 64; // 0x40
+ field public static final byte SUBCLASS1_MOUSE = -128; // 0xffffff80
+ field public static final byte SUBCLASS1_NONE = 0; // 0x0
+ field public static final byte SUBCLASS2_CARD_READER = 6; // 0x6
+ field public static final byte SUBCLASS2_DIGITIZER_TABLET = 5; // 0x5
+ field public static final byte SUBCLASS2_GAMEPAD = 2; // 0x2
+ field public static final byte SUBCLASS2_JOYSTICK = 1; // 0x1
+ field public static final byte SUBCLASS2_REMOTE_CONTROL = 3; // 0x3
+ field public static final byte SUBCLASS2_SENSING_DEVICE = 4; // 0x4
+ field public static final byte SUBCLASS2_UNCATEGORIZED = 0; // 0x0
+ }
+
+ public final class BluetoothHidDeviceAppQosSettings implements android.os.Parcelable {
+ ctor public BluetoothHidDeviceAppQosSettings(int, int, int, int, int, int);
+ method public int describeContents();
+ method public int[] toArray();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHidDeviceAppQosSettings> CREATOR;
+ field public static final int MAX = -1; // 0xffffffff
+ field public static final int SERVICE_BEST_EFFORT = 1; // 0x1
+ field public static final int SERVICE_GUARANTEED = 2; // 0x2
+ field public static final int SERVICE_NO_TRAFFIC = 0; // 0x0
+ field public final int delayVariation;
+ field public final int latency;
+ field public final int peakBandwidth;
+ field public final int serviceType;
+ field public final int tokenBucketSize;
+ field public final int tokenRate;
+ }
+
+ public static class BluetoothHidDeviceAppQosSettings.Builder {
+ ctor public BluetoothHidDeviceAppQosSettings.Builder();
+ method public android.bluetooth.BluetoothHidDeviceAppQosSettings build();
+ method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder delayVariation(int);
+ method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder latency(int);
+ method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder peakBandwidth(int);
+ method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder serviceType(int);
+ method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder tokenBucketSize(int);
+ method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder tokenRate(int);
+ }
+
+ public final class BluetoothHidDeviceAppSdpSettings implements android.os.Parcelable {
+ ctor public BluetoothHidDeviceAppSdpSettings(java.lang.String, java.lang.String, java.lang.String, byte, byte[]);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHidDeviceAppSdpSettings> CREATOR;
+ field public final java.lang.String description;
+ field public final byte[] descriptors;
+ field public final java.lang.String name;
+ field public final java.lang.String provider;
+ field public final byte subclass;
+ }
+
+ public abstract class BluetoothHidDeviceCallback {
+ ctor public BluetoothHidDeviceCallback();
+ method public void onAppStatusChanged(android.bluetooth.BluetoothDevice, boolean);
+ method public void onConnectionStateChanged(android.bluetooth.BluetoothDevice, int);
+ method public void onGetReport(android.bluetooth.BluetoothDevice, byte, byte, int);
+ method public void onInterruptData(android.bluetooth.BluetoothDevice, byte, byte[]);
+ method public void onSetProtocol(android.bluetooth.BluetoothDevice, byte);
+ method public void onSetReport(android.bluetooth.BluetoothDevice, byte, byte, byte[]);
+ method public void onVirtualCableUnplug(android.bluetooth.BluetoothDevice);
+ }
+
public final class BluetoothManager {
method public android.bluetooth.BluetoothAdapter getAdapter();
method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int);
@@ -8116,6 +8253,7 @@
field public static final int GATT_SERVER = 8; // 0x8
field public static final int HEADSET = 1; // 0x1
field public static final int HEALTH = 3; // 0x3
+ field public static final int HID_DEVICE = 19; // 0x13
field public static final int SAP = 10; // 0xa
field public static final int STATE_CONNECTED = 2; // 0x2
field public static final int STATE_CONNECTING = 1; // 0x1
@@ -9039,6 +9177,7 @@
method public abstract java.io.File[] getExternalMediaDirs();
method public abstract java.io.File getFileStreamPath(java.lang.String);
method public abstract java.io.File getFilesDir();
+ method public java.util.concurrent.Executor getMainExecutor();
method public abstract android.os.Looper getMainLooper();
method public abstract java.io.File getNoBackupFilesDir();
method public abstract java.io.File getObbDir();
@@ -10398,6 +10537,7 @@
field public static final int FLAG_UPDATED_SYSTEM_APP = 128; // 0x80
field public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 134217728; // 0x8000000
field public static final int FLAG_VM_SAFE_MODE = 16384; // 0x4000
+ field public java.lang.String appComponentFactory;
field public java.lang.String backupAgentName;
field public int category;
field public java.lang.String className;
@@ -16510,6 +16650,17 @@
field public static final int KNOTTED_HEH = 21; // 0x15
field public static final int LAM = 22; // 0x16
field public static final int LAMADH = 23; // 0x17
+ field public static final int MALAYALAM_BHA = 89; // 0x59
+ field public static final int MALAYALAM_JA = 90; // 0x5a
+ field public static final int MALAYALAM_LLA = 91; // 0x5b
+ field public static final int MALAYALAM_LLLA = 92; // 0x5c
+ field public static final int MALAYALAM_NGA = 93; // 0x5d
+ field public static final int MALAYALAM_NNA = 94; // 0x5e
+ field public static final int MALAYALAM_NNNA = 95; // 0x5f
+ field public static final int MALAYALAM_NYA = 96; // 0x60
+ field public static final int MALAYALAM_RA = 97; // 0x61
+ field public static final int MALAYALAM_SSA = 98; // 0x62
+ field public static final int MALAYALAM_TTA = 99; // 0x63
field public static final int MANICHAEAN_ALEPH = 58; // 0x3a
field public static final int MANICHAEAN_AYIN = 59; // 0x3b
field public static final int MANICHAEAN_BETH = 60; // 0x3c
@@ -16765,6 +16916,8 @@
field public static final int CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D_ID = 209; // 0xd1
field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E;
field public static final int CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E_ID = 256; // 0x100
+ field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F;
+ field public static final int CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F_ID = 274; // 0x112
field public static final int CJK_UNIFIED_IDEOGRAPHS_ID = 71; // 0x47
field public static final android.icu.lang.UCharacter.UnicodeBlock COMBINING_DIACRITICAL_MARKS;
field public static final android.icu.lang.UCharacter.UnicodeBlock COMBINING_DIACRITICAL_MARKS_EXTENDED;
@@ -16910,6 +17063,8 @@
field public static final int JAVANESE_ID = 181; // 0xb5
field public static final android.icu.lang.UCharacter.UnicodeBlock KAITHI;
field public static final int KAITHI_ID = 193; // 0xc1
+ field public static final android.icu.lang.UCharacter.UnicodeBlock KANA_EXTENDED_A;
+ field public static final int KANA_EXTENDED_A_ID = 275; // 0x113
field public static final android.icu.lang.UCharacter.UnicodeBlock KANA_SUPPLEMENT;
field public static final int KANA_SUPPLEMENT_ID = 203; // 0xcb
field public static final android.icu.lang.UCharacter.UnicodeBlock KANBUN;
@@ -16982,6 +17137,8 @@
field public static final int MANICHAEAN_ID = 234; // 0xea
field public static final android.icu.lang.UCharacter.UnicodeBlock MARCHEN;
field public static final int MARCHEN_ID = 268; // 0x10c
+ field public static final android.icu.lang.UCharacter.UnicodeBlock MASARAM_GONDI;
+ field public static final int MASARAM_GONDI_ID = 276; // 0x114
field public static final android.icu.lang.UCharacter.UnicodeBlock MATHEMATICAL_ALPHANUMERIC_SYMBOLS;
field public static final int MATHEMATICAL_ALPHANUMERIC_SYMBOLS_ID = 93; // 0x5d
field public static final android.icu.lang.UCharacter.UnicodeBlock MATHEMATICAL_OPERATORS;
@@ -17041,6 +17198,8 @@
field public static final android.icu.lang.UCharacter.UnicodeBlock NO_BLOCK;
field public static final android.icu.lang.UCharacter.UnicodeBlock NUMBER_FORMS;
field public static final int NUMBER_FORMS_ID = 45; // 0x2d
+ field public static final android.icu.lang.UCharacter.UnicodeBlock NUSHU;
+ field public static final int NUSHU_ID = 277; // 0x115
field public static final android.icu.lang.UCharacter.UnicodeBlock OGHAM;
field public static final int OGHAM_ID = 34; // 0x22
field public static final android.icu.lang.UCharacter.UnicodeBlock OLD_HUNGARIAN;
@@ -17119,6 +17278,8 @@
field public static final int SMALL_FORM_VARIANTS_ID = 84; // 0x54
field public static final android.icu.lang.UCharacter.UnicodeBlock SORA_SOMPENG;
field public static final int SORA_SOMPENG_ID = 218; // 0xda
+ field public static final android.icu.lang.UCharacter.UnicodeBlock SOYOMBO;
+ field public static final int SOYOMBO_ID = 278; // 0x116
field public static final android.icu.lang.UCharacter.UnicodeBlock SPACING_MODIFIER_LETTERS;
field public static final int SPACING_MODIFIER_LETTERS_ID = 6; // 0x6
field public static final android.icu.lang.UCharacter.UnicodeBlock SPECIALS;
@@ -17151,6 +17312,8 @@
field public static final int SYLOTI_NAGRI_ID = 143; // 0x8f
field public static final android.icu.lang.UCharacter.UnicodeBlock SYRIAC;
field public static final int SYRIAC_ID = 13; // 0xd
+ field public static final android.icu.lang.UCharacter.UnicodeBlock SYRIAC_SUPPLEMENT;
+ field public static final int SYRIAC_SUPPLEMENT_ID = 279; // 0x117
field public static final android.icu.lang.UCharacter.UnicodeBlock TAGALOG;
field public static final int TAGALOG_ID = 98; // 0x62
field public static final android.icu.lang.UCharacter.UnicodeBlock TAGBANWA;
@@ -17211,6 +17374,8 @@
field public static final int YI_RADICALS_ID = 73; // 0x49
field public static final android.icu.lang.UCharacter.UnicodeBlock YI_SYLLABLES;
field public static final int YI_SYLLABLES_ID = 72; // 0x48
+ field public static final android.icu.lang.UCharacter.UnicodeBlock ZANABAZAR_SQUARE;
+ field public static final int ZANABAZAR_SQUARE_ID = 280; // 0x118
}
public static abstract interface UCharacter.WordBreak {
@@ -17361,6 +17526,11 @@
field public static final int DIACRITIC = 7; // 0x7
field public static final int DOUBLE_START = 12288; // 0x3000
field public static final int EAST_ASIAN_WIDTH = 4100; // 0x1004
+ field public static final int EMOJI = 57; // 0x39
+ field public static final int EMOJI_COMPONENT = 61; // 0x3d
+ field public static final int EMOJI_MODIFIER = 59; // 0x3b
+ field public static final int EMOJI_MODIFIER_BASE = 60; // 0x3c
+ field public static final int EMOJI_PRESENTATION = 58; // 0x3a
field public static final int EXTENDER = 8; // 0x8
field public static final int FULL_COMPOSITION_EXCLUSION = 9; // 0x9
field public static final int GENERAL_CATEGORY = 4101; // 0x1005
@@ -17408,8 +17578,10 @@
field public static final int POSIX_GRAPH = 46; // 0x2e
field public static final int POSIX_PRINT = 47; // 0x2f
field public static final int POSIX_XDIGIT = 48; // 0x30
+ field public static final int PREPENDED_CONCATENATION_MARK = 63; // 0x3f
field public static final int QUOTATION_MARK = 25; // 0x19
field public static final int RADICAL = 26; // 0x1a
+ field public static final int REGIONAL_INDICATOR = 62; // 0x3e
field public static final int SCRIPT = 4106; // 0x100a
field public static final int SCRIPT_EXTENSIONS = 28672; // 0x7000
field public static final int SEGMENT_STARTER = 41; // 0x29
@@ -17551,6 +17723,7 @@
field public static final int MANDAIC = 84; // 0x54
field public static final int MANICHAEAN = 121; // 0x79
field public static final int MARCHEN = 169; // 0xa9
+ field public static final int MASARAM_GONDI = 175; // 0xaf
field public static final int MATHEMATICAL_NOTATION = 128; // 0x80
field public static final int MAYAN_HIEROGLYPHS = 85; // 0x55
field public static final int MEITEI_MAYEK = 115; // 0x73
@@ -17605,6 +17778,7 @@
field public static final int SINDHI = 145; // 0x91
field public static final int SINHALA = 33; // 0x21
field public static final int SORA_SOMPENG = 152; // 0x98
+ field public static final int SOYOMBO = 176; // 0xb0
field public static final int SUNDANESE = 113; // 0x71
field public static final int SYLOTI_NAGRI = 58; // 0x3a
field public static final int SYMBOLS = 129; // 0x81
@@ -17635,6 +17809,7 @@
field public static final int WESTERN_SYRIAC = 96; // 0x60
field public static final int WOLEAI = 155; // 0x9b
field public static final int YI = 41; // 0x29
+ field public static final int ZANABAZAR_SQUARE = 177; // 0xb1
}
public static final class UScript.ScriptUsage extends java.lang.Enum {
@@ -18429,11 +18604,14 @@
method public android.icu.util.Currency getCurrency();
method public java.lang.String getCurrencySymbol();
method public char getDecimalSeparator();
+ method public java.lang.String getDecimalSeparatorString();
method public char getDigit();
+ method public java.lang.String[] getDigitStrings();
method public char[] getDigits();
method public java.lang.String getExponentMultiplicationSign();
method public java.lang.String getExponentSeparator();
method public char getGroupingSeparator();
+ method public java.lang.String getGroupingSeparatorString();
method public java.lang.String getInfinity();
method public static android.icu.text.DecimalFormatSymbols getInstance();
method public static android.icu.text.DecimalFormatSymbols getInstance(java.util.Locale);
@@ -18441,37 +18619,52 @@
method public java.lang.String getInternationalCurrencySymbol();
method public java.util.Locale getLocale();
method public char getMinusSign();
+ method public java.lang.String getMinusSignString();
method public char getMonetaryDecimalSeparator();
+ method public java.lang.String getMonetaryDecimalSeparatorString();
method public char getMonetaryGroupingSeparator();
+ method public java.lang.String getMonetaryGroupingSeparatorString();
method public java.lang.String getNaN();
method public char getPadEscape();
method public java.lang.String getPatternForCurrencySpacing(int, boolean);
method public char getPatternSeparator();
method public char getPerMill();
+ method public java.lang.String getPerMillString();
method public char getPercent();
+ method public java.lang.String getPercentString();
method public char getPlusSign();
+ method public java.lang.String getPlusSignString();
method public char getSignificantDigit();
method public android.icu.util.ULocale getULocale();
method public char getZeroDigit();
method public void setCurrency(android.icu.util.Currency);
method public void setCurrencySymbol(java.lang.String);
method public void setDecimalSeparator(char);
+ method public void setDecimalSeparatorString(java.lang.String);
method public void setDigit(char);
+ method public void setDigitStrings(java.lang.String[]);
method public void setExponentMultiplicationSign(java.lang.String);
method public void setExponentSeparator(java.lang.String);
method public void setGroupingSeparator(char);
+ method public void setGroupingSeparatorString(java.lang.String);
method public void setInfinity(java.lang.String);
method public void setInternationalCurrencySymbol(java.lang.String);
method public void setMinusSign(char);
+ method public void setMinusSignString(java.lang.String);
method public void setMonetaryDecimalSeparator(char);
+ method public void setMonetaryDecimalSeparatorString(java.lang.String);
method public void setMonetaryGroupingSeparator(char);
+ method public void setMonetaryGroupingSeparatorString(java.lang.String);
method public void setNaN(java.lang.String);
method public void setPadEscape(char);
method public void setPatternForCurrencySpacing(int, boolean, java.lang.String);
method public void setPatternSeparator(char);
method public void setPerMill(char);
+ method public void setPerMillString(java.lang.String);
method public void setPercent(char);
+ method public void setPercentString(java.lang.String);
method public void setPlusSign(char);
+ method public void setPlusSignString(java.lang.String);
method public void setSignificantDigit(char);
method public void setZeroDigit(char);
field public static final int CURRENCY_SPC_CURRENCY_MATCH = 0; // 0x0
@@ -18492,7 +18685,9 @@
enum_constant public static final android.icu.text.DisplayContext DIALECT_NAMES;
enum_constant public static final android.icu.text.DisplayContext LENGTH_FULL;
enum_constant public static final android.icu.text.DisplayContext LENGTH_SHORT;
+ enum_constant public static final android.icu.text.DisplayContext NO_SUBSTITUTE;
enum_constant public static final android.icu.text.DisplayContext STANDARD_NAMES;
+ enum_constant public static final android.icu.text.DisplayContext SUBSTITUTE;
}
public static final class DisplayContext.Type extends java.lang.Enum {
@@ -18501,6 +18696,7 @@
enum_constant public static final android.icu.text.DisplayContext.Type CAPITALIZATION;
enum_constant public static final android.icu.text.DisplayContext.Type DIALECT_HANDLING;
enum_constant public static final android.icu.text.DisplayContext.Type DISPLAY_LENGTH;
+ enum_constant public static final android.icu.text.DisplayContext.Type SUBSTITUTE_HANDLING;
}
public abstract class IDNA {
@@ -18608,6 +18804,7 @@
method public static android.icu.text.MeasureFormat getInstance(java.util.Locale, android.icu.text.MeasureFormat.FormatWidth, android.icu.text.NumberFormat);
method public final android.icu.util.ULocale getLocale();
method public android.icu.text.NumberFormat getNumberFormat();
+ method public java.lang.String getUnitDisplayName(android.icu.util.MeasureUnit);
method public android.icu.text.MeasureFormat.FormatWidth getWidth();
method public final int hashCode();
method public android.icu.util.Measure parseObject(java.lang.String, java.text.ParsePosition);
@@ -20146,6 +20343,7 @@
field public static final android.icu.util.MeasureUnit MILLILITER;
field public static final android.icu.util.MeasureUnit MILLIMETER;
field public static final android.icu.util.MeasureUnit MILLIMETER_OF_MERCURY;
+ field public static final android.icu.util.MeasureUnit MILLIMOLE_PER_LITER;
field public static final android.icu.util.MeasureUnit MILLISECOND;
field public static final android.icu.util.MeasureUnit MILLIWATT;
field public static final android.icu.util.TimeUnit MINUTE;
@@ -20157,6 +20355,7 @@
field public static final android.icu.util.MeasureUnit OUNCE;
field public static final android.icu.util.MeasureUnit OUNCE_TROY;
field public static final android.icu.util.MeasureUnit PARSEC;
+ field public static final android.icu.util.MeasureUnit PART_PER_MILLION;
field public static final android.icu.util.MeasureUnit PICOMETER;
field public static final android.icu.util.MeasureUnit PINT;
field public static final android.icu.util.MeasureUnit PINT_METRIC;
@@ -20280,6 +20479,9 @@
public static final class TimeZone.SystemTimeZoneType extends java.lang.Enum {
method public static android.icu.util.TimeZone.SystemTimeZoneType valueOf(java.lang.String);
method public static final android.icu.util.TimeZone.SystemTimeZoneType[] values();
+ enum_constant public static final android.icu.util.TimeZone.SystemTimeZoneType ANY;
+ enum_constant public static final android.icu.util.TimeZone.SystemTimeZoneType CANONICAL;
+ enum_constant public static final android.icu.util.TimeZone.SystemTimeZoneType CANONICAL_LOCATION;
}
public final class ULocale implements java.lang.Comparable java.io.Serializable {
@@ -20481,6 +20683,7 @@
field public static final android.icu.util.VersionInfo ICU_VERSION;
field public static final android.icu.util.VersionInfo UCOL_BUILDER_VERSION;
field public static final android.icu.util.VersionInfo UCOL_RUNTIME_VERSION;
+ field public static final android.icu.util.VersionInfo UNICODE_10_0;
field public static final android.icu.util.VersionInfo UNICODE_1_0;
field public static final android.icu.util.VersionInfo UNICODE_1_0_1;
field public static final android.icu.util.VersionInfo UNICODE_1_1_0;
@@ -20981,6 +21184,7 @@
method public void onGnssMeasurementsReceived(android.location.GnssMeasurementsEvent);
method public void onStatusChanged(int);
field public static final int STATUS_LOCATION_DISABLED = 2; // 0x2
+ field public static final int STATUS_NOT_ALLOWED = 3; // 0x3
field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
field public static final int STATUS_READY = 1; // 0x1
}
@@ -25894,12 +26098,12 @@
}
public final class IpSecManager {
+ method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
+ method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
- method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
- method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
}
public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
@@ -30957,7 +31161,9 @@
ctor public Build.VERSION();
field public static final java.lang.String BASE_OS;
field public static final java.lang.String CODENAME;
+ field public static final int FIRST_SDK_INT;
field public static final java.lang.String INCREMENTAL;
+ field public static final int MIN_SUPPORTED_TARGET_SDK_INT;
field public static final int PREVIEW_SDK_INT;
field public static final java.lang.String RELEASE;
field public static final deprecated java.lang.String SDK;
@@ -32096,6 +32302,7 @@
field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
field public static final java.lang.String DISALLOW_SMS = "no_sms";
field public static final java.lang.String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
+ field public static final java.lang.String DISALLOW_UNIFIED_PASSWORD = "no_unified_password";
field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
@@ -37485,6 +37692,22 @@
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.widget.RemoteViews);
}
+ public final class EditDistanceScorer implements android.os.Parcelable android.service.autofill.Scorer {
+ method public int describeContents();
+ method public static android.service.autofill.EditDistanceScorer getInstance();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.EditDistanceScorer> CREATOR;
+ }
+
+ public final class FieldClassification {
+ method public java.util.List<android.service.autofill.FieldClassification.Match> getMatches();
+ }
+
+ public static final class FieldClassification.Match {
+ method public java.lang.String getRemoteId();
+ method public float getScore();
+ }
+
public final class FillCallback {
method public void onFailure(java.lang.CharSequence);
method public void onSuccess(android.service.autofill.FillResponse);
@@ -37510,6 +37733,7 @@
method public java.util.Map<android.view.autofill.AutofillId, java.lang.String> getChangedFields();
method public android.os.Bundle getClientState();
method public java.lang.String getDatasetId();
+ method public java.util.Map<android.view.autofill.AutofillId, android.service.autofill.FieldClassification> getFieldsClassification();
method public java.util.Set<java.lang.String> getIgnoredDatasetIds();
method public java.util.Map<android.view.autofill.AutofillId, java.util.Set<java.lang.String>> getManuallyEnteredField();
method public java.util.Set<java.lang.String> getSelectedDatasetIds();
@@ -37547,6 +37771,7 @@
method public android.service.autofill.FillResponse.Builder disableAutofill(long);
method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews);
method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle);
+ method public android.service.autofill.FillResponse.Builder setFieldClassificationIds(android.view.autofill.AutofillId...);
method public android.service.autofill.FillResponse.Builder setFlags(int);
method public android.service.autofill.FillResponse.Builder setFooter(android.widget.RemoteViews);
method public android.service.autofill.FillResponse.Builder setHeader(android.widget.RemoteViews);
@@ -37630,6 +37855,9 @@
field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
}
+ public abstract interface Scorer {
+ }
+
public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
method public int describeContents();
@@ -37640,6 +37868,22 @@
public abstract interface Transformation {
}
+ public final class UserData implements android.os.Parcelable {
+ method public int describeContents();
+ method public static int getMaxFieldClassificationIdsSize();
+ method public static int getMaxUserDataSize();
+ method public static int getMaxValueLength();
+ method public static int getMinValueLength();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.UserData> CREATOR;
+ }
+
+ public static final class UserData.Builder {
+ ctor public UserData.Builder(android.service.autofill.Scorer, java.lang.String, java.lang.String);
+ method public android.service.autofill.UserData.Builder add(java.lang.String, java.lang.String);
+ method public android.service.autofill.UserData build();
+ }
+
public abstract interface Validator {
}
@@ -40104,6 +40348,7 @@
public class CarrierConfigManager {
method public android.os.PersistableBundle getConfig();
method public android.os.PersistableBundle getConfigForSubId(int);
+ method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle);
method public void notifyConfigChangedForSubId(int);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
@@ -40219,10 +40464,12 @@
field public static final java.lang.String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
field public static final java.lang.String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
field public static final java.lang.String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL = "restart_radio_on_pdp_fail_regular_deactivation_bool";
+ field public static final java.lang.String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool";
field public static final java.lang.String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
field public static final java.lang.String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
field public static final java.lang.String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
+ field public static final java.lang.String KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL = "show_signal_strength_in_sim_status_bool";
field public static final java.lang.String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
field public static final java.lang.String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
@@ -40824,6 +41071,7 @@
method public java.lang.String getMeid(int);
method public java.lang.String getMmsUAProfUrl();
method public java.lang.String getMmsUserAgent();
+ method public java.lang.String getNai();
method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
method public java.lang.String getNetworkCountryIso();
method public java.lang.String getNetworkOperator();
@@ -40841,6 +41089,8 @@
method public int getSimState();
method public int getSimState(int);
method public java.lang.String getSubscriberId();
+ method public int getSubscriptionCarrierId();
+ method public java.lang.String getSubscriptionCarrierName();
method public java.lang.String getVisualVoicemailPackageName();
method public java.lang.String getVoiceMailAlphaTag();
method public java.lang.String getVoiceMailNumber();
@@ -40855,11 +41105,12 @@
method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String);
method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
method public boolean isConcurrentVoiceAndDataSupported();
- method public boolean isDataEnabled();
+ method public deprecated boolean isDataEnabled();
method public boolean isHearingAidCompatibilitySupported();
method public boolean isNetworkRoaming();
method public boolean isSmsCapable();
method public deprecated boolean isTtyModeSupported();
+ method public boolean isUserMobileDataEnabled();
method public boolean isVoiceCapable();
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
@@ -40868,18 +41119,20 @@
method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
method public void sendVisualVoicemailSms(java.lang.String, int, java.lang.String, android.app.PendingIntent);
- method public void setDataEnabled(boolean);
+ method public deprecated void setDataEnabled(boolean);
method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
method public boolean setOperatorBrandOverride(java.lang.String);
method public boolean setPreferredNetworkTypeToGlobal();
+ method public void setUserMobileDataEnabled(boolean);
method public void setVisualVoicemailSmsFilterSettings(android.telephony.VisualVoicemailSmsFilterSettings);
method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
- method public void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
- method public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean);
+ method public deprecated void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
+ method public deprecated void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean);
field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
field public static final java.lang.String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
+ field public static final java.lang.String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED";
field public static final int APPTYPE_CSIM = 4; // 0x4
field public static final int APPTYPE_ISIM = 5; // 0x5
field public static final int APPTYPE_RUIM = 3; // 0x3
@@ -40904,6 +41157,8 @@
field public static final int DATA_DISCONNECTED = 0; // 0x0
field public static final int DATA_SUSPENDED = 3; // 0x3
field public static final java.lang.String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
+ field public static final java.lang.String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
+ field public static final java.lang.String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME";
field public static final java.lang.String EXTRA_HIDE_PUBLIC_SETTINGS = "android.telephony.extra.HIDE_PUBLIC_SETTINGS";
field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number";
field public static final java.lang.String EXTRA_IS_REFRESH = "android.telephony.extra.IS_REFRESH";
@@ -40914,6 +41169,7 @@
field public static final java.lang.String EXTRA_STATE_IDLE;
field public static final java.lang.String EXTRA_STATE_OFFHOOK;
field public static final java.lang.String EXTRA_STATE_RINGING;
+ field public static final java.lang.String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
field public static final java.lang.String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
field public static final java.lang.String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU = "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU";
field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
@@ -40949,6 +41205,7 @@
field public static final int SIM_STATE_PUK_REQUIRED = 3; // 0x3
field public static final int SIM_STATE_READY = 5; // 0x5
field public static final int SIM_STATE_UNKNOWN = 0; // 0x0
+ field public static final int UNKNOWN_CARRIER_ID = -1; // 0xffffffff
field public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; // 0xfffffffe
field public static final int USSD_RETURN_FAILURE = -1; // 0xffffffff
field public static final java.lang.String VVM_TYPE_CVVM = "vvm_type_cvvm";
@@ -41262,314 +41519,6 @@
}
-package android.test {
-
- public abstract deprecated class ActivityInstrumentationTestCase<T extends android.app.Activity> extends android.test.ActivityTestCase {
- ctor public ActivityInstrumentationTestCase(java.lang.String, java.lang.Class<T>);
- ctor public ActivityInstrumentationTestCase(java.lang.String, java.lang.Class<T>, boolean);
- method public T getActivity();
- method public void testActivityTestCaseSetUpProperly() throws java.lang.Exception;
- }
-
- public abstract deprecated class ActivityInstrumentationTestCase2<T extends android.app.Activity> extends android.test.ActivityTestCase {
- ctor public deprecated ActivityInstrumentationTestCase2(java.lang.String, java.lang.Class<T>);
- ctor public ActivityInstrumentationTestCase2(java.lang.Class<T>);
- method public T getActivity();
- method public void setActivityInitialTouchMode(boolean);
- method public void setActivityIntent(android.content.Intent);
- }
-
- public abstract deprecated class ActivityTestCase extends android.test.InstrumentationTestCase {
- ctor public ActivityTestCase();
- method protected android.app.Activity getActivity();
- method protected void scrubClass(java.lang.Class<?>) throws java.lang.IllegalAccessException;
- method protected void setActivity(android.app.Activity);
- }
-
- public abstract deprecated class ActivityUnitTestCase<T extends android.app.Activity> extends android.test.ActivityTestCase {
- ctor public ActivityUnitTestCase(java.lang.Class<T>);
- method public T getActivity();
- method public int getFinishedActivityRequest();
- method public int getRequestedOrientation();
- method public android.content.Intent getStartedActivityIntent();
- method public int getStartedActivityRequest();
- method public boolean isFinishCalled();
- method public void setActivityContext(android.content.Context);
- method public void setApplication(android.app.Application);
- method protected T startActivity(android.content.Intent, android.os.Bundle, java.lang.Object);
- }
-
- public deprecated class AndroidTestCase extends junit.framework.TestCase {
- ctor public AndroidTestCase();
- method public void assertActivityRequiresPermission(java.lang.String, java.lang.String, java.lang.String);
- method public void assertReadingContentUriRequiresPermission(android.net.Uri, java.lang.String);
- method public void assertWritingContentUriRequiresPermission(android.net.Uri, java.lang.String);
- method public android.content.Context getContext();
- method protected void scrubClass(java.lang.Class<?>) throws java.lang.IllegalAccessException;
- method public void setContext(android.content.Context);
- method public void testAndroidTestCaseSetupProperly();
- field protected android.content.Context mContext;
- }
-
- public deprecated class AndroidTestRunner extends junit.runner.BaseTestRunner {
- ctor public AndroidTestRunner();
- method public void addTestListener(junit.framework.TestListener);
- method public void clearTestListeners();
- method protected junit.framework.TestResult createTestResult();
- method public java.util.List<junit.framework.TestCase> getTestCases();
- method public java.lang.String getTestClassName();
- method public junit.framework.TestResult getTestResult();
- method protected java.lang.Class loadSuiteClass(java.lang.String) throws java.lang.ClassNotFoundException;
- method protected void runFailed(java.lang.String);
- method public void runTest();
- method public void runTest(junit.framework.TestResult);
- method public void setContext(android.content.Context);
- method public deprecated void setInstrumentaiton(android.app.Instrumentation);
- method public void setInstrumentation(android.app.Instrumentation);
- method public void setTest(junit.framework.Test);
- method public void setTestClassName(java.lang.String, java.lang.String);
- method public void testEnded(java.lang.String);
- method public void testFailed(int, junit.framework.Test, java.lang.Throwable);
- method public void testStarted(java.lang.String);
- }
-
- public abstract deprecated class ApplicationTestCase<T extends android.app.Application> extends android.test.AndroidTestCase {
- ctor public ApplicationTestCase(java.lang.Class<T>);
- method protected final void createApplication();
- method public T getApplication();
- method public android.content.Context getSystemContext();
- method protected final void terminateApplication();
- method public final void testApplicationTestCaseSetUpProperly() throws java.lang.Exception;
- }
-
- public deprecated class AssertionFailedError extends java.lang.Error {
- ctor public AssertionFailedError();
- ctor public AssertionFailedError(java.lang.String);
- }
-
- public deprecated class ComparisonFailure extends android.test.AssertionFailedError {
- ctor public ComparisonFailure(java.lang.String, java.lang.String, java.lang.String);
- }
-
- public abstract deprecated class FlakyTest implements java.lang.annotation.Annotation {
- }
-
- public deprecated class InstrumentationTestCase extends junit.framework.TestCase {
- ctor public InstrumentationTestCase();
- method public android.app.Instrumentation getInstrumentation();
- method public deprecated void injectInsrumentation(android.app.Instrumentation);
- method public void injectInstrumentation(android.app.Instrumentation);
- method public final <T extends android.app.Activity> T launchActivity(java.lang.String, java.lang.Class<T>, android.os.Bundle);
- method public final <T extends android.app.Activity> T launchActivityWithIntent(java.lang.String, java.lang.Class<T>, android.content.Intent);
- method public void runTestOnUiThread(java.lang.Runnable) throws java.lang.Throwable;
- method public void sendKeys(java.lang.String);
- method public void sendKeys(int...);
- method public void sendRepeatedKeys(int...);
- }
-
- public deprecated class InstrumentationTestRunner extends android.app.Instrumentation implements android.test.TestSuiteProvider {
- ctor public InstrumentationTestRunner();
- method public junit.framework.TestSuite getAllTests();
- method protected android.test.AndroidTestRunner getAndroidTestRunner();
- method public android.os.Bundle getArguments();
- method public java.lang.ClassLoader getLoader();
- method public junit.framework.TestSuite getTestSuite();
- field public static final java.lang.String REPORT_KEY_NAME_CLASS = "class";
- field public static final java.lang.String REPORT_KEY_NAME_TEST = "test";
- field public static final java.lang.String REPORT_KEY_NUM_CURRENT = "current";
- field public static final java.lang.String REPORT_KEY_NUM_TOTAL = "numtests";
- field public static final java.lang.String REPORT_KEY_STACK = "stack";
- field public static final java.lang.String REPORT_VALUE_ID = "InstrumentationTestRunner";
- field public static final int REPORT_VALUE_RESULT_ERROR = -1; // 0xffffffff
- field public static final int REPORT_VALUE_RESULT_FAILURE = -2; // 0xfffffffe
- field public static final int REPORT_VALUE_RESULT_OK = 0; // 0x0
- field public static final int REPORT_VALUE_RESULT_START = 1; // 0x1
- }
-
- public deprecated class InstrumentationTestSuite extends junit.framework.TestSuite {
- ctor public InstrumentationTestSuite(android.app.Instrumentation);
- ctor public InstrumentationTestSuite(java.lang.String, android.app.Instrumentation);
- ctor public InstrumentationTestSuite(java.lang.Class, android.app.Instrumentation);
- method public void addTestSuite(java.lang.Class);
- }
-
- public deprecated class IsolatedContext extends android.content.ContextWrapper {
- ctor public IsolatedContext(android.content.ContentResolver, android.content.Context);
- method public java.util.List<android.content.Intent> getAndClearBroadcastIntents();
- }
-
- public class LoaderTestCase extends android.test.AndroidTestCase {
- ctor public LoaderTestCase();
- method public <T> T getLoaderResultSynchronously(android.content.Loader<T>);
- }
-
- public final deprecated class MoreAsserts {
- method public static void assertAssignableFrom(java.lang.Class<?>, java.lang.Object);
- method public static void assertAssignableFrom(java.lang.Class<?>, java.lang.Class<?>);
- method public static java.util.regex.MatchResult assertContainsRegex(java.lang.String, java.lang.String, java.lang.String);
- method public static java.util.regex.MatchResult assertContainsRegex(java.lang.String, java.lang.String);
- method public static void assertContentsInAnyOrder(java.lang.String, java.lang.Iterable<?>, java.lang.Object...);
- method public static void assertContentsInAnyOrder(java.lang.Iterable<?>, java.lang.Object...);
- method public static void assertContentsInOrder(java.lang.String, java.lang.Iterable<?>, java.lang.Object...);
- method public static void assertContentsInOrder(java.lang.Iterable<?>, java.lang.Object...);
- method public static void assertEmpty(java.lang.String, java.lang.Iterable<?>);
- method public static void assertEmpty(java.lang.Iterable<?>);
- method public static void assertEmpty(java.lang.String, java.util.Map<?, ?>);
- method public static void assertEmpty(java.util.Map<?, ?>);
- method public static void assertEquals(java.lang.String, byte[], byte[]);
- method public static void assertEquals(byte[], byte[]);
- method public static void assertEquals(java.lang.String, int[], int[]);
- method public static void assertEquals(int[], int[]);
- method public static void assertEquals(java.lang.String, double[], double[]);
- method public static void assertEquals(double[], double[]);
- method public static void assertEquals(java.lang.String, java.lang.Object[], java.lang.Object[]);
- method public static void assertEquals(java.lang.Object[], java.lang.Object[]);
- method public static void assertEquals(java.lang.String, java.util.Set<? extends java.lang.Object>, java.util.Set<? extends java.lang.Object>);
- method public static void assertEquals(java.util.Set<? extends java.lang.Object>, java.util.Set<? extends java.lang.Object>);
- method public static java.util.regex.MatchResult assertMatchesRegex(java.lang.String, java.lang.String, java.lang.String);
- method public static java.util.regex.MatchResult assertMatchesRegex(java.lang.String, java.lang.String);
- method public static void assertNotContainsRegex(java.lang.String, java.lang.String, java.lang.String);
- method public static void assertNotContainsRegex(java.lang.String, java.lang.String);
- method public static void assertNotEmpty(java.lang.String, java.lang.Iterable<?>);
- method public static void assertNotEmpty(java.lang.Iterable<?>);
- method public static void assertNotEmpty(java.lang.String, java.util.Map<?, ?>);
- method public static void assertNotEmpty(java.util.Map<?, ?>);
- method public static void assertNotEqual(java.lang.String, java.lang.Object, java.lang.Object);
- method public static void assertNotEqual(java.lang.Object, java.lang.Object);
- method public static void assertNotMatchesRegex(java.lang.String, java.lang.String, java.lang.String);
- method public static void assertNotMatchesRegex(java.lang.String, java.lang.String);
- method public static void checkEqualsAndHashCodeMethods(java.lang.String, java.lang.Object, java.lang.Object, boolean);
- method public static void checkEqualsAndHashCodeMethods(java.lang.Object, java.lang.Object, boolean);
- }
-
- public abstract deprecated interface PerformanceTestCase {
- method public abstract boolean isPerformanceOnly();
- method public abstract int startPerformance(android.test.PerformanceTestCase.Intermediates);
- }
-
- public static abstract interface PerformanceTestCase.Intermediates {
- method public abstract void addIntermediate(java.lang.String);
- method public abstract void addIntermediate(java.lang.String, long);
- method public abstract void finishTiming(boolean);
- method public abstract void setInternalIterations(int);
- method public abstract void startTiming(boolean);
- }
-
- public abstract deprecated class ProviderTestCase<T extends android.content.ContentProvider> extends android.test.InstrumentationTestCase {
- ctor public ProviderTestCase(java.lang.Class<T>, java.lang.String);
- method public android.test.mock.MockContentResolver getMockContentResolver();
- method public android.test.IsolatedContext getMockContext();
- method public T getProvider();
- method public static <T extends android.content.ContentProvider> android.content.ContentResolver newResolverWithContentProviderFromSql(android.content.Context, java.lang.Class<T>, java.lang.String, java.lang.String, int, java.lang.String) throws java.lang.IllegalAccessException, java.lang.InstantiationException;
- }
-
- public abstract class ProviderTestCase2<T extends android.content.ContentProvider> extends android.test.AndroidTestCase {
- ctor public ProviderTestCase2(java.lang.Class<T>, java.lang.String);
- method public android.test.mock.MockContentResolver getMockContentResolver();
- method public android.test.IsolatedContext getMockContext();
- method public T getProvider();
- method public static <T extends android.content.ContentProvider> android.content.ContentResolver newResolverWithContentProviderFromSql(android.content.Context, java.lang.String, java.lang.Class<T>, java.lang.String, java.lang.String, int, java.lang.String) throws java.lang.IllegalAccessException, java.lang.InstantiationException;
- }
-
- public deprecated class RenamingDelegatingContext extends android.content.ContextWrapper {
- ctor public RenamingDelegatingContext(android.content.Context, java.lang.String);
- ctor public RenamingDelegatingContext(android.content.Context, android.content.Context, java.lang.String);
- method public java.lang.String getDatabasePrefix();
- method public void makeExistingFilesAndDbsAccessible();
- method public static <T extends android.content.ContentProvider> T providerWithRenamedContext(java.lang.Class<T>, android.content.Context, java.lang.String) throws java.lang.IllegalAccessException, java.lang.InstantiationException;
- method public static <T extends android.content.ContentProvider> T providerWithRenamedContext(java.lang.Class<T>, android.content.Context, java.lang.String, boolean) throws java.lang.IllegalAccessException, java.lang.InstantiationException;
- }
-
- public abstract deprecated class ServiceTestCase<T extends android.app.Service> extends android.test.AndroidTestCase {
- ctor public ServiceTestCase(java.lang.Class<T>);
- method protected android.os.IBinder bindService(android.content.Intent);
- method public android.app.Application getApplication();
- method public T getService();
- method public android.content.Context getSystemContext();
- method public void setApplication(android.app.Application);
- method protected void setupService();
- method protected void shutdownService();
- method protected void startService(android.content.Intent);
- method public void testServiceTestCaseSetUpProperly() throws java.lang.Exception;
- }
-
- public abstract deprecated class SingleLaunchActivityTestCase<T extends android.app.Activity> extends android.test.InstrumentationTestCase {
- ctor public SingleLaunchActivityTestCase(java.lang.String, java.lang.Class<T>);
- method public T getActivity();
- method public void testActivityTestCaseSetUpProperly() throws java.lang.Exception;
- }
-
- public deprecated class SyncBaseInstrumentation extends android.test.InstrumentationTestCase {
- ctor public SyncBaseInstrumentation();
- method protected void cancelSyncsandDisableAutoSync();
- method protected void syncProvider(android.net.Uri, java.lang.String, java.lang.String) throws java.lang.Exception;
- }
-
- public abstract deprecated interface TestSuiteProvider {
- method public abstract junit.framework.TestSuite getTestSuite();
- }
-
- public deprecated class TouchUtils {
- ctor public TouchUtils();
- method public static void clickView(android.test.InstrumentationTestCase, android.view.View);
- method public static deprecated void drag(android.test.ActivityInstrumentationTestCase, float, float, float, float, int);
- method public static void drag(android.test.InstrumentationTestCase, float, float, float, float, int);
- method public static deprecated void dragQuarterScreenDown(android.test.ActivityInstrumentationTestCase);
- method public static void dragQuarterScreenDown(android.test.InstrumentationTestCase, android.app.Activity);
- method public static deprecated void dragQuarterScreenUp(android.test.ActivityInstrumentationTestCase);
- method public static void dragQuarterScreenUp(android.test.InstrumentationTestCase, android.app.Activity);
- method public static deprecated int dragViewBy(android.test.ActivityInstrumentationTestCase, android.view.View, int, int, int);
- method public static deprecated int dragViewBy(android.test.InstrumentationTestCase, android.view.View, int, int, int);
- method public static deprecated int dragViewTo(android.test.ActivityInstrumentationTestCase, android.view.View, int, int, int);
- method public static int dragViewTo(android.test.InstrumentationTestCase, android.view.View, int, int, int);
- method public static deprecated void dragViewToBottom(android.test.ActivityInstrumentationTestCase, android.view.View);
- method public static void dragViewToBottom(android.test.InstrumentationTestCase, android.app.Activity, android.view.View);
- method public static deprecated void dragViewToBottom(android.test.ActivityInstrumentationTestCase, android.view.View, int);
- method public static void dragViewToBottom(android.test.InstrumentationTestCase, android.app.Activity, android.view.View, int);
- method public static deprecated void dragViewToTop(android.test.ActivityInstrumentationTestCase, android.view.View);
- method public static deprecated void dragViewToTop(android.test.ActivityInstrumentationTestCase, android.view.View, int);
- method public static void dragViewToTop(android.test.InstrumentationTestCase, android.view.View);
- method public static void dragViewToTop(android.test.InstrumentationTestCase, android.view.View, int);
- method public static deprecated int dragViewToX(android.test.ActivityInstrumentationTestCase, android.view.View, int, int);
- method public static int dragViewToX(android.test.InstrumentationTestCase, android.view.View, int, int);
- method public static deprecated int dragViewToY(android.test.ActivityInstrumentationTestCase, android.view.View, int, int);
- method public static int dragViewToY(android.test.InstrumentationTestCase, android.view.View, int, int);
- method public static deprecated void longClickView(android.test.ActivityInstrumentationTestCase, android.view.View);
- method public static void longClickView(android.test.InstrumentationTestCase, android.view.View);
- method public static deprecated void scrollToBottom(android.test.ActivityInstrumentationTestCase, android.view.ViewGroup);
- method public static void scrollToBottom(android.test.InstrumentationTestCase, android.app.Activity, android.view.ViewGroup);
- method public static deprecated void scrollToTop(android.test.ActivityInstrumentationTestCase, android.view.ViewGroup);
- method public static void scrollToTop(android.test.InstrumentationTestCase, android.app.Activity, android.view.ViewGroup);
- method public static void tapView(android.test.InstrumentationTestCase, android.view.View);
- method public static void touchAndCancelView(android.test.InstrumentationTestCase, android.view.View);
- }
-
- public abstract deprecated class UiThreadTest implements java.lang.annotation.Annotation {
- }
-
- public deprecated class ViewAsserts {
- method public static void assertBaselineAligned(android.view.View, android.view.View);
- method public static void assertBottomAligned(android.view.View, android.view.View);
- method public static void assertBottomAligned(android.view.View, android.view.View, int);
- method public static void assertGroupContains(android.view.ViewGroup, android.view.View);
- method public static void assertGroupIntegrity(android.view.ViewGroup);
- method public static void assertGroupNotContains(android.view.ViewGroup, android.view.View);
- method public static void assertHasScreenCoordinates(android.view.View, android.view.View, int, int);
- method public static void assertHorizontalCenterAligned(android.view.View, android.view.View);
- method public static void assertLeftAligned(android.view.View, android.view.View);
- method public static void assertLeftAligned(android.view.View, android.view.View, int);
- method public static void assertOffScreenAbove(android.view.View, android.view.View);
- method public static void assertOffScreenBelow(android.view.View, android.view.View);
- method public static void assertOnScreen(android.view.View, android.view.View);
- method public static void assertRightAligned(android.view.View, android.view.View);
- method public static void assertRightAligned(android.view.View, android.view.View, int);
- method public static void assertTopAligned(android.view.View, android.view.View);
- method public static void assertTopAligned(android.view.View, android.view.View, int);
- method public static void assertVerticalCenterAligned(android.view.View, android.view.View);
- }
-
-}
-
package android.test.mock {
public deprecated class MockApplication extends android.app.Application {
@@ -41863,56 +41812,6 @@
}
-package android.test.suitebuilder {
-
- public deprecated class TestMethod {
- ctor public TestMethod(java.lang.reflect.Method, java.lang.Class<? extends junit.framework.TestCase>);
- ctor public TestMethod(java.lang.String, java.lang.Class<? extends junit.framework.TestCase>);
- ctor public TestMethod(junit.framework.TestCase);
- method public junit.framework.TestCase createTest() throws java.lang.IllegalAccessException, java.lang.InstantiationException, java.lang.reflect.InvocationTargetException;
- method public <T extends java.lang.annotation.Annotation> T getAnnotation(java.lang.Class<T>);
- method public java.lang.Class<? extends junit.framework.TestCase> getEnclosingClass();
- method public java.lang.String getEnclosingClassname();
- method public java.lang.String getName();
- }
-
- public deprecated class TestSuiteBuilder {
- ctor public TestSuiteBuilder(java.lang.Class);
- ctor public TestSuiteBuilder(java.lang.String, java.lang.ClassLoader);
- method public final junit.framework.TestSuite build();
- method public android.test.suitebuilder.TestSuiteBuilder excludePackages(java.lang.String...);
- method protected java.lang.String getSuiteName();
- method public final android.test.suitebuilder.TestSuiteBuilder includeAllPackagesUnderHere();
- method public android.test.suitebuilder.TestSuiteBuilder includePackages(java.lang.String...);
- method public android.test.suitebuilder.TestSuiteBuilder named(java.lang.String);
- }
-
- public static deprecated class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase {
- ctor public TestSuiteBuilder.FailedToCreateTests(java.lang.Exception);
- method public void testSuiteConstructionFailed();
- }
-
-}
-
-package android.test.suitebuilder.annotation {
-
- public abstract deprecated class LargeTest implements java.lang.annotation.Annotation {
- }
-
- public abstract deprecated class MediumTest implements java.lang.annotation.Annotation {
- }
-
- public abstract deprecated class SmallTest implements java.lang.annotation.Annotation {
- }
-
- public abstract deprecated class Smoke implements java.lang.annotation.Annotation {
- }
-
- public abstract deprecated class Suppress implements java.lang.annotation.Annotation {
- }
-
-}
-
package android.text {
public class AlteredCharSequence implements java.lang.CharSequence android.text.GetChars {
@@ -42016,9 +41915,9 @@
}
public class DynamicLayout extends android.text.Layout {
- ctor public DynamicLayout(java.lang.CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, boolean);
- ctor public DynamicLayout(java.lang.CharSequence, java.lang.CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, boolean);
- ctor public DynamicLayout(java.lang.CharSequence, java.lang.CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, boolean, android.text.TextUtils.TruncateAt, int);
+ ctor public deprecated DynamicLayout(java.lang.CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, boolean);
+ ctor public deprecated DynamicLayout(java.lang.CharSequence, java.lang.CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, boolean);
+ ctor public deprecated DynamicLayout(java.lang.CharSequence, java.lang.CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, boolean, android.text.TextUtils.TruncateAt, int);
method public int getBottomPadding();
method public int getEllipsisCount(int);
method public int getEllipsisStart(int);
@@ -42271,6 +42170,27 @@
method public abstract int getSpanTypeId();
}
+ public class PremeasuredText implements android.text.Spanned {
+ method public static android.text.PremeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic);
+ method public static android.text.PremeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic, int, int);
+ method public char charAt(int);
+ method public int getEnd();
+ method public android.text.TextPaint getPaint();
+ method public int getParagraphCount();
+ method public int getParagraphEnd(int);
+ method public int getParagraphStart(int);
+ method public int getSpanEnd(java.lang.Object);
+ method public int getSpanFlags(java.lang.Object);
+ method public int getSpanStart(java.lang.Object);
+ method public <T> T[] getSpans(int, int, java.lang.Class<T>);
+ method public int getStart();
+ method public java.lang.CharSequence getText();
+ method public android.text.TextDirectionHeuristic getTextDir();
+ method public int length();
+ method public int nextSpanTransition(int, int, java.lang.Class);
+ method public java.lang.CharSequence subSequence(int, int);
+ }
+
public class Selection {
method public static boolean extendDown(android.text.Spannable, android.text.Layout);
method public static boolean extendLeft(android.text.Spannable, android.text.Layout);
@@ -42384,9 +42304,9 @@
}
public class StaticLayout extends android.text.Layout {
- ctor public StaticLayout(java.lang.CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, boolean);
- ctor public StaticLayout(java.lang.CharSequence, int, int, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, boolean);
- ctor public StaticLayout(java.lang.CharSequence, int, int, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, boolean, android.text.TextUtils.TruncateAt, int);
+ ctor public deprecated StaticLayout(java.lang.CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, boolean);
+ ctor public deprecated StaticLayout(java.lang.CharSequence, int, int, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, boolean);
+ ctor public deprecated StaticLayout(java.lang.CharSequence, int, int, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, boolean, android.text.TextUtils.TruncateAt, int);
method public int getBottomPadding();
method public int getEllipsisCount(int);
method public int getEllipsisStart(int);
@@ -44517,6 +44437,12 @@
field public static final int[] WILD_CARD;
}
+ public final class StatsLog {
+ method public static boolean logEvent(int);
+ method public static boolean logStart(int);
+ method public static boolean logStop(int);
+ }
+
public class StringBuilderPrinter implements android.util.Printer {
ctor public StringBuilderPrinter(java.lang.StringBuilder);
method public void println(java.lang.String);
@@ -44797,6 +44723,14 @@
field public static final android.os.Parcelable.Creator<android.view.Display.Mode> CREATOR;
}
+ public final class DisplayCutout {
+ method public android.graphics.Region getBounds();
+ method public int getSafeInsetBottom();
+ method public int getSafeInsetLeft();
+ method public int getSafeInsetRight();
+ method public int getSafeInsetTop();
+ }
+
public final class DragAndDropPermissions implements android.os.Parcelable {
method public int describeContents();
method public void release();
@@ -47010,6 +46944,7 @@
method public int getScaledEdgeSlop();
method public int getScaledFadingEdgeLength();
method public float getScaledHorizontalScrollFactor();
+ method public int getScaledHoverSlop();
method public int getScaledMaximumDrawingCacheSize();
method public int getScaledMaximumFlingVelocity();
method public int getScaledMinimumFlingVelocity();
@@ -47749,8 +47684,10 @@
public final class WindowInsets {
ctor public WindowInsets(android.view.WindowInsets);
+ method public android.view.WindowInsets consumeDisplayCutout();
method public android.view.WindowInsets consumeStableInsets();
method public android.view.WindowInsets consumeSystemWindowInsets();
+ method public android.view.DisplayCutout getDisplayCutout();
method public int getStableInsetBottom();
method public int getStableInsetLeft();
method public int getStableInsetRight();
@@ -47810,6 +47747,7 @@
field public static final int FIRST_APPLICATION_WINDOW = 1; // 0x1
field public static final int FIRST_SUB_WINDOW = 1000; // 0x3e8
field public static final int FIRST_SYSTEM_WINDOW = 2000; // 0x7d0
+ field public static final long FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA = 1L; // 0x1L
field public static final int FLAGS_CHANGED = 4; // 0x4
field public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 1; // 0x1
field public static final int FLAG_ALT_FOCUSABLE_IM = 131072; // 0x20000
@@ -47904,6 +47842,7 @@
field public float buttonBrightness;
field public float dimAmount;
field public int flags;
+ field public long flags2;
field public int format;
field public int gravity;
field public float horizontalMargin;
@@ -48696,9 +48635,11 @@
method public void cancel();
method public void commit();
method public void disableAutofillServices();
+ method public android.service.autofill.UserData getUserData();
method public boolean hasEnabledAutofillServices();
method public boolean isAutofillSupported();
method public boolean isEnabled();
+ method public boolean isFieldClassificationEnabled();
method public void notifyValueChanged(android.view.View);
method public void notifyValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
method public void notifyViewEntered(android.view.View);
@@ -48710,6 +48651,7 @@
method public void registerCallback(android.view.autofill.AutofillManager.AutofillCallback);
method public void requestAutofill(android.view.View);
method public void requestAutofill(android.view.View, int, android.graphics.Rect);
+ method public void setUserData(android.service.autofill.UserData);
method public void unregisterCallback(android.view.autofill.AutofillManager.AutofillCallback);
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
@@ -49197,9 +49139,13 @@
method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.os.LocaleList);
method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence, android.view.textclassifier.TextLinks.Options);
method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence);
+ method public default java.util.Collection<java.lang.String> getEntitiesForPreset(int);
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.view.textclassifier.TextSelection.Options);
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int);
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
+ field public static final int ENTITY_PRESET_ALL = 0; // 0x0
+ field public static final int ENTITY_PRESET_BASE = 2; // 0x2
+ field public static final int ENTITY_PRESET_NONE = 1; // 0x1
field public static final android.view.textclassifier.TextClassifier NO_OP;
field public static final java.lang.String TYPE_ADDRESS = "address";
field public static final java.lang.String TYPE_EMAIL = "email";
@@ -49209,6 +49155,13 @@
field public static final java.lang.String TYPE_URL = "url";
}
+ public static final class TextClassifier.EntityConfig {
+ ctor public TextClassifier.EntityConfig(int);
+ method public android.view.textclassifier.TextClassifier.EntityConfig excludeEntities(java.lang.String...);
+ method public java.util.List<java.lang.String> getEntities(android.view.textclassifier.TextClassifier);
+ method public android.view.textclassifier.TextClassifier.EntityConfig includeEntities(java.lang.String...);
+ }
+
public final class TextLinks {
method public boolean apply(android.text.SpannableString, java.util.function.Function<android.view.textclassifier.TextLinks.TextLink, android.text.style.ClickableSpan>);
method public java.util.Collection<android.view.textclassifier.TextLinks.TextLink> getLinks();
@@ -49223,7 +49176,9 @@
public static final class TextLinks.Options {
ctor public TextLinks.Options();
method public android.os.LocaleList getDefaultLocales();
+ method public android.view.textclassifier.TextClassifier.EntityConfig getEntityConfig();
method public android.view.textclassifier.TextLinks.Options setDefaultLocales(android.os.LocaleList);
+ method public android.view.textclassifier.TextLinks.Options setEntityConfig(android.view.textclassifier.TextClassifier.EntityConfig);
}
public static final class TextLinks.TextLink {
@@ -49529,6 +49484,44 @@
method public void proceed();
}
+ public class TracingConfig {
+ ctor public TracingConfig(int);
+ ctor public TracingConfig(int, java.lang.String, int);
+ method public java.lang.String getCustomCategoryPattern();
+ method public int getPresetCategories();
+ method public int getTracingMode();
+ field public static final int CATEGORIES_FRAME_VIEWER = 4; // 0x4
+ field public static final int CATEGORIES_INPUT_LATENCY = 1; // 0x1
+ field public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 3; // 0x3
+ field public static final int CATEGORIES_NONE = -1; // 0xffffffff
+ field public static final int CATEGORIES_RENDERING = 2; // 0x2
+ field public static final int CATEGORIES_WEB_DEVELOPER = 0; // 0x0
+ field public static final int RECORD_CONTINUOUSLY = 1; // 0x1
+ field public static final int RECORD_TO_CONSOLE = 3; // 0x3
+ field public static final int RECORD_UNTIL_FULL = 0; // 0x0
+ field public static final int RECORD_UNTIL_FULL_LARGE_BUFFER = 2; // 0x2
+ }
+
+ public abstract class TracingController {
+ ctor public TracingController();
+ method public static android.webkit.TracingController getInstance();
+ method public abstract boolean isTracing();
+ method public abstract boolean start(android.webkit.TracingConfig);
+ method public abstract boolean stop();
+ method public abstract boolean stopAndFlush(android.webkit.TracingController.TracingOutputStream, android.os.Handler);
+ }
+
+ public static abstract interface TracingController.TracingOutputStream {
+ method public abstract void complete();
+ method public abstract void write(byte[]);
+ }
+
+ public class TracingFileOutputStream implements android.webkit.TracingController.TracingOutputStream {
+ ctor public TracingFileOutputStream(java.lang.String) throws java.io.FileNotFoundException;
+ method public void complete();
+ method public void write(byte[]);
+ }
+
public final class URLUtil {
ctor public URLUtil();
method public static java.lang.String composeSearchUrl(java.lang.String, java.lang.String, java.lang.String);
@@ -49886,6 +49879,7 @@
method public android.print.PrintDocumentAdapter createPrintDocumentAdapter(java.lang.String);
method public android.webkit.WebMessagePort[] createWebMessageChannel();
method public void destroy();
+ method public static void disableWebView();
method public void documentHasImages(android.os.Message);
method public static void enableSlowWholeDocumentDraw();
method public void evaluateJavascript(java.lang.String, android.webkit.ValueCallback<java.lang.String>);
@@ -49946,6 +49940,7 @@
method public void saveWebArchive(java.lang.String);
method public void saveWebArchive(java.lang.String, boolean, android.webkit.ValueCallback<java.lang.String>);
method public deprecated void setCertificate(android.net.http.SslCertificate);
+ method public static void setDataDirectorySuffix(java.lang.String);
method public void setDownloadListener(android.webkit.DownloadListener);
method public void setFindListener(android.webkit.WebView.FindListener);
method public deprecated void setHorizontalScrollbarOverlay(boolean);
diff --git a/api/removed.txt b/api/removed.txt
index be4d5be..1a4e796 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -506,14 +506,6 @@
}
-package android.view.accessibility {
-
- public final class AccessibilityWindowInfo implements android.os.Parcelable {
- method public boolean inPictureInPicture();
- }
-
-}
-
package android.webkit {
public class WebViewClient {
diff --git a/api/system-current.txt b/api/system-current.txt
index 33fa246..30530a3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -613,7 +613,9 @@
public final class UsageStatsManager {
method public int getAppStandbyBucket(java.lang.String);
+ method public java.util.Map<java.lang.String, java.lang.Integer> getAppStandbyBuckets();
method public void setAppStandbyBucket(java.lang.String, int);
+ method public void setAppStandbyBuckets(java.util.Map<java.lang.String, java.lang.Integer>);
method public void whitelistAppTemporarily(java.lang.String, long, android.os.UserHandle);
field public static final int STANDBY_BUCKET_EXEMPTED = 5; // 0x5
field public static final int STANDBY_BUCKET_NEVER = 50; // 0x32
@@ -1337,7 +1339,7 @@
package android.hardware.location {
- public class ContextHubInfo {
+ public class ContextHubInfo implements android.os.Parcelable {
ctor public ContextHubInfo();
method public int describeContents();
method public int getId();
@@ -2104,11 +2106,13 @@
method public int getQuality();
method public float getSmallestDisplacement();
method public android.os.WorkSource getWorkSource();
+ method public boolean isLowPowerMode();
method public android.location.LocationRequest setExpireAt(long);
method public android.location.LocationRequest setExpireIn(long);
method public android.location.LocationRequest setFastestInterval(long);
method public void setHideFromAppOps(boolean);
method public android.location.LocationRequest setInterval(long);
+ method public android.location.LocationRequest setLowPowerMode(boolean);
method public android.location.LocationRequest setNumUpdates(int);
method public android.location.LocationRequest setProvider(java.lang.String);
method public android.location.LocationRequest setQuality(int);
@@ -3527,6 +3531,11 @@
public static final class Settings.Secure 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_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
+ field public static final java.lang.String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE = "autofill_user_data_max_field_classification_size";
+ field public static final java.lang.String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size";
+ field public static final java.lang.String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
+ field public static final java.lang.String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
field public static final java.lang.String INSTANT_APPS_ENABLED = "instant_apps_enabled";
}
@@ -4136,6 +4145,7 @@
method public int getCurrentPhoneType(int);
method public deprecated boolean getDataEnabled();
method public deprecated boolean getDataEnabled(int);
+ method public boolean getEmergencyCallbackMode();
method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
method public android.os.Bundle getVisualVoicemailSettings();
method public boolean handlePinMmi(java.lang.String);
@@ -4149,7 +4159,7 @@
method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
method public boolean needsOtaServiceProvisioning();
method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
- method public void setDataEnabled(int, boolean);
+ method public deprecated void setDataEnabled(int, boolean);
method public boolean setRadio(boolean);
method public boolean setRadioPower(boolean);
method public deprecated void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean);
@@ -4595,6 +4605,7 @@
method public boolean canInvokeDrawGlFunctor(android.view.View);
method public void detachDrawGlFunctor(android.view.View, long);
method public android.app.Application getApplication();
+ method public java.lang.String getDataDirectorySuffix();
method public java.lang.String getErrorString(android.content.Context, int);
method public int getPackageId(android.content.res.Resources, java.lang.String);
method public void invokeDrawGlFunctor(android.view.View, long, boolean);
@@ -4632,6 +4643,7 @@
method public abstract android.webkit.ServiceWorkerController getServiceWorkerController();
method public abstract android.webkit.WebViewFactoryProvider.Statics getStatics();
method public abstract android.webkit.TokenBindingService getTokenBindingService();
+ method public abstract android.webkit.TracingController getTracingController();
method public abstract android.webkit.WebIconDatabase getWebIconDatabase();
method public abstract android.webkit.WebStorage getWebStorage();
method public abstract android.webkit.WebViewDatabase getWebViewDatabase(android.content.Context);
diff --git a/api/test-current.txt b/api/test-current.txt
index 27e585d..e64c320 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -120,6 +120,7 @@
public class StorageStatsManager {
method public boolean isQuotaSupported(java.util.UUID);
+ method public boolean isReservedSupported(java.util.UUID);
}
}
@@ -325,6 +326,13 @@
field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
}
+ public class TrafficStats {
+ method public static long getLoopbackRxBytes();
+ method public static long getLoopbackRxPackets();
+ method public static long getLoopbackTxBytes();
+ method public static long getLoopbackTxPackets();
+ }
+
}
package android.os {
@@ -462,34 +470,11 @@
}
public final class EditDistanceScorer extends android.service.autofill.InternalScorer implements android.os.Parcelable android.service.autofill.Scorer {
- method public int describeContents();
- method public static android.service.autofill.EditDistanceScorer getInstance();
method public float getScore(android.view.autofill.AutofillValue, java.lang.String);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.autofill.EditDistanceScorer> CREATOR;
}
- public final class FieldClassification implements android.os.Parcelable {
- method public int describeContents();
- method public java.util.List<android.service.autofill.FieldClassification.Match> getMatches();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.autofill.FieldClassification> CREATOR;
- }
-
- public static final class FieldClassification.Match implements android.os.Parcelable {
- method public int describeContents();
- method public java.lang.String getRemoteId();
- method public float getScore();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.autofill.FieldClassification.Match> CREATOR;
- }
-
- public static final class FillEventHistory.Event {
- method public java.util.Map<android.view.autofill.AutofillId, android.service.autofill.FieldClassification> getFieldsClassification();
- }
-
- public static final class FillResponse.Builder {
- method public android.service.autofill.FillResponse.Builder setFieldClassificationIds(android.view.autofill.AutofillId...);
+ public final class FillResponse implements android.os.Parcelable {
+ method public int getFlags();
}
public final class ImageTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -522,29 +507,10 @@
method public boolean isValid(android.service.autofill.ValueFinder);
}
- public abstract interface Scorer {
- }
-
public final class TextValueSanitizer extends android.service.autofill.InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
}
- public final class UserData implements android.os.Parcelable {
- method public int describeContents();
- method public static int getMaxFieldClassificationIdsSize();
- method public static int getMaxUserDataSize();
- method public static int getMaxValueLength();
- method public static int getMinValueLength();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.autofill.UserData> CREATOR;
- }
-
- public static final class UserData.Builder {
- ctor public UserData.Builder(android.service.autofill.Scorer, java.lang.String, java.lang.String);
- method public android.service.autofill.UserData.Builder add(java.lang.String, java.lang.String);
- method public android.service.autofill.UserData build();
- }
-
public abstract interface ValueFinder {
method public abstract java.lang.String findByAutofillId(android.view.autofill.AutofillId);
}
@@ -992,12 +958,6 @@
ctor public AutofillId(int);
}
- public final class AutofillManager {
- method public android.service.autofill.UserData getUserData();
- method public boolean isFieldClassificationEnabled();
- method public void setUserData(android.service.autofill.UserData);
- }
-
}
package android.widget {
diff --git a/cmds/bootanimation/iot/BootParameters.h b/cmds/bootanimation/iot/BootParameters.h
index ff3b018..c10bd44 100644
--- a/cmds/bootanimation/iot/BootParameters.h
+++ b/cmds/bootanimation/iot/BootParameters.h
@@ -48,8 +48,8 @@
struct SavedBootParameters {
int brightness;
int volume;
- ScopedVector<std::string> param_names;
- ScopedVector<std::string> param_values;
+ std::vector<std::unique_ptr<std::string>> param_names;
+ std::vector<std::unique_ptr<std::string>> param_values;
SavedBootParameters();
static void RegisterJSONConverter(
diff --git a/cmds/ime/Android.mk b/cmds/ime/Android.mk
index 6803fc0..ca608e8 100644
--- a/cmds/ime/Android.mk
+++ b/cmds/ime/Android.mk
@@ -3,14 +3,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_MODULE := imelib
-LOCAL_MODULE_STEM := ime
-include $(BUILD_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
LOCAL_MODULE := ime
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_SRC_FILES := ime
-LOCAL_REQUIRED_MODULES := imelib
include $(BUILD_PREBUILT)
diff --git a/cmds/ime/ime b/cmds/ime/ime
index 1a1fdd9..7d2f72f 100755
--- a/cmds/ime/ime
+++ b/cmds/ime/ime
@@ -1,8 +1,2 @@
#!/system/bin/sh
-# Script to start "pm" on the device, which has a very rudimentary
-# shell.
-#
-base=/system
-export CLASSPATH=$base/framework/ime.jar
-exec app_process $base/bin com.android.commands.ime.Ime "$@"
-
+exec cmd input_method ime "$@"
diff --git a/cmds/ime/src/com/android/commands/ime/Ime.java b/cmds/ime/src/com/android/commands/ime/Ime.java
deleted file mode 100644
index 72a0af6..0000000
--- a/cmds/ime/src/com/android/commands/ime/Ime.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2007 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.commands.ime;
-
-import com.android.internal.view.IInputMethodManager;
-
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.util.PrintStreamPrinter;
-import android.util.Printer;
-import android.view.inputmethod.InputMethodInfo;
-
-import java.util.List;
-
-public final class Ime {
- IInputMethodManager mImm;
-
- private String[] mArgs;
- private int mNextArg;
- private String mCurArgData;
-
- private static final String IMM_NOT_RUNNING_ERR =
- "Error: Could not access the Input Method Manager. Is the system running?";
-
- public static void main(String[] args) {
- new Ime().run(args);
- }
-
- public void run(String[] args) {
- if (args.length < 1) {
- showUsage();
- return;
- }
-
- mImm = IInputMethodManager.Stub.asInterface(ServiceManager.getService("input_method"));
- if (mImm == null) {
- System.err.println(IMM_NOT_RUNNING_ERR);
- return;
- }
-
- mArgs = args;
- String op = args[0];
- mNextArg = 1;
-
- if ("list".equals(op)) {
- runList();
- return;
- }
-
- if ("enable".equals(op)) {
- runSetEnabled(true);
- return;
- }
-
- if ("disable".equals(op)) {
- runSetEnabled(false);
- return;
- }
-
- if ("set".equals(op)) {
- runSet();
- return;
- }
-
- if (op != null) {
- System.err.println("Error: unknown command '" + op + "'");
- }
- showUsage();
- }
-
- /**
- * Execute the list sub-command.
- */
- private void runList() {
- String opt;
- boolean all = false;
- boolean brief = false;
- while ((opt=nextOption()) != null) {
- if (opt.equals("-a")) {
- all = true;
- } else if (opt.equals("-s")) {
- brief = true;
- } else {
- System.err.println("Error: Unknown option: " + opt);
- showUsage();
- return;
- }
- }
-
-
- List<InputMethodInfo> methods;
- if (!all) {
- try {
- methods = mImm.getEnabledInputMethodList();
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(IMM_NOT_RUNNING_ERR);
- return;
- }
- } else {
- try {
- methods = mImm.getInputMethodList();
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(IMM_NOT_RUNNING_ERR);
- return;
- }
- }
-
- if (methods != null) {
- Printer pr = new PrintStreamPrinter(System.out);
- for (int i=0; i<methods.size(); i++) {
- InputMethodInfo imi = methods.get(i);
- if (brief) {
- System.out.println(imi.getId());
- } else {
- System.out.println(imi.getId() + ":");
- imi.dump(pr, " ");
- }
- }
- }
- }
-
- private void runSetEnabled(boolean state) {
- String id = nextArg();
- if (id == null) {
- System.err.println("Error: no input method ID specified");
- showUsage();
- return;
- }
-
- try {
- boolean res = mImm.setInputMethodEnabled(id, state);
- if (state) {
- System.out.println("Input method " + id + ": "
- + (res ? "already enabled" : "now enabled"));
- } else {
- System.out.println("Input method " + id + ": "
- + (res ? "now disabled" : "already disabled"));
- }
- } catch (IllegalArgumentException e) {
- System.err.println("Error: " + e.getMessage());
- return;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(IMM_NOT_RUNNING_ERR);
- return;
- }
- }
-
- private void runSet() {
- String id = nextArg();
- if (id == null) {
- System.err.println("Error: no input method ID specified");
- showUsage();
- return;
- }
-
- try {
- mImm.setInputMethod(null, id);
- System.out.println("Input method " + id + " selected");
- } catch (IllegalArgumentException e) {
- System.err.println("Error: " + e.getMessage());
- return;
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(IMM_NOT_RUNNING_ERR);
- return;
- }
- }
-
- private String nextOption() {
- if (mNextArg >= mArgs.length) {
- return null;
- }
- String arg = mArgs[mNextArg];
- if (!arg.startsWith("-")) {
- return null;
- }
- mNextArg++;
- if (arg.equals("--")) {
- return null;
- }
- if (arg.length() > 1 && arg.charAt(1) != '-') {
- if (arg.length() > 2) {
- mCurArgData = arg.substring(2);
- return arg.substring(0, 2);
- } else {
- mCurArgData = null;
- return arg;
- }
- }
- mCurArgData = null;
- return arg;
- }
-
- private String nextOptionData() {
- if (mCurArgData != null) {
- return mCurArgData;
- }
- if (mNextArg >= mArgs.length) {
- return null;
- }
- String data = mArgs[mNextArg];
- mNextArg++;
- return data;
- }
-
- private String nextArg() {
- if (mNextArg >= mArgs.length) {
- return null;
- }
- String arg = mArgs[mNextArg];
- mNextArg++;
- return arg;
- }
-
- private static void showUsage() {
- System.err.println("usage: ime list [-a] [-s]");
- System.err.println(" ime enable ID");
- System.err.println(" ime disable ID");
- System.err.println(" ime set ID");
- System.err.println("");
- System.err.println("The list command prints all enabled input methods. Use");
- System.err.println("the -a option to see all input methods. Use");
- System.err.println("the -s option to see only a single summary line of each.");
- System.err.println("");
- System.err.println("The enable command allows the given input method ID to be used.");
- System.err.println("");
- System.err.println("The disable command disallows the given input method ID from use.");
- System.err.println("");
- System.err.println("The set command switches to the given input method ID.");
- }
-}
diff --git a/cmds/incident_helper/Android.bp b/cmds/incident_helper/Android.bp
index 2ef0371..d7b6d69 100644
--- a/cmds/incident_helper/Android.bp
+++ b/cmds/incident_helper/Android.bp
@@ -38,6 +38,7 @@
cc_test {
name: "incident_helper_test",
+ test_suites: ["device-tests"],
defaults: ["incident_helper_defaults"],
local_include_dirs: ["src/"],
@@ -49,12 +50,15 @@
"testdata/*",
],
- shared_libs: [
- "libprotobuf-cpp-full",
- ],
-
static_libs: [
"libgmock",
"libplatformprotos"
],
+
+ shared_libs: [
+ "libprotobuf-cpp-full"
+ ],
+ proto: {
+ type: "full",
+ },
}
diff --git a/cmds/incident_helper/AndroidTest.xml b/cmds/incident_helper/AndroidTest.xml
new file mode 100644
index 0000000..6d242bc
--- /dev/null
+++ b/cmds/incident_helper/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for incident_helper_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="incident_helper_test->/data/nativetest/incident_helper_test" />
+ <option name="push" value="testdata->/data/nativetest/testdata" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/nativetest" />
+ <option name="module-name" value="incident_helper_test" />
+ </test>
+</configuration>
diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp
index db4f586..4bf956a 100644
--- a/cmds/incident_helper/src/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -297,3 +297,82 @@
}
return true;
}
+
+// ================================================================================
+Message::Message(Table* table)
+ :mTable(table),
+ mPreviousField(""),
+ mTokens(),
+ mSubMessages()
+{
+}
+
+Message::~Message()
+{
+}
+
+void
+Message::addSubMessage(uint64_t fieldId, Message* fieldMsg)
+{
+ for (auto iter = mTable->mFields.begin(); iter != mTable->mFields.end(); iter++) {
+ if (iter->second == fieldId) {
+ mSubMessages[iter->first] = fieldMsg;
+ return;
+ }
+ }
+}
+
+bool
+Message::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
+{
+ // If the field name can be found, it means the name is a primitive field.
+ if (mTable->mFields.find(name) != mTable->mFields.end()) {
+ endSession(proto);
+ // The only edge case is for example ro.hardware itself is a message, so a field called "value"
+ // would be defined in proto Ro::Hardware and it must be the first field.
+ if (mSubMessages.find(name) != mSubMessages.end()) {
+ startSession(proto, name);
+ return mSubMessages[name]->insertField(proto, "value", value);
+ } else {
+ return mTable->insertField(proto, name, value);
+ }
+ }
+
+ // Try to find the message field which is the prefix of name, so the value would be inserted
+ // recursively into the submessage.
+ string mutableName = name;
+ for (auto iter = mSubMessages.begin(); iter != mSubMessages.end(); iter++) {
+ string fieldName = iter->first;
+ string prefix = fieldName + "_"; // underscore is the delimiter in the name
+ if (stripPrefix(&mutableName, prefix.c_str())) {
+ if (mPreviousField != fieldName) {
+ endSession(proto);
+ startSession(proto, fieldName);
+ }
+ return mSubMessages[fieldName]->insertField(proto, mutableName, value);
+ }
+ }
+ // Can't find the name in proto definition, handle it separately.
+ return false;
+}
+
+void
+Message::startSession(ProtoOutputStream* proto, const string& name)
+{
+ uint64_t fieldId = mTable->mFields[name];
+ long long token = proto->start(fieldId);
+ mPreviousField = name;
+ mTokens.push(token);
+}
+
+void
+Message::endSession(ProtoOutputStream* proto)
+{
+ if (mPreviousField == "") return;
+ if (mSubMessages.find(mPreviousField) != mSubMessages.end()) {
+ mSubMessages[mPreviousField]->endSession(proto);
+ }
+ proto->end(mTokens.top());
+ mTokens.pop();
+ mPreviousField = "";
+}
diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h
index 4a5fe1d..58ef290 100644
--- a/cmds/incident_helper/src/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -18,6 +18,7 @@
#define INCIDENT_HELPER_UTIL_H
#include <map>
+#include <stack>
#include <string>
#include <vector>
@@ -34,7 +35,7 @@
const std::string TAB_DELIMITER = "\t";
const std::string COMMA_DELIMITER = ",";
-// returns true if c is a-zA-Z0-9 or underscore _
+// returns true if c is a-zA-Z0-9 or underscore
bool isValidChar(char c);
// trim the string with the given charset
@@ -102,11 +103,20 @@
};
/**
- * The class contains a mapping between table headers to its field ids.
- * And allow users to insert the field values to proto based on its header name.
+ * The Table class is constructed from two arrays generated by the given message with
+ * option (stream_proto.stream_msg).enable_fields_mapping = true.
+ * The names are each field's names in the message and must corresponding to the header/name of
+ * the text to be parsed, and the ids are the streaming proto encoded field ids.
+ *
+ * This class then allows users to insert the table values to proto based on its header.
+ *
+ * Advance feature: if some fields in the message are enums, user must explicitly add the
+ * mapping from enum name string to its enum values.
*/
+class Message;
class Table
{
+friend class Message;
public:
Table(const char* names[], const uint64_t ids[], const int count);
~Table();
@@ -114,9 +124,12 @@
// Add enum names to values for parsing purpose.
void addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize);
- // manually add enum names to values mapping, useful when an Enum type is used by a lot of fields, and there are no name conflicts
+ // Manually add enum names to values mapping, useful when an Enum type is used by
+ // a number of fields, there must not be any enum name conflicts.
void addEnumNameToValue(const char* enumName, const int enumValue);
+ // Based on given name, find the right field id, parse the text value and insert to proto.
+ // Return false if the given name can't be found.
bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value);
private:
map<std::string, uint64_t> mFields;
@@ -124,4 +137,47 @@
map<std::string, int> mEnumValuesByName;
};
+/**
+ * Reconstructs a typical proto message given its message Table, adds submessage fields explicitly.
+ * It allows user to insert nested proto values purely by the names. See insertField for detail.
+ */
+class Message
+{
+public:
+ Message(Table* table);
+ ~Message();
+
+ // Reconstructs the typical proto message by adding its message fields.
+ void addSubMessage(uint64_t fieldId, Message* fieldMsg);
+
+ // Inserts value if the given name has the corresponding field in its message and return true.
+ // It will recursively search the name in submessages and find the correct field to insert.
+ // For example, when the name is dalvik_vm_heapsize, and the message's corresponding proto is:
+ // message Properties {
+ // message DalvikVm {
+ // int32 heapsize = 1;
+ // bool usejit = 2;
+ // }
+ // DalvikVm dalvik_vm = 1;
+ // string hack_in = 2;
+ // }
+ // The value will be inserted into field heapsize in dalvik_vm submessage.
+ //
+ // Also value belongs to same submessage MUST be inserted contiguously.
+ // For example, dalvik_vm_usejit must be inserted directly after dalvik_vm_heapsize, otherwise
+ // if hack_in attempts to be inserted before dalvik_vm_usejit, value of usejit isn't added as expected.
+ bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value);
+
+ // Starts a new message field proto session.
+ void startSession(ProtoOutputStream* proto, const string& name);
+
+ // Ends the previous message field proto session.
+ void endSession(ProtoOutputStream* proto);
+private:
+ Table* mTable;
+ std::string mPreviousField;
+ stack<long long> mTokens;
+ map<std::string, Message*> mSubMessages;
+};
+
#endif // INCIDENT_HELPER_UTIL_H
diff --git a/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp b/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp
index ee5feb0..23393da0 100644
--- a/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp
+++ b/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp
@@ -45,11 +45,130 @@
string line;
string name; // the name of the property
string value; // the string value of the property
-
ProtoOutputStream proto;
- Table table(SystemPropertiesProto::_FIELD_NAMES, SystemPropertiesProto::_FIELD_IDS, SystemPropertiesProto::_FIELD_COUNT);
- table.addEnumNameToValue("running", SystemPropertiesProto::STATUS_RUNNING);
- table.addEnumNameToValue("stopped", SystemPropertiesProto::STATUS_STOPPED);
+ vector<pair<string, string>> extras;
+
+ Table sysPropTable(SystemPropertiesProto::_FIELD_NAMES,
+ SystemPropertiesProto::_FIELD_IDS,
+ SystemPropertiesProto::_FIELD_COUNT);
+ Message sysProp(&sysPropTable);
+
+ Table aacDrcTable(SystemPropertiesProto::AacDrc::_FIELD_NAMES,
+ SystemPropertiesProto::AacDrc::_FIELD_IDS,
+ SystemPropertiesProto::AacDrc::_FIELD_COUNT);
+ Message aacDrc(&aacDrcTable);
+ sysProp.addSubMessage(SystemPropertiesProto::AAC_DRC, &aacDrc);
+
+ Table aaudioTable(SystemPropertiesProto::Aaudio::_FIELD_NAMES,
+ SystemPropertiesProto::Aaudio::_FIELD_IDS,
+ SystemPropertiesProto::Aaudio::_FIELD_COUNT);
+ Message aaudio(&aaudioTable);
+ sysProp.addSubMessage(SystemPropertiesProto::AAUDIO, &aaudio);
+
+ Table cameraTable(SystemPropertiesProto::Camera::_FIELD_NAMES,
+ SystemPropertiesProto::Camera::_FIELD_IDS,
+ SystemPropertiesProto::Camera::_FIELD_COUNT);
+ Message camera(&cameraTable);
+ sysProp.addSubMessage(SystemPropertiesProto::CAMERA, &camera);
+
+ Table dalvikVmTable(SystemPropertiesProto::DalvikVm::_FIELD_NAMES,
+ SystemPropertiesProto::DalvikVm::_FIELD_IDS,
+ SystemPropertiesProto::DalvikVm::_FIELD_COUNT);
+ Message dalvikVm(&dalvikVmTable);
+ sysProp.addSubMessage(SystemPropertiesProto::DALVIK_VM, &dalvikVm);
+
+ Table initSvcTable(SystemPropertiesProto::InitSvc::_FIELD_NAMES,
+ SystemPropertiesProto::InitSvc::_FIELD_IDS,
+ SystemPropertiesProto::InitSvc::_FIELD_COUNT);
+ initSvcTable.addEnumNameToValue("running", SystemPropertiesProto::InitSvc::STATUS_RUNNING);
+ initSvcTable.addEnumNameToValue("stopped", SystemPropertiesProto::InitSvc::STATUS_STOPPED);
+ Message initSvc(&initSvcTable);
+ sysProp.addSubMessage(SystemPropertiesProto::INIT_SVC, &initSvc);
+
+ Table logTable(SystemPropertiesProto::Log::_FIELD_NAMES,
+ SystemPropertiesProto::Log::_FIELD_IDS,
+ SystemPropertiesProto::Log::_FIELD_COUNT);
+ Message logMsg(&logTable);
+ sysProp.addSubMessage(SystemPropertiesProto::LOG, &logMsg);
+
+ Table persistTable(SystemPropertiesProto::Persist::_FIELD_NAMES,
+ SystemPropertiesProto::Persist::_FIELD_IDS,
+ SystemPropertiesProto::Persist::_FIELD_COUNT);
+ Message persist(&persistTable);
+ sysProp.addSubMessage(SystemPropertiesProto::PERSIST, &persist);
+
+ Table pmDexoptTable(SystemPropertiesProto::PmDexopt::_FIELD_NAMES,
+ SystemPropertiesProto::PmDexopt::_FIELD_IDS,
+ SystemPropertiesProto::PmDexopt::_FIELD_COUNT);
+ Message pmDexopt(&pmDexoptTable);
+ sysProp.addSubMessage(SystemPropertiesProto::PM_DEXOPT, &pmDexopt);
+
+ Table roTable(SystemPropertiesProto::Ro::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::_FIELD_IDS,
+ SystemPropertiesProto::Ro::_FIELD_COUNT);
+ Message ro(&roTable);
+
+ Table bootTable(SystemPropertiesProto::Ro::Boot::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::Boot::_FIELD_IDS,
+ SystemPropertiesProto::Ro::Boot::_FIELD_COUNT);
+ Message boot(&bootTable);
+ ro.addSubMessage(SystemPropertiesProto::Ro::BOOT, &boot);
+
+ Table bootimageTable(SystemPropertiesProto::Ro::BootImage::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::BootImage::_FIELD_IDS,
+ SystemPropertiesProto::Ro::BootImage::_FIELD_COUNT);
+ Message bootimage(&bootimageTable);
+ ro.addSubMessage(SystemPropertiesProto::Ro::BOOTIMAGE, &bootimage);
+
+ Table buildTable(SystemPropertiesProto::Ro::Build::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::Build::_FIELD_IDS,
+ SystemPropertiesProto::Ro::Build::_FIELD_COUNT);
+ Message build(&buildTable);
+
+ Table versionTable(SystemPropertiesProto::Ro::Build::Version::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::Build::Version::_FIELD_IDS,
+ SystemPropertiesProto::Ro::Build::Version::_FIELD_COUNT);
+ Message version(&versionTable);
+ build.addSubMessage(SystemPropertiesProto::Ro::Build::VERSION, &version);
+ ro.addSubMessage(SystemPropertiesProto::Ro::BUILD, &build);
+
+ Table configTable(SystemPropertiesProto::Ro::Config::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::Config::_FIELD_IDS,
+ SystemPropertiesProto::Ro::Config::_FIELD_COUNT);
+ Message config(&configTable);
+ ro.addSubMessage(SystemPropertiesProto::Ro::CONFIG, &config);
+
+ Table hardwareTable(SystemPropertiesProto::Ro::Hardware::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::Hardware::_FIELD_IDS,
+ SystemPropertiesProto::Ro::Hardware::_FIELD_COUNT);
+ Message hardware(&hardwareTable);
+ ro.addSubMessage(SystemPropertiesProto::Ro::HARDWARE, &hardware);
+
+ Table productTable(SystemPropertiesProto::Ro::Product::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::Product::_FIELD_IDS,
+ SystemPropertiesProto::Ro::Product::_FIELD_COUNT);
+ Message product(&productTable);
+
+ Table vendorTable(SystemPropertiesProto::Ro::Product::Vendor::_FIELD_NAMES,
+ SystemPropertiesProto::Ro::Product::Vendor::_FIELD_IDS,
+ SystemPropertiesProto::Ro::Product::Vendor::_FIELD_COUNT);
+ Message vendor(&vendorTable);
+ product.addSubMessage(SystemPropertiesProto::Ro::Product::VENDOR, &vendor);
+ ro.addSubMessage(SystemPropertiesProto::Ro::PRODUCT, &product);
+ sysProp.addSubMessage(SystemPropertiesProto::RO, &ro);
+
+ Table sysTable(SystemPropertiesProto::Sys::_FIELD_NAMES,
+ SystemPropertiesProto::Sys::_FIELD_IDS,
+ SystemPropertiesProto::Sys::_FIELD_COUNT);
+ Message sys(&sysTable);
+
+ Table usbTable(SystemPropertiesProto::Sys::Usb::_FIELD_NAMES,
+ SystemPropertiesProto::Sys::Usb::_FIELD_IDS,
+ SystemPropertiesProto::Sys::Usb::_FIELD_COUNT);
+ Message usb(&usbTable);
+ sys.addSubMessage(SystemPropertiesProto::Sys::USB, &usb);
+
+ sysProp.addSubMessage(SystemPropertiesProto::SYS, &sys);
// parse line by line
while (reader.readLine(&line)) {
@@ -67,13 +186,19 @@
// if the property name couldn't be found in proto definition or the value has mistype,
// add to extra properties with its name and value
- if (!table.insertField(&proto, convertToFieldName(name), value)) {
- long long token = proto.start(SystemPropertiesProto::EXTRA_PROPERTIES);
- proto.write(SystemPropertiesProto::Property::NAME, name);
- proto.write(SystemPropertiesProto::Property::VALUE, value);
- proto.end(token);
+ if (!sysProp.insertField(&proto, convertToFieldName(name), value)) {
+ extras.push_back(make_pair(name, value));
}
}
+ // end session for the last write.
+ sysProp.endSession(&proto);
+
+ for (auto it = extras.begin(); it != extras.end(); it++) {
+ long long token = proto.start(SystemPropertiesProto::EXTRA_PROPERTIES);
+ proto.write(SystemPropertiesProto::Property::NAME, it->first);
+ proto.write(SystemPropertiesProto::Property::VALUE, it->second);
+ proto.end(token);
+ }
if (!reader.ok(&line)) {
fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
diff --git a/cmds/incident_helper/testdata/system_properties.txt b/cmds/incident_helper/testdata/system_properties.txt
index 57c07ee..bf7d4ad 100644
--- a/cmds/incident_helper/testdata/system_properties.txt
+++ b/cmds/incident_helper/testdata/system_properties.txt
@@ -1,14 +1,16 @@
+[aac_drc_cut]: [123]
[aaudio.hw_burst_min_usec]: [2000]
[aaudio.mmap_exclusive_policy]: [2]
[dalvik.vm.appimageformat]: [lz4]
-[gsm.operator.isroaming]: [false]
-[init.svc.vendor.imsqmidaemon]: [running]
-[init.svc.vendor.init-radio-sh]: [stopped]
-[net.dns1]: [2001:4860:4860::8844]
-[net.tcp.buffersize.wifi]: [524288,2097152,4194304,262144,524288,1048576]
-[nfc.initialized]: [True]
-[persist_radio_VT_ENABLE]: [1]
+[drm_64bit_enabled]: [false]
+[init.svc.adbd]: [running]
+[init.svc.lmkd]: [stopped]
+[media_mediadrmservice_enable]: [True]
[ro.boot.boottime]: [1BLL:85,1BLE:898,2BLL:0,2BLE:862,SW:6739,KL:340]
[ro.bootimage.build.date.utc]: [1509394807]
[ro.bootimage.build.fingerprint]: [google/marlin/marlin:P/MASTER/jinyithu10301320:eng/dev-keys]
-[ro.wifi.channels]: []
\ No newline at end of file
+[ro.hardware]: [marlin]
+[ro.hardware.power]: [marlin-profile]
+[ro.product.cpu.abilist]: [arm64-v8a,armeabi-v7a,armeabi]
+[ro.product.vendor.brand]: [google]
+[ro.wifi.channels]: []
diff --git a/cmds/incident_helper/tests/CpuFreqParser_test.cpp b/cmds/incident_helper/tests/CpuFreqParser_test.cpp
index 1c2f9e5..82deee4 100644
--- a/cmds/incident_helper/tests/CpuFreqParser_test.cpp
+++ b/cmds/incident_helper/tests/CpuFreqParser_test.cpp
@@ -21,7 +21,7 @@
#include <android-base/file.h>
#include <android-base/test_utils.h>
#include <gmock/gmock.h>
-#include <google/protobuf/message.h>
+#include <google/protobuf/message_lite.h>
#include <gtest/gtest.h>
#include <string.h>
#include <fcntl.h>
@@ -42,13 +42,6 @@
ASSERT_TRUE(tf.fd != -1);
}
- string getSerializedString(::google::protobuf::Message& message) {
- string expectedStr;
- message.SerializeToFileDescriptor(tf.fd);
- ReadFileToString(tf.path, &expectedStr);
- return expectedStr;
- }
-
protected:
TemporaryFile tf;
@@ -125,6 +118,6 @@
CaptureStdout();
ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
- EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ EXPECT_EQ(GetCapturedStdout(), expected.SerializeAsString());
close(fd);
}
diff --git a/cmds/incident_helper/tests/CpuInfoParser_test.cpp b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
index bbc14bc..8dce53e 100644
--- a/cmds/incident_helper/tests/CpuInfoParser_test.cpp
+++ b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
@@ -21,7 +21,7 @@
#include <android-base/file.h>
#include <android-base/test_utils.h>
#include <gmock/gmock.h>
-#include <google/protobuf/message.h>
+#include <google/protobuf/message_lite.h>
#include <gtest/gtest.h>
#include <string.h>
#include <fcntl.h>
@@ -42,13 +42,6 @@
ASSERT_TRUE(tf.fd != -1);
}
- string getSerializedString(::google::protobuf::Message& message) {
- string expectedStr;
- message.SerializeToFileDescriptor(tf.fd);
- ReadFileToString(tf.path, &expectedStr);
- return expectedStr;
- }
-
protected:
TemporaryFile tf;
@@ -153,6 +146,6 @@
CaptureStdout();
ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
- EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ EXPECT_EQ(GetCapturedStdout(), expected.SerializeAsString());
close(fd);
}
diff --git a/cmds/incident_helper/tests/KernelWakesParser_test.cpp b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
index a8fa6208..a98c62b 100644
--- a/cmds/incident_helper/tests/KernelWakesParser_test.cpp
+++ b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
@@ -21,7 +21,7 @@
#include <android-base/file.h>
#include <android-base/test_utils.h>
#include <gmock/gmock.h>
-#include <google/protobuf/message.h>
+#include <google/protobuf/message_lite.h>
#include <gtest/gtest.h>
#include <string.h>
#include <fcntl.h>
@@ -42,13 +42,6 @@
ASSERT_TRUE(tf.fd != -1);
}
- string getSerializedString(::google::protobuf::Message& message) {
- string expectedStr;
- message.SerializeToFileDescriptor(tf.fd);
- ReadFileToString(tf.path, &expectedStr);
- return expectedStr;
- }
-
protected:
TemporaryFile tf;
@@ -76,7 +69,7 @@
CaptureStdout();
ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
- EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ EXPECT_EQ(GetCapturedStdout(), expected.SerializeAsString());
close(fd);
}
@@ -114,6 +107,6 @@
CaptureStdout();
ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
- EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ EXPECT_EQ(GetCapturedStdout(), expected.SerializeAsString());
close(fd);
}
diff --git a/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
index de64e70..a9e6e816 100644
--- a/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
+++ b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
@@ -21,7 +21,7 @@
#include <android-base/file.h>
#include <android-base/test_utils.h>
#include <gmock/gmock.h>
-#include <google/protobuf/message.h>
+#include <google/protobuf/message_lite.h>
#include <gtest/gtest.h>
#include <string.h>
#include <fcntl.h>
@@ -42,13 +42,6 @@
ASSERT_TRUE(tf.fd != -1);
}
- string getSerializedString(::google::protobuf::Message& message) {
- string expectedStr;
- message.SerializeToFileDescriptor(tf.fd);
- ReadFileToString(tf.path, &expectedStr);
- return expectedStr;
- }
-
protected:
TemporaryFile tf;
@@ -108,6 +101,6 @@
CaptureStdout();
ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
- EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ EXPECT_EQ(GetCapturedStdout(), expected.SerializeAsString());
close(fd);
-}
\ No newline at end of file
+}
diff --git a/cmds/incident_helper/tests/ProcrankParser_test.cpp b/cmds/incident_helper/tests/ProcrankParser_test.cpp
index e86647a..76b25d7 100644
--- a/cmds/incident_helper/tests/ProcrankParser_test.cpp
+++ b/cmds/incident_helper/tests/ProcrankParser_test.cpp
@@ -21,7 +21,7 @@
#include <android-base/file.h>
#include <android-base/test_utils.h>
#include <gmock/gmock.h>
-#include <google/protobuf/message.h>
+#include <google/protobuf/message_lite.h>
#include <gtest/gtest.h>
#include <string.h>
#include <fcntl.h>
@@ -42,13 +42,6 @@
ASSERT_TRUE(tf.fd != -1);
}
- string getSerializedString(::google::protobuf::Message& message) {
- string expectedStr;
- message.SerializeToFileDescriptor(tf.fd);
- ReadFileToString(tf.path, &expectedStr);
- return expectedStr;
- }
-
protected:
TemporaryFile tf;
@@ -104,7 +97,7 @@
CaptureStdout();
ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
- EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ EXPECT_EQ(GetCapturedStdout(), expected.SerializeAsString());
close(fd);
}
@@ -142,6 +135,6 @@
CaptureStdout();
ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
- EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ EXPECT_EQ(GetCapturedStdout(), expected.SerializeAsString());
close(fd);
}
diff --git a/cmds/incident_helper/tests/SystemPropertiesParser_test.cpp b/cmds/incident_helper/tests/SystemPropertiesParser_test.cpp
index 23e292a..ef28c51 100644
--- a/cmds/incident_helper/tests/SystemPropertiesParser_test.cpp
+++ b/cmds/incident_helper/tests/SystemPropertiesParser_test.cpp
@@ -21,7 +21,7 @@
#include <android-base/file.h>
#include <android-base/test_utils.h>
#include <gmock/gmock.h>
-#include <google/protobuf/message.h>
+#include <google/protobuf/message_lite.h>
#include <gtest/gtest.h>
#include <string.h>
#include <fcntl.h>
@@ -42,13 +42,6 @@
ASSERT_TRUE(tf.fd != -1);
}
- string getSerializedString(::google::protobuf::Message& message) {
- string expectedStr;
- message.SerializeToFileDescriptor(tf.fd);
- ReadFileToString(tf.path, &expectedStr);
- return expectedStr;
- }
-
protected:
TemporaryFile tf;
@@ -61,35 +54,39 @@
SystemPropertiesParser parser;
SystemPropertiesProto expected;
- expected.set_aaudio_hw_burst_min_usec(2000);
- expected.set_aaudio_mmap_exclusive_policy(2);
- expected.set_dalvik_vm_appimageformat("lz4");
- expected.set_gsm_operator_isroaming(false);
- expected.set_init_svc_vendor_imsqmidaemon(SystemPropertiesProto_Status_STATUS_RUNNING);
- expected.set_init_svc_vendor_init_radio_sh(SystemPropertiesProto_Status_STATUS_STOPPED);
- expected.set_net_dns1("2001:4860:4860::8844");
- expected.add_net_tcp_buffersize_wifi(524288);
- expected.add_net_tcp_buffersize_wifi(2097152);
- expected.add_net_tcp_buffersize_wifi(4194304);
- expected.add_net_tcp_buffersize_wifi(262144);
- expected.add_net_tcp_buffersize_wifi(524288);
- expected.add_net_tcp_buffersize_wifi(1048576);
- expected.set_nfc_initialized(true);
- expected.set_persist_radio_vt_enable(1);
- expected.add_ro_boot_boottime("1BLL:85");
- expected.add_ro_boot_boottime("1BLE:898");
- expected.add_ro_boot_boottime("2BLL:0");
- expected.add_ro_boot_boottime("2BLE:862");
- expected.add_ro_boot_boottime("SW:6739");
- expected.add_ro_boot_boottime("KL:340");
- expected.set_ro_bootimage_build_date_utc(1509394807LL);
- expected.set_ro_bootimage_build_fingerprint("google/marlin/marlin:P/MASTER/jinyithu10301320:eng/dev-keys");
+ expected.mutable_aac_drc()->set_cut(123);
+ expected.mutable_aaudio()->set_hw_burst_min_usec(2000);
+ expected.mutable_aaudio()->set_mmap_exclusive_policy(2);
+ expected.mutable_dalvik_vm()->set_appimageformat("lz4");
+ expected.set_drm_64bit_enabled(false);
+ expected.mutable_init_svc()->set_adbd(
+ SystemPropertiesProto_InitSvc_Status_STATUS_RUNNING);
+ expected.mutable_init_svc()->set_lmkd(
+ SystemPropertiesProto_InitSvc_Status_STATUS_STOPPED);
+ expected.set_media_mediadrmservice_enable(true);
+
+ SystemPropertiesProto::Ro* ro = expected.mutable_ro();
+ ro->mutable_boot()->add_boottime("1BLL:85");
+ ro->mutable_boot()->add_boottime("1BLE:898");
+ ro->mutable_boot()->add_boottime("2BLL:0");
+ ro->mutable_boot()->add_boottime("2BLE:862");
+ ro->mutable_boot()->add_boottime("SW:6739");
+ ro->mutable_boot()->add_boottime("KL:340");
+ ro->mutable_bootimage()->set_build_date_utc(1509394807LL);
+ ro->mutable_bootimage()->set_build_fingerprint(
+ "google/marlin/marlin:P/MASTER/jinyithu10301320:eng/dev-keys");
+ ro->mutable_hardware()->set_value("marlin");
+ ro->mutable_hardware()->set_power("marlin-profile");
+ ro->mutable_product()->add_cpu_abilist("arm64-v8a");
+ ro->mutable_product()->add_cpu_abilist("armeabi-v7a");
+ ro->mutable_product()->add_cpu_abilist("armeabi");
+ ro->mutable_product()->mutable_vendor()->set_brand("google");
int fd = open(testFile.c_str(), O_RDONLY);
ASSERT_TRUE(fd != -1);
CaptureStdout();
ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
- EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ EXPECT_EQ(GetCapturedStdout(), expected.SerializeAsString());
close(fd);
}
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index cb5fd02..fb8ef63 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -120,14 +120,6 @@
libservices \
libutils \
-relative_path_prefix := nativetest64/incidentd_test
-testdata_files := $(call find-subdir-files, testdata/*)
-
-GEN := $(addprefix $(TARGET_OUT_DATA)/$(relative_path_prefix)/, $(testdata_files))
-$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
-$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
-$(GEN): $(TARGET_OUT_DATA)/$(relative_path_prefix)/testdata/% : $(LOCAL_PATH)/testdata/%
- $(transform-generated-source)
-LOCAL_GENERATED_SOURCES += $(GEN)
+LOCAL_TEST_DATA := $(call find-test-data-in-subdirs, $(LOCAL_PATH), *, testdata)
include $(BUILD_NATIVE_TEST)
diff --git a/cmds/incidentd/AndroidTest.xml b/cmds/incidentd/AndroidTest.xml
new file mode 100644
index 0000000..7f0e4ee
--- /dev/null
+++ b/cmds/incidentd/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for incidentd_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="incidentd_test->/data/nativetest/incidentd_test" />
+ <option name="push" value="testdata->/data/nativetest/testdata" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/nativetest" />
+ <option name="module-name" value="incidentd_test" />
+ </test>
+</configuration>
diff --git a/cmds/incidentd/README.md b/cmds/incidentd/README.md
index daa3924..ad0fa08 100644
--- a/cmds/incidentd/README.md
+++ b/cmds/incidentd/README.md
@@ -5,13 +5,19 @@
For the first time, build the test and create an empty directly on device:
```
-root$ make -j incidentd_test && adb shell mkdir /data/nativetest64/incidentd_test
+root$ make -j incidentd_test && adb shell mkdir /data/nativetest/incidentd_test
```
-Run the test on a device
+Run the test on a device manually
```
root$ mmm -j frameworks/base/cmds/incidentd && \
-adb push $OUT/data/nativetest64/incidentd_test/* /data/nativetest64/incidentd_test/ && \
-adb shell /data/nativetest64/incidentd_test/incidentd_test 2>/dev/null
+adb push $OUT/data/nativetest/incidentd_test/* /data/nativetest/incidentd_test/ && \
+adb shell /data/nativetest/incidentd_test/incidentd_test 2>/dev/null
```
+
+Run the test via AndroidTest.xml
+
+```
+root$ atest incidentd_test
+```
\ No newline at end of file
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index 9ee11f8..d3ec320 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -88,8 +88,8 @@
final boolean longpress = "--longpress".equals(args[index + 1]);
final int start = longpress ? index + 2 : index + 1;
inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
- if (length > start) {
- for (int i = start; i < length; i++) {
+ if (args.length > start) {
+ for (int i = start; i < args.length; i++) {
int keyCode = KeyEvent.keyCodeFromString(args[i]);
if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
keyCode = KeyEvent.keyCodeFromString("KEYCODE_" + args[i]);
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 5fcb8a1..a5eae15 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -31,5 +31,11 @@
type: "full",
export_proto_headers: true,
},
+
+ export_shared_lib_headers: [
+ "libplatformprotos",
+ ]
+
}
+
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 337aeaa..3e517bb 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -19,7 +19,7 @@
../../core/java/android/os/IStatsManager.aidl \
src/stats_log.proto \
src/statsd_config.proto \
- src/atoms_copy.proto \
+ src/atoms.proto \
src/anomaly/AnomalyMonitor.cpp \
src/anomaly/AnomalyTracker.cpp \
src/condition/CombinationConditionTracker.cpp \
@@ -51,8 +51,6 @@
src/metrics/MetricsManager.cpp \
src/metrics/metrics_manager_util.cpp \
src/packages/UidMap.cpp \
- src/storage/DropboxReader.cpp \
- src/storage/DropboxWriter.cpp \
src/storage/StorageManager.cpp \
src/StatsLogProcessor.cpp \
src/StatsService.cpp \
@@ -67,6 +65,9 @@
statsd_common_aidl_includes := \
$(LOCAL_PATH)/../../core/java
+statsd_common_static_libraries := \
+ libplatformprotos
+
statsd_common_shared_libraries := \
libbase \
libbinder \
@@ -76,7 +77,6 @@
libselinux \
libutils \
libservices \
- libandroidfw \
libprotoutil \
libstatslog \
libhardware \
@@ -121,12 +121,14 @@
LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
LOCAL_C_INCLUDES += $(statsd_common_c_includes)
+LOCAL_STATIC_LIBRARIES := $(statsd_common_static_libraries)
+
LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
libgtest_prod
LOCAL_MODULE_CLASS := EXECUTABLES
-#LOCAL_INIT_RC := statsd.rc
+LOCAL_INIT_RC := statsd.rc
include $(BUILD_EXECUTABLE)
@@ -171,20 +173,26 @@
tests/metrics/DurationMetricProducer_test.cpp \
tests/metrics/EventMetricProducer_test.cpp \
tests/metrics/ValueMetricProducer_test.cpp \
+ tests/metrics/GaugeMetricProducer_test.cpp \
tests/guardrail/StatsdStats_test.cpp
LOCAL_STATIC_LIBRARIES := \
+ $(statsd_common_static_libraries) \
libgmock
LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries)
LOCAL_PROTOC_OPTIMIZE_TYPE := lite
+include $(BUILD_NATIVE_TEST)
+
+
statsd_common_src:=
statsd_common_aidl_includes:=
statsd_common_c_includes:=
+statsd_common_static_libraries:=
+statsd_common_shared_libraries:=
-include $(BUILD_NATIVE_TEST)
##############################
diff --git a/cmds/statsd/src/Log.h b/cmds/statsd/src/Log.h
index 7852709..87f4cba 100644
--- a/cmds/statsd/src/Log.h
+++ b/cmds/statsd/src/Log.h
@@ -26,5 +26,8 @@
#include <log/log.h>
+// Use the local value to turn on/off debug logs instead of using log.tag. properties.
+// The advantage is that in production compiler can remove the logging code if the local
+// DEBUG/VERBOSE is false.
#define VLOG(...) \
if (DEBUG) ALOGD(__VA_ARGS__);
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 2df0c90..f6caca8 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -24,6 +24,7 @@
#include "android-base/stringprintf.h"
#include "guardrail/StatsdStats.h"
#include "metrics/CountMetricProducer.h"
+#include "external/StatsPullerManager.h"
#include "stats_util.h"
#include "storage/StorageManager.h"
@@ -58,15 +59,20 @@
const int FIELD_ID_METRICS = 1;
const int FIELD_ID_UID_MAP = 2;
-#define STATS_DATA_DIR "/data/system/stats-data"
+#define STATS_DATA_DIR "/data/misc/stats-data"
StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
const sp<AnomalyMonitor>& anomalyMonitor,
const std::function<void(const ConfigKey&)>& sendBroadcast)
- : mUidMap(uidMap), mAnomalyMonitor(anomalyMonitor), mSendBroadcast(sendBroadcast) {
+ : mUidMap(uidMap),
+ mAnomalyMonitor(anomalyMonitor),
+ mSendBroadcast(sendBroadcast),
+ mTimeBaseSec(time(nullptr)) {
// On each initialization of StatsLogProcessor, check stats-data directory to see if there is
// any left over data to be read.
StorageManager::sendBroadcast(STATS_DATA_DIR, mSendBroadcast);
+ StatsPullerManager statsPullerManager;
+ statsPullerManager.SetTimeBaseSec(mTimeBaseSec);
}
StatsLogProcessor::~StatsLogProcessor() {
@@ -108,12 +114,10 @@
void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
ALOGD("Updated configuration for key %s", key.ToString().c_str());
- unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(key, config);
+ unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(key, config, mTimeBaseSec);
auto it = mMetricsManagers.find(key);
- if (it != mMetricsManagers.end()) {
- it->second->finish();
- } else if (mMetricsManagers.size() > StatsdStats::kMaxConfigCount) {
+ if (it == mMetricsManagers.end() && mMetricsManagers.size() > StatsdStats::kMaxConfigCount) {
ALOGE("Can't accept more configs!");
return;
}
@@ -167,11 +171,7 @@
// First, fill in ConfigMetricsReport using current data on memory, which
// starts from filling in StatsLogReport's.
- for (auto& m : it->second->onDumpReport()) {
- // Add each vector of StatsLogReport into a repeated field.
- proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS,
- reinterpret_cast<char*>(m.get()->data()), m.get()->size());
- }
+ it->second->onDumpReport(&proto);
// Fill in UidMap.
auto uidMap = mUidMap->getOutput(key);
@@ -205,7 +205,6 @@
void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
auto it = mMetricsManagers.find(key);
if (it != mMetricsManagers.end()) {
- it->second->finish();
mMetricsManagers.erase(it);
mUidMap->OnConfigRemoved(key);
}
@@ -231,8 +230,9 @@
mLastByteSizeTimes[key] = timestampNs;
if (totalBytes >
StatsdStats::kMaxMetricsBytesPerConfig) { // Too late. We need to start clearing data.
- // We ignore the return value so we force each metric producer to clear its contents.
- metricsManager.onDumpReport();
+ // TODO(b/70571383): By 12/15/2017 add API to drop data directly
+ ProtoOutputStream proto;
+ metricsManager.onDumpReport(&proto);
StatsdStats::getInstance().noteDataDropped(key);
VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
} else if (totalBytes > .9 * StatsdStats::kMaxMetricsBytesPerConfig) {
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index a4df23a..7ec4e4b 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -76,6 +76,8 @@
// to retrieve the stored data.
std::function<void(const ConfigKey& key)> mSendBroadcast;
+ const long mTimeBaseSec;
+
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast);
FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index dc12efb..4dd2539 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -23,7 +23,6 @@
#include "config/ConfigManager.h"
#include "guardrail/MemoryLeakTrackUtil.h"
#include "guardrail/StatsdStats.h"
-#include "storage/DropboxReader.h"
#include "storage/StorageManager.h"
#include <android-base/file.h>
@@ -46,7 +45,7 @@
namespace statsd {
constexpr const char* kPermissionDump = "android.permission.DUMP";
-#define STATS_SERVICE_DIR "/data/system/stats-service"
+#define STATS_SERVICE_DIR "/data/misc/stats-service"
// ======================================================================
/**
@@ -201,11 +200,6 @@
return cmd_config(in, out, err, args);
}
- // adb shell cmd stats print-stats-log
- if (!args[0].compare(String8("print-stats-log")) && args.size() > 1) {
- return cmd_print_stats_log(out, args);
- }
-
if (!args[0].compare(String8("print-uid-map"))) {
return cmd_print_uid_map(out);
}
@@ -503,15 +497,6 @@
return NO_ERROR;
}
-status_t StatsService::cmd_print_stats_log(FILE* out, const Vector<String8>& args) {
- long msec = 0;
-
- if (args.size() > 2) {
- msec = strtol(args[2].string(), NULL, 10);
- }
- return DropboxReader::readStatsLogs(out, args[1].string(), msec);
-}
-
status_t StatsService::cmd_print_uid_map(FILE* out) {
mUidMap->printUidMap(out);
return NO_ERROR;
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index e8b4083..162a34b 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -23,6 +23,7 @@
#include <android/os/IIncidentManager.h>
#include <android/os/IncidentReportArgs.h>
#include <binder/IServiceManager.h>
+#include <statslog.h>
#include <time.h>
namespace android {
@@ -224,6 +225,9 @@
}
StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.name());
+
+ android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(),
+ mConfigKey.GetName().c_str(), mAlert.name().c_str());
}
void AnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
@@ -317,7 +321,7 @@
for (const auto& kv : matchedAlarms) {
declareAnomaly(timestampNs /* TODO: , kv.first */);
mAlarms.erase(kv.first);
- firedAlarms.erase(kv.second); // No one else can also own it, so we're done with it.
+ firedAlarms.erase(kv.second); // No one else can also own it, so we're done with it.
}
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index c81fc1d..1c6d9b0 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -21,6 +21,8 @@
option java_package = "com.android.os";
option java_outer_classname = "AtomsProto";
+import "frameworks/base/core/proto/android/app/activitymanager.proto";
+
/**
* The master atom class. This message defines all of the available
* raw stats log events from the Android system, also known as "atoms."
@@ -77,37 +79,49 @@
IsolatedUidChanged isolated_uid_changed = 43;
PacketWakeupOccurred packet_wakeup_occurred = 44;
DropboxErrorChanged dropbox_error_changed = 45;
+ AnomalyDetected anomaly_detected = 46;
+ AppHook app_hook = 47;
// TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
// Pulled events will start at field 1000.
oneof pulled {
- WifiBytesTransferred wifi_bytes_transferred = 1000;
- WifiBytesTransferredByFgBg wifi_bytes_transferred_by_fg_bg = 1001;
- MobileBytesTransferred mobile_bytes_transferred = 1002;
- MobileBytesTransferredByFgBg mobile_bytes_transferred_by_fg_bg = 1003;
- KernelWakelockPulled kernel_wakelock_pulled = 1004;
- PowerStatePlatformSleepStatePulled power_state_platform_sleep_state_pulled = 1005;
- PowerStateVoterPulled power_state_voter_pulled = 1006;
- PowerStateSubsystemSleepStatePulled power_state_subsystem_sleep_state_pulled = 1007;
- CpuTimePerFreqPulled cpu_time_per_freq_pulled = 1008;
- CpuTimePerUidPulled cpu_time_per_uid_pulled = 1009;
- CpuTimePerUidFreqPulled cpu_time_per_uid_freq_pulled = 1010;
- WifiActivityEnergyInfoPulled wifi_activity_energy_info_pulled = 1011;
- ModemActivityInfoPulled modem_activity_info_pulled = 1012;
+ WifiBytesTransfer wifi_bytes_transfer = 1000;
+ WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 1001;
+ MobileBytesTransfer mobile_bytes_transfer = 1002;
+ MobileBytesTransferByFgBg mobile_bytes_transfer_by_fg_bg = 1003;
+ KernelWakelock kernel_wakelock = 1004;
+ PlatformSleepState platform_sleep_state = 1005;
+ SleepStateVoter sleep_state_voter = 1006;
+ SubsystemSleepState subsystem_sleep_state = 1007;
+ CpuTimePerFreq cpu_time_per_freq = 1008;
+ CpuTimePerUid cpu_time_per_uid = 1009;
+ CpuTimePerUidFreq cpu_time_per_uid_freq = 1010;
+ WifiActivityEnergyInfo wifi_activity_energy_info = 1011;
+ ModemActivityInfo modem_activity_info = 1012;
+ AttributionChainDummyAtom attribution_chain_dummy_atom = 10000;
}
}
/**
- * A WorkSource represents the chained attribution of applications that
- * resulted in a particular bit of work being done.
+ * An attribution represents an application or module that is part of process where a particular bit
+ * of work is done.
*/
-message WorkSource {
- // The uid for a given element in the attribution chain.
- repeated int32 uid = 1;
- // The (optional) string tag for an element in the attribution chain. If the
- // element has no tag, it is encoded as an empty string.
- repeated string tag = 2;
+message Attribution {
+ // The uid for an application or module.
+ optional int32 uid = 1;
+ // The string tag for the attribution node.
+ optional string tag = 2;
+}
+
+/**
+ * An attribution chain represents the chained attributions of applications or modules that
+ * resulted in a particular bit of work being done.
+ * The ordering of the attributions is that of calls, that is uid = [A, B, C] if A calls B that
+ * calls C.
+ */
+message AttributionChain {
+ repeated Attribution attribution = 1;
}
/*
@@ -125,7 +139,7 @@
* - The CamelCase name of the message type should match the
* underscore_separated name as defined in Atom.
* - If an atom represents work that can be attributed to an app, there can
- * be exactly one WorkSource field. It must be field number 1.
+ * be exactly one AttributionChain field. It must be field number 1.
* - A field that is a uid should be a string field, tagged with the [xxx]
* annotation. The generated code on android will be represented by UIDs,
* and those UIDs will be translated in xxx to those strings.
@@ -136,6 +150,11 @@
* *****************************************************************************
*/
+message AttributionChainDummyAtom {
+ optional AttributionChain attribution_chain = 1;
+ optional int32 value = 2;
+}
+
/**
* Logs when the screen state changes.
*
@@ -168,7 +187,7 @@
// The state.
// TODO: Use the real (mapped) process states.
- optional int32 state = 2;
+ optional android.app.ProcessState state = 2;
}
/**
@@ -799,13 +818,53 @@
optional int32 is_foreground = 7;
}
+/*
+ * Allows other apps to push events into statsd.
+ * Logged from:
+ * frameworks/base/core/java/android/util/StatsLog.java
+ */
+message AppHook {
+ // The uid of the application that sent this custom atom.
+ optional int32 uid = 1;
+
+ // An arbitrary label chosen by the developer. For Android P, the label should be in [0, 16).
+ optional int32 label = 2;
+
+ // Allows applications to easily use a custom event as start/stop boundaries (ie, define custom
+ // predicates for the metrics).
+ enum State {
+ UNKNOWN = 0;
+ UNSPECIFIED = 1; // For events that are known to not represent START/STOP.
+ STOP = 2;
+ START = 3;
+ }
+ optional State state = 3;
+}
+
+/**
+ * Logs when statsd detects an anomaly.
+ *
+ * Logged from:
+ * frameworks/base/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+ */
+message AnomalyDetected {
+ // Uid that owns the config whose anomaly detection alert fired.
+ optional int32 config_uid = 1;
+
+ // Name of the config whose anomaly detection alert fired.
+ optional string config_name = 2;
+
+ // Name of the alert (i.e. name of the anomaly that was detected).
+ optional string alert_name = 3;
+}
+
/**
* Pulls bytes transferred via wifi (Sum of foreground and background usage).
*
* Pulled from:
* StatsCompanionService (using BatteryStats to get which interfaces are wifi)
*/
-message WifiBytesTransferred {
+message WifiBytesTransfer {
optional int32 uid = 1;
optional int64 rx_bytes = 2;
@@ -823,7 +882,7 @@
* Pulled from:
* StatsCompanionService (using BatteryStats to get which interfaces are wifi)
*/
-message WifiBytesTransferredByFgBg {
+message WifiBytesTransferByFgBg {
optional int32 uid = 1;
// 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats.
@@ -844,7 +903,7 @@
* Pulled from:
* StatsCompanionService (using BatteryStats to get which interfaces are mobile data)
*/
-message MobileBytesTransferred {
+message MobileBytesTransfer {
optional int32 uid = 1;
optional int64 rx_bytes = 2;
@@ -862,7 +921,7 @@
* Pulled from:
* StatsCompanionService (using BatteryStats to get which interfaces are mobile data)
*/
-message MobileBytesTransferredByFgBg {
+message MobileBytesTransferByFgBg {
optional int32 uid = 1;
// 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats.
@@ -884,7 +943,7 @@
* Pulled from:
* StatsCompanionService using KernelWakelockReader.
*/
-message KernelWakelockPulled {
+message KernelWakelock {
optional string name = 1;
optional int32 count = 2;
@@ -900,7 +959,7 @@
* Definition here:
* hardware/interfaces/power/1.0/types.hal
*/
-message PowerStatePlatformSleepStatePulled {
+message PlatformSleepState {
optional string name = 1;
optional uint64 residency_in_msec_since_boot = 2;
optional uint64 total_transitions = 3;
@@ -913,9 +972,9 @@
* Definition here:
* hardware/interfaces/power/1.0/types.hal
*/
-message PowerStateVoterPulled {
- optional string power_state_platform_sleep_state_name = 1;
- optional string power_state_voter_name = 2;
+message SleepStateVoter {
+ optional string platform_sleep_state_name = 1;
+ optional string voter_name = 2;
optional uint64 total_time_in_msec_voted_for_since_boot = 3;
optional uint64 total_number_of_times_voted_since_boot = 4;
}
@@ -926,9 +985,9 @@
* Definition here:
* hardware/interfaces/power/1.1/types.hal
*/
-message PowerStateSubsystemSleepStatePulled {
- optional string power_state_subsystem_name = 1;
- optional string power_state_subsystem_sleep_state_name = 2;
+message SubsystemSleepState {
+ optional string subsystem_name = 1;
+ optional string subsystem_sleep_state_name = 2;
optional uint64 residency_in_msec_since_boot = 3;
optional uint64 total_transitions = 4;
optional uint64 last_entry_timestamp_ms = 5;
@@ -965,17 +1024,17 @@
* if current time is smaller than last value, there must be a cpu
* hotplug event, and the current time is taken as delta.
*/
-message CpuTimePerFreqPulled {
+message CpuTimePerFreq {
optional uint32 cluster = 1;
optional uint32 freq_index = 2;
- optional uint64 time = 3;
+ optional uint64 time_ms = 3;
}
/**
* Pulls Cpu Time Per Uid.
* Note that isolated process uid time should be attributed to host uids.
*/
-message CpuTimePerUidPulled {
+message CpuTimePerUid {
optional uint64 uid = 1;
optional uint64 user_time_ms = 2;
optional uint64 sys_time_ms = 3;
@@ -986,7 +1045,7 @@
* Note that isolated process uid time should be attributed to host uids.
* For each uid, we order the time by descending frequencies.
*/
-message CpuTimePerUidFreqPulled {
+message CpuTimePerUidFreq {
optional uint64 uid = 1;
optional uint64 freq_idx = 2;
optional uint64 time_ms = 3;
@@ -1024,7 +1083,7 @@
/**
* Pulls Wifi Controller Activity Energy Info
*/
-message WifiActivityEnergyInfoPulled {
+message WifiActivityEnergyInfo {
// timestamp(wall clock) of record creation
optional uint64 timestamp_ms = 1;
// stack reported state
@@ -1043,7 +1102,7 @@
/**
* Pulls Modem Activity Energy Info
*/
-message ModemActivityInfoPulled {
+message ModemActivityInfo {
// timestamp(wall clock) of record creation
optional uint64 timestamp_ms = 1;
// sleep time in ms.
@@ -1072,4 +1131,4 @@
optional uint64 controller_rx_time_ms = 9;
// product of current(mA), voltage(V) and time(ms)
optional uint64 energy_used = 10;
-}
\ No newline at end of file
+}
diff --git a/cmds/statsd/src/atoms_copy.proto b/cmds/statsd/src/atoms_copy.proto
deleted file mode 100644
index 72bb38a..0000000
--- a/cmds/statsd/src/atoms_copy.proto
+++ /dev/null
@@ -1,1077 +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.
- */
-
-syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
-
-
-// TODO: Not the right package and class name
-package android.os.statsd;
-option java_package = "com.android.os";
-option java_outer_classname = "AtomsProto";
-
-/**
- * The master atom class. This message defines all of the available
- * raw stats log events from the Android system, also known as "atoms."
- *
- * This field contains a single oneof with all of the available messages.
- * The stats-log-api-gen tool runs as part of the Android build and
- * generates the android.util.StatsLog class, which contains the constants
- * and methods that Android uses to log.
- *
- * This Atom class is not actually built into the Android system.
- * Instead, statsd on Android constructs these messages synthetically,
- * in the format defined here and in stats_log.proto.
- */
-message Atom {
- // Pushed atoms start at 2.
- oneof pushed {
- // For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
- BleScanStateChanged ble_scan_state_changed = 2;
- BleUnoptimizedScanStateChanged ble_unoptimized_scan_state_changed = 3;
- BleScanResultReceived ble_scan_result_received = 4;
- SensorStateChanged sensor_state_changed = 5;
- GpsScanStateChanged gps_scan_state_changed = 6; // TODO: untested
- SyncStateChanged sync_state_changed = 7;
- ScheduledJobStateChanged scheduled_job_state_changed = 8;
- ScreenBrightnessChanged screen_brightness_changed = 9;
- WakelockStateChanged wakelock_state_changed = 10;
- LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 11;
- MobileRadioPowerStateChanged mobile_radio_power_state_changed = 12;
- WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13;
- // TODO: 14-19 are blank, but need not be
- BatterySaverModeStateChanged battery_saver_mode_state_changed = 20;
- DeviceIdleModeStateChanged device_idle_mode_state_changed = 21;
- DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22;
- AudioStateChanged audio_state_changed = 23;
- MediaCodecActivityChanged media_codec_activity_changed = 24;
- CameraStateChanged camera_state_changed = 25;
- FlashlightStateChanged flashlight_state_changed = 26;
- UidProcessStateChanged uid_process_state_changed = 27;
- ProcessLifeCycleStateChanged process_life_cycle_state_changed = 28;
- ScreenStateChanged screen_state_changed = 29;
- BatteryLevelChanged battery_level_changed = 30;
- ChargingStateChanged charging_state_changed = 31;
- PluggedStateChanged plugged_state_changed = 32;
- DeviceTemperatureReported device_temperature_reported = 33;
- DeviceOnStatusChanged device_on_status_changed = 34;
- WakeupAlarmOccurred wakeup_alarm_occurred = 35;
- KernelWakeupReported kernel_wakeup_reported = 36;
- WifiLockStateChanged wifi_lock_state_changed = 37;
- WifiSignalStrengthChanged wifi_signal_strength_changed = 38;
- WifiScanStateChanged wifi_scan_state_changed = 39;
- PhoneSignalStrengthChanged phone_signal_strength_changed = 40;
- SettingChanged setting_changed = 41;
- ActivityForegroundStateChanged activity_foreground_state_changed = 42;
- IsolatedUidChanged isolated_uid_changed = 43;
- PacketWakeupOccurred packet_wakeup_occurred = 44;
- DropboxErrorChanged dropbox_error_changed = 45;
- // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
- }
-
- // Pulled events will start at field 1000.
- oneof pulled {
- WifiBytesTransferred wifi_bytes_transferred = 1000;
- WifiBytesTransferredByFgBg wifi_bytes_transferred_by_fg_bg = 1001;
- MobileBytesTransferred mobile_bytes_transferred = 1002;
- MobileBytesTransferredByFgBg mobile_bytes_transferred_by_fg_bg = 1003;
- KernelWakelockPulled kernel_wakelock_pulled = 1004;
- PowerStatePlatformSleepStatePulled power_state_platform_sleep_state_pulled = 1005;
- PowerStateVoterPulled power_state_voter_pulled = 1006;
- PowerStateSubsystemSleepStatePulled power_state_subsystem_sleep_state_pulled = 1007;
- CpuTimePerFreqPulled cpu_time_per_freq_pulled = 1008;
- CpuTimePerUidPulled cpu_time_per_uid_pulled = 1009;
- CpuTimePerUidFreqPulled cpu_time_per_uid_freq_pulled = 1010;
- WifiActivityEnergyInfoPulled wifi_activity_energy_info_pulled = 1011;
- ModemActivityInfoPulled modem_activity_info_pulled = 1012;
- }
-}
-
-/**
- * A WorkSource represents the chained attribution of applications that
- * resulted in a particular bit of work being done.
- */
-message WorkSource {
- // The uid for a given element in the attribution chain.
- repeated int32 uid = 1;
- // The (optional) string tag for an element in the attribution chain. If the
- // element has no tag, it is encoded as an empty string.
- repeated string tag = 2;
-}
-
-/*
- * *****************************************************************************
- * Below are all of the individual atoms that are logged by Android via statsd.
- *
- * RULES:
- * - The field ids for each atom must start at 1, and count upwards by 1.
- * Skipping field ids is not allowed.
- * - These form an API, so renaming, renumbering or removing fields is
- * not allowed between android releases. (This is not currently enforced,
- * but there will be a tool to enforce this restriction).
- * - The types must be built-in protocol buffer types, namely, no sub-messages
- * are allowed (yet). The bytes type is also not allowed.
- * - The CamelCase name of the message type should match the
- * underscore_separated name as defined in Atom.
- * - If an atom represents work that can be attributed to an app, there can
- * be exactly one WorkSource field. It must be field number 1.
- * - A field that is a uid should be a string field, tagged with the [xxx]
- * annotation. The generated code on android will be represented by UIDs,
- * and those UIDs will be translated in xxx to those strings.
- *
- * CONVENTIONS:
- * - Events are past tense. e.g. ScreenStateChanged, not ScreenStateChange.
- * - If there is a UID, it goes first. Think in an object-oriented fashion.
- * *****************************************************************************
- */
-
-/**
- * Logs when the screen state changes.
- *
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
- */
-message ScreenStateChanged {
- // TODO: Use the real screen state.
- enum State {
- STATE_UNKNOWN = 0;
- STATE_OFF = 1;
- STATE_ON = 2;
- STATE_DOZE = 3;
- STATE_DOZE_SUSPEND = 4;
- STATE_VR = 5;
- STATE_ON_SUSPEND = 6;
- }
- // New screen state.
- optional State display_state = 1;
-}
-
-/**
- * Logs that the state of a process state, as per the activity manager, has changed.
- *
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
- */
-message UidProcessStateChanged {
- optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation
-
- // The state.
- // TODO: Use the real (mapped) process states.
- optional int32 state = 2;
-}
-
-/**
- * Logs that a process started, finished, crashed, or ANRed.
- *
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
- */
-message ProcessLifeCycleStateChanged {
- // TODO: Use the real (mapped) process states.
- optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation
-
- // TODO: What is this?
- optional string name = 2;
-
- // What lifecycle state the process changed to.
- // This enum is specific to atoms.proto.
- enum Event {
- PROCESS_FINISHED = 0;
- PROCESS_STARTED = 1;
- PROCESS_CRASHED = 2;
- PROCESS_ANRED = 3;
- }
- optional Event event = 3;
-}
-
-/**
- * Logs when the ble scan state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message BleScanStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 2;
-}
-
-/**
- * Logs when an unoptimized ble scan state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
-message BleUnoptimizedScanStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 2;
-}
-
-/**
- * Logs reporting of a ble scan finding results.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
-message BleScanResultReceived {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- // Number of ble scan results returned.
- optional int32 num_of_results = 2;
-}
-
-/**
- * Logs when a sensor state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message SensorStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- // TODO: Is there a way to get the actual name of the sensor?
- // The id (int) of the sensor.
- optional int32 sensor_id = 2;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 3;
-}
-
-
-/**
- * Logs when GPS state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message GpsScanStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 2;
-}
-
-
-/**
- * Logs when a sync manager sync state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message SyncStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- // Name of the sync (as named in the app)
- optional string name = 2;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 3;
-}
-
-/**
- * Logs when a job scheduler job state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message ScheduledJobStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- // Name of the job (as named in the app)
- optional string name = 2;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 3;
-
- // TODO: Consider adding the stopReason (int)
-}
-
-/**
- * Logs when the audio state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message AudioStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 2;
-}
-
-/**
- * Logs when the video codec state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message MediaCodecActivityChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 2;
-}
-
-/**
- * Logs when the flashlight state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message FlashlightStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 2;
-}
-
-/**
- * Logs when the camera state changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message CameraStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 2;
-}
-
-/**
- * Logs that the state of a wakelock (per app and per wakelock name) has changed.
- *
- * Logged from:
- * TODO
- */
-message WakelockStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- // Type of wakelock.
- enum Type {
- PARTIAL = 0;
- FULL = 1;
- WINDOW = 2;
- }
- optional Type type = 2;
-
- // The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
- optional string tag = 3;
-
- enum State {
- RELEASE = 0;
- ACQUIRE = 1;
- CHANGE_RELEASE = 2;
- CHANGE_ACQUIRE = 3;
- }
- optional State state = 4;
-}
-
-/**
- * Logs when a partial wakelock is considered 'long' (over 1 min).
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message LongPartialWakelockStateChanged {
- // TODO: Add attribution instead of uid?
- optional int32 uid = 1;
-
- // The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
- optional string tag = 2;
-
- // TODO: I have no idea what this is.
- optional string history_tag = 3;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 4;
-}
-
-/**
- * Logs Battery Saver state change.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message BatterySaverModeStateChanged {
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 1;
-}
-
-/**
- * Logs Doze mode state change.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message DeviceIdleModeStateChanged {
- // TODO: Use the enum matching BatteryStats.DEVICE_IDLE_MODE_.
- enum State {
- DEVICE_IDLE_MODE_OFF = 0;
- DEVICE_IDLE_MODE_LIGHT = 1;
- DEVICE_IDLE_MODE_DEEP = 2;
- }
- optional State state = 1;
-}
-
-
-/**
- * Logs state change of Doze mode including maintenance windows.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message DeviceIdlingModeStateChanged {
- // TODO: Use the enum matching BatteryStats.DEVICE_IDLE_MODE_.
- enum State {
- DEVICE_IDLE_MODE_OFF = 0;
- DEVICE_IDLE_MODE_LIGHT = 1;
- DEVICE_IDLE_MODE_DEEP = 2;
- }
- optional State state = 1;
-}
-
-/**
- * Logs screen brightness level.
- *
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
- */
-message ScreenBrightnessChanged {
- // Screen brightness level. Should be in [-1, 255] according to PowerManager.java.
- optional int32 level = 1;
-}
-
-/**
- * Logs battery level (percent full, from 0 to 100).
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message BatteryLevelChanged {
- // Battery level. Should be in [0, 100].
- optional int32 battery_level = 1;
-}
-
-/**
- * Logs change in charging status of the device.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message ChargingStateChanged {
- // TODO: Link directly to BatteryManager.java's constants (via a proto).
- enum State {
- BATTERY_STATUS_UNKNOWN = 1;
- BATTERY_STATUS_CHARGING = 2;
- BATTERY_STATUS_DISCHARGING = 3;
- BATTERY_STATUS_NOT_CHARGING = 4;
- BATTERY_STATUS_FULL = 5;
- }
- optional State charging_state = 1;
-}
-
-/**
- * Logs whether the device is plugged in, and what power source it is using.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message PluggedStateChanged {
- // TODO: Link directly to BatteryManager.java's constants (via a proto).
- enum State {
- // Note that NONE is not in BatteryManager.java's constants.
- BATTERY_PLUGGED_NONE = 0;
- // Power source is an AC charger.
- BATTERY_PLUGGED_AC = 1;
- // Power source is a USB port.
- BATTERY_PLUGGED_USB = 2;
- // Power source is wireless.
- BATTERY_PLUGGED_WIRELESS = 4;
- }
- optional State plugged_state = 1;
-}
-
-/**
- * Logs the temperature of the device, in tenths of a degree Celsius.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message DeviceTemperatureReported {
- // Temperature in tenths of a degree C.
- optional int32 temperature = 1;
-}
-
-// TODO: Define this more precisely.
-// TODO: Log the ON state somewhere. It isn't currently logged anywhere.
-/**
- * Logs when the device turns off or on.
- *
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
- */
-message DeviceOnStatusChanged {
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 1;
-}
-
-/**
- * Logs when an app's wakeup alarm fires.
- *
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
- */
-message WakeupAlarmOccurred {
- // TODO: Add attribution instead of uid?
- optional int32 uid = 1;
-
- // Name of the wakeup alarm.
- optional string tag = 2;
-}
-
-/**
- * Logs when an an app causes the mobile radio to change state.
- * Changing from LOW to MEDIUM or HIGH can be considered the app waking the mobile radio.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message MobileRadioPowerStateChanged {
- // TODO: Add attribution instead of uid?
- optional int32 uid = 1;
-
- // TODO: Reference telephony/java/android/telephony/DataConnectionRealTimeInfo.java states.
- enum PowerState {
- DC_POWER_STATE_LOW = 1;
- DC_POWER_STATE_MEDIUM = 2;
- DC_POWER_STATE_HIGH = 3;
- }
- optional PowerState power_state = 2;
-}
-
-/**
- * Logs when an an app causes the wifi radio to change state.
- * Changing from LOW to MEDIUM or HIGH can be considered the app waking the wifi radio.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message WifiRadioPowerStateChanged {
- // TODO: Add attribution instead of uid?
- optional int32 uid = 1;
-
- // TODO: Reference telephony/java/android/telephony/DataConnectionRealTimeInfo.java states.
- enum PowerState {
- DC_POWER_STATE_LOW = 1;
- DC_POWER_STATE_MEDIUM = 2;
- DC_POWER_STATE_HIGH = 3;
- }
- optional PowerState power_state = 2;
-}
-
-/**
- * Logs kernel wakeup reasons and aborts.
- *
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
- */
-message KernelWakeupReported {
- // Name of the kernel wakeup reason (or abort).
- optional string wakeup_reason_name = 1;
-
- // Duration (in microseconds) for the wake-up interrupt to be serviced.
- optional int64 duration_usec = 2;
-}
-
-/**
- * Logs wifi locks held by an app.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message WifiLockStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 2;
-}
-
-/**
- * Logs wifi signal strength changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message WifiSignalStrengthChanged {
- // TODO: Reference the actual telephony/java/android/telephony/SignalStrength.java states.
- enum SignalStrength {
- SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
- SIGNAL_STRENGTH_POOR = 1;
- SIGNAL_STRENGTH_MODERATE = 2;
- SIGNAL_STRENGTH_GOOD = 3;
- SIGNAL_STRENGTH_GREAT = 4;
- }
- optional SignalStrength signal_strength = 1;
-}
-
-/**
- * Logs wifi scans performed by an app.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message WifiScanStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
-
- enum State {
- OFF = 0;
- ON = 1;
- }
- optional State state = 2;
-}
-
-/**
- * Logs phone signal strength changes.
- *
- * Logged from:
- * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-message PhoneSignalStrengthChanged {
- // TODO: Reference the actual telephony/java/android/telephony/SignalStrength.java states.
- enum SignalStrength {
- SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
- SIGNAL_STRENGTH_POOR = 1;
- SIGNAL_STRENGTH_MODERATE = 2;
- SIGNAL_STRENGTH_GOOD = 3;
- SIGNAL_STRENGTH_GREAT = 4;
- }
- optional SignalStrength signal_strength = 1;
-}
-
-/**
- * Logs that a setting was updated.
- * Logged from:
- * frameworks/base/core/java/android/provider/Settings.java
- * The tag and is_default allow resetting of settings to default values based on the specified
- * tag. See Settings#putString(ContentResolver, String, String, String, boolean) for more details.
- */
-message SettingChanged {
- // The name of the setting.
- optional string setting = 1;
-
- // The change being imposed on this setting. May represent a number, eg "3".
- optional string value = 2;
-
- // The new value of this setting. For most settings, this is same as value. For some settings,
- // value is +X or -X where X represents an element in a set. For example, if the previous value
- // is A,B,C and value is -B, then new_value is A,C and prev_value is A,B,C.
- // The +/- feature is currently only used for location_providers_allowed.
- optional string new_value = 3;
-
- // The previous value of this setting.
- optional string prev_value = 4;
-
- // 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;
-
- // The user ID associated. Defined in android/os/UserHandle.java
- optional int32 user = 7;
-}
-
-/**
- * Logs activity going to foreground or background
- *
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java
- */
-message ActivityForegroundStateChanged {
- enum Activity {
- MOVE_TO_BACKGROUND = 0;
- MOVE_TO_FOREGROUND = 1;
- }
- optional int32 uid = 1;
- optional string pkg_name = 2;
- optional string class_name = 3;
- optional Activity activity = 4;
-}
-
-/**
- * Logs when an error is written to dropbox.
- * Logged from:
- * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
- */
-message DropboxErrorChanged {
- // The uid if available. -1 means not available.
- optional int32 uid = 1;
-
- // Tag used when recording this error to dropbox. Contains data_ or system_ prefix.
- optional string tag = 2;
-
- // The name of the process.
- optional string process_name = 3;
-
- // The pid if available. -1 means not available.
- optional int32 pid = 4;
-
- // 1 indicates is instant app. -1 indicates Not applicable.
- optional int32 is_instant_app = 5;
-
- // The activity name if available.
- optional string activity_name = 6;
-
- // 1 indicates in foreground. -1 indicates not available.
- optional int32 is_foreground = 7;
-}
-
-/**
- * Pulls bytes transferred via wifi (Sum of foreground and background usage).
- *
- * Pulled from:
- * StatsCompanionService (using BatteryStats to get which interfaces are wifi)
- */
-message WifiBytesTransferred {
- optional int32 uid = 1;
-
- optional int64 rx_bytes = 2;
-
- optional int64 rx_packets = 3;
-
- optional int64 tx_bytes = 4;
-
- optional int64 tx_packets = 5;
-}
-
-/**
- * Pulls bytes transferred via wifi (separated by foreground and background usage).
- *
- * Pulled from:
- * StatsCompanionService (using BatteryStats to get which interfaces are wifi)
- */
-message WifiBytesTransferredByFgBg {
- optional int32 uid = 1;
-
- // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats.
- optional int32 is_foreground = 2;
-
- optional int64 rx_bytes = 3;
-
- optional int64 rx_packets = 4;
-
- optional int64 tx_bytes = 5;
-
- optional int64 tx_packets = 6;
-}
-
-/**
- * Pulls bytes transferred via mobile networks (Sum of foreground and background usage).
- *
- * Pulled from:
- * StatsCompanionService (using BatteryStats to get which interfaces are mobile data)
- */
-message MobileBytesTransferred {
- optional int32 uid = 1;
-
- optional int64 rx_bytes = 2;
-
- optional int64 rx_packets = 3;
-
- optional int64 tx_bytes = 4;
-
- optional int64 tx_packets = 5;
-}
-
-/**
- * Pulls bytes transferred via mobile networks (separated by foreground and background usage).
- *
- * Pulled from:
- * StatsCompanionService (using BatteryStats to get which interfaces are mobile data)
- */
-message MobileBytesTransferredByFgBg {
- optional int32 uid = 1;
-
- // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats.
- optional int32 is_foreground = 2;
-
- optional int64 rx_bytes = 3;
-
- optional int64 rx_packets = 4;
-
- optional int64 tx_bytes = 5;
-
- optional int64 tx_packets = 6;
-}
-
-/**
- * Pulls the kernel wakelock durations. This atom is adapted from
- * android/internal/os/KernelWakelockStats.java
- *
- * Pulled from:
- * StatsCompanionService using KernelWakelockReader.
- */
-message KernelWakelockPulled {
- optional string name = 1;
-
- optional int32 count = 2;
-
- optional int32 version = 3;
-
- optional int64 time = 4;
-}
-
-/**
- * Pulls PowerStatePlatformSleepState.
- *
- * Definition here:
- * hardware/interfaces/power/1.0/types.hal
- */
-message PowerStatePlatformSleepStatePulled {
- optional string name = 1;
- optional uint64 residency_in_msec_since_boot = 2;
- optional uint64 total_transitions = 3;
- optional bool supported_only_in_suspend = 4;
-}
-
-/**
- * Pulls PowerStateVoter.
- *
- * Definition here:
- * hardware/interfaces/power/1.0/types.hal
- */
-message PowerStateVoterPulled {
- optional string power_state_platform_sleep_state_name = 1;
- optional string power_state_voter_name = 2;
- optional uint64 total_time_in_msec_voted_for_since_boot = 3;
- optional uint64 total_number_of_times_voted_since_boot = 4;
-}
-
-/**
- * Pulls PowerStateSubsystemSleepState.
- *
- * Definition here:
- * hardware/interfaces/power/1.1/types.hal
- */
-message PowerStateSubsystemSleepStatePulled {
- optional string power_state_subsystem_name = 1;
- optional string power_state_subsystem_sleep_state_name = 2;
- optional uint64 residency_in_msec_since_boot = 3;
- optional uint64 total_transitions = 4;
- optional uint64 last_entry_timestamp_ms = 5;
- optional bool supported_only_in_suspend = 6;
-}
-
-/**
- * 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;
-}
-
-/**
- * Pulls Cpu time per frequency.
- * Note: this should be pulled for gauge metric only, without condition.
- * The puller keeps internal state of last values. It should not be pulled by
- * different metrics.
- * The pulled data is delta of cpu time from last pull, calculated as
- * following:
- * if current time is larger than last value, take delta between the two.
- * if current time is smaller than last value, there must be a cpu
- * hotplug event, and the current time is taken as delta.
- */
-message CpuTimePerFreqPulled {
- optional uint32 cluster = 1;
- optional uint32 freq_index = 2;
- optional uint64 time = 3;
-}
-
-/**
- * Pulls Cpu Time Per Uid.
- * Note that isolated process uid time should be attributed to host uids.
- */
-message CpuTimePerUidPulled {
- optional uint64 uid = 1;
- optional uint64 user_time_ms = 2;
- optional uint64 sys_time_ms = 3;
-}
-
-/**
- * Pulls Cpu Time Per Uid per frequency.
- * Note that isolated process uid time should be attributed to host uids.
- * For each uid, we order the time by descending frequencies.
- */
-message CpuTimePerUidFreqPulled {
- optional uint64 uid = 1;
- optional uint64 freq_idx = 2;
- optional uint64 time_ms = 3;
-}
-
-/*
- * Logs the reception of an incoming network packet causing the main system to wake up for
- * processing that packet. These events are notified by the kernel via Netlink NFLOG to Netd
- * and processed by WakeupController.cpp.
- */
-message PacketWakeupOccurred {
- // The uid owning the socket into which the packet was delivered, or -1 if the packet was
- // delivered nowhere.
- optional int32 uid = 1;
- // The interface name on which the packet was received.
- optional string iface = 2;
- // The ethertype value of the packet.
- optional int32 ethertype = 3;
- // String representation of the destination MAC address of the packet.
- optional string destination_hardware_address = 4;
- // String representation of the source address of the packet if this was an IP packet.
- optional string source_ip = 5;
- // String representation of the destination address of the packet if this was an IP packet.
- optional string destination_ip = 6;
- // The value of the protocol field if this was an IPv4 packet or the value of the Next Header
- // field if this was an IPv6 packet. The range of possible values is the same for both IP
- // families.
- optional int32 ip_next_header = 7;
- // The source port if this was a TCP or UDP packet.
- optional int32 source_port = 8;
- // The destination port if this was a TCP or UDP packet.
- optional int32 destination_port = 9;
-}
-
-/**
- * Pulls Wifi Controller Activity Energy Info
- */
-message WifiActivityEnergyInfoPulled {
- // timestamp(wall clock) of record creation
- optional uint64 timestamp_ms = 1;
- // stack reported state
- // TODO: replace this with proto enum
- optional int32 stack_state = 2;
- // tx time in ms
- optional uint64 controller_tx_time_ms = 3;
- // rx time in ms
- optional uint64 controller_rx_time_ms = 4;
- // idle time in ms
- optional uint64 controller_idle_time_ms = 5;
- // product of current(mA), voltage(V) and time(ms)
- optional uint64 controller_energy_used = 6;
-}
-
-/**
- * Pulls Modem Activity Energy Info
- */
-message ModemActivityInfoPulled {
- // timestamp(wall clock) of record creation
- optional uint64 timestamp_ms = 1;
- // sleep time in ms.
- optional uint64 sleep_time_ms = 2;
- // idle time in ms
- optional uint64 controller_idle_time_ms = 3;
- /**
- * Tx power index
- * index 0 = tx_power < 0dBm
- * index 1 = 0dBm < tx_power < 5dBm
- * index 2 = 5dBm < tx_power < 15dBm
- * index 3 = 15dBm < tx_power < 20dBm
- * index 4 = tx_power > 20dBm
- */
- // tx time in ms at power level 0
- optional uint64 controller_tx_time_pl0_ms = 4;
- // tx time in ms at power level 1
- optional uint64 controller_tx_time_pl1_ms = 5;
- // tx time in ms at power level 2
- optional uint64 controller_tx_time_pl2_ms = 6;
- // tx time in ms at power level 3
- optional uint64 controller_tx_time_pl3_ms = 7;
- // tx time in ms at power level 4
- optional uint64 controller_tx_time_pl4_ms = 8;
- // rx time in ms at power level 5
- optional uint64 controller_rx_time_ms = 9;
- // product of current(mA), voltage(V) and time(ms)
- optional uint64 energy_used = 10;
-}
\ No newline at end of file
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index 02aca1a..bb4b817 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.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 "CombinationConditionTracker.h"
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 540199d..a6d2719 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -29,7 +29,13 @@
namespace os {
namespace statsd {
-#define STATS_SERVICE_DIR "/data/system/stats-service"
+using std::map;
+using std::pair;
+using std::set;
+using std::string;
+using std::vector;
+
+#define STATS_SERVICE_DIR "/data/misc/stats-service"
using android::base::StringPrintf;
using std::unique_ptr;
@@ -41,10 +47,17 @@
}
void ConfigManager::Startup() {
- StorageManager::readConfigFromDisk(mConfigs);
+ map<ConfigKey, StatsdConfig> configsFromDisk;
+ StorageManager::readConfigFromDisk(configsFromDisk);
+ // TODO(b/70667694): Make the configs from disk be used. And remove the fake config,
+ // and tests shouldn't call this Startup(), maybe call StartupForTest() so we don't read
+ // configs from disk for tests.
+ // for (const auto& pair : configsFromDisk) {
+ // UpdateConfig(pair.first, pair.second);
+ //}
- // this should be called from StatsService when it receives a statsd_config
- UpdateConfig(ConfigKey(1000, "fake"), build_fake_config());
+ // Uncomment the following line and use the hard coded config for development.
+ // UpdateConfig(ConfigKey(1000, "fake"), build_fake_config());
}
void ConfigManager::AddListener(const sp<ConfigListener>& listener) {
@@ -52,9 +65,8 @@
}
void ConfigManager::UpdateConfig(const ConfigKey& key, const StatsdConfig& config) {
- // Add to map
- mConfigs[key] = config;
- // Why doesn't this work? mConfigs.insert({key, config});
+ // Add to set
+ mConfigs.insert(key);
// Save to disk
update_saved_configs(key, config);
@@ -74,7 +86,7 @@
}
void ConfigManager::RemoveConfig(const ConfigKey& key) {
- unordered_map<ConfigKey, StatsdConfig>::iterator it = mConfigs.find(key);
+ auto it = mConfigs.find(key);
if (it != mConfigs.end()) {
// Remove from map
mConfigs.erase(it);
@@ -100,9 +112,9 @@
for (auto it = mConfigs.begin(); it != mConfigs.end();) {
// Remove from map
- if (it->first.GetUid() == uid) {
- removed.push_back(it->first);
- mConfigReceivers.erase(it->first);
+ if (it->GetUid() == uid) {
+ removed.push_back(*it);
+ mConfigReceivers.erase(*it);
it = mConfigs.erase(it);
} else {
it++;
@@ -123,10 +135,10 @@
for (auto it = mConfigs.begin(); it != mConfigs.end();) {
// Remove from map
- removed.push_back(it->first);
- auto receiverIt = mConfigReceivers.find(it->first);
+ removed.push_back(*it);
+ auto receiverIt = mConfigReceivers.find(*it);
if (receiverIt != mConfigReceivers.end()) {
- mConfigReceivers.erase(it->first);
+ mConfigReceivers.erase(*it);
}
it = mConfigs.erase(it);
}
@@ -143,7 +155,7 @@
vector<ConfigKey> ConfigManager::GetAllConfigKeys() const {
vector<ConfigKey> ret;
for (auto it = mConfigs.cbegin(); it != mConfigs.cend(); ++it) {
- ret.push_back(it->first);
+ ret.push_back(*it);
}
return ret;
}
@@ -160,15 +172,13 @@
void ConfigManager::Dump(FILE* out) {
fprintf(out, "CONFIGURATIONS (%d)\n", (int)mConfigs.size());
fprintf(out, " uid name\n");
- for (unordered_map<ConfigKey, StatsdConfig>::const_iterator it = mConfigs.begin();
- it != mConfigs.end(); it++) {
- fprintf(out, " %6d %s\n", it->first.GetUid(), it->first.GetName().c_str());
- auto receiverIt = mConfigReceivers.find(it->first);
+ for (const auto& key : mConfigs) {
+ fprintf(out, " %6d %s\n", key.GetUid(), key.GetName().c_str());
+ 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());
}
- // TODO: Print the contents of the config too.
}
}
@@ -227,7 +237,8 @@
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
// Anomaly threshold for screen-on count.
- Alert* alert = config.add_alert();
+ // TODO(b/70627390): Uncomment once the bug is fixed.
+ /*Alert* alert = config.add_alert();
alert->set_name("ALERT_1");
alert->set_metric_name("METRIC_1");
alert->set_number_of_buckets(6);
@@ -235,7 +246,7 @@
alert->set_refractory_period_secs(30);
Alert::IncidentdDetails* details = alert->mutable_incidentd_details();
details->add_section(12);
- details->add_section(13);
+ details->add_section(13);*/
// Count process state changes, slice by uid.
metric = config.add_count_metric();
@@ -246,6 +257,8 @@
keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY);
// Anomaly threshold for background count.
+ // TODO(b/70627390): Uncomment once the bug is fixed.
+ /*
alert = config.add_alert();
alert->set_name("ALERT_2");
alert->set_metric_name("METRIC_2");
@@ -254,7 +267,7 @@
alert->set_refractory_period_secs(20);
details = alert->mutable_incidentd_details();
details->add_section(14);
- details->add_section(15);
+ details->add_section(15);*/
// Count process state changes, slice by uid, while SCREEN_IS_OFF
metric = config.add_count_metric();
@@ -326,6 +339,8 @@
durationMetric->set_what("SCREEN_IS_ON");
// Anomaly threshold for background count.
+ // TODO(b/70627390): Uncomment once the bug is fixed.
+ /*
alert = config.add_alert();
alert->set_name("ALERT_8");
alert->set_metric_name("METRIC_8");
@@ -333,7 +348,7 @@
alert->set_trigger_if_sum_gt(2000000000); // 2 seconds
alert->set_refractory_period_secs(120);
details = alert->mutable_incidentd_details();
- details->add_section(-1);
+ details->add_section(-1);*/
// Value metric to count KERNEL_WAKELOCK when screen turned on
ValueMetric* valueMetric = config.add_value_metric();
@@ -355,7 +370,7 @@
GaugeMetric* gaugeMetric = config.add_gauge_metric();
gaugeMetric->set_name("METRIC_10");
gaugeMetric->set_what("DEVICE_TEMPERATURE");
- gaugeMetric->set_gauge_field(DEVICE_TEMPERATURE_KEY);
+ gaugeMetric->mutable_gauge_fields()->add_field_num(DEVICE_TEMPERATURE_KEY);
gaugeMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);
// Event matchers............
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index 90ea6c0..ea42a35 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -19,8 +19,9 @@
#include "config/ConfigKey.h"
#include "config/ConfigListener.h"
+#include <map>
+#include <set>
#include <string>
-#include <unordered_map>
#include <stdio.h>
@@ -28,13 +29,7 @@
namespace os {
namespace statsd {
-using android::RefBase;
-using std::string;
-using std::unordered_map;
-using std::vector;
-using std::pair;
-
-// Util function to Hard code a test metric for counting screen on events.
+// Util function to build a hard coded config with test metrics.
StatsdConfig build_fake_config();
/**
@@ -43,7 +38,7 @@
* TODO: Store the configs persistently too.
* TODO: Dump method for debugging.
*/
-class ConfigManager : public virtual RefBase {
+class ConfigManager : public virtual android::RefBase {
public:
ConfigManager();
virtual ~ConfigManager();
@@ -68,17 +63,17 @@
/**
* Sets the broadcast receiver for a configuration key.
*/
- void SetConfigReceiver(const ConfigKey& key, const string& pkg, const string& cls);
+ void SetConfigReceiver(const ConfigKey& key, const std::string& pkg, const std::string& cls);
/**
* Returns the package name and class name representing the broadcast receiver for this config.
*/
- const pair<string, string> GetConfigReceiver(const ConfigKey& key) const;
+ const std::pair<std::string, std::string> GetConfigReceiver(const ConfigKey& key) const;
/**
* Returns all config keys registered.
*/
- vector<ConfigKey> GetAllConfigKeys() const;
+ std::vector<ConfigKey> GetAllConfigKeys() const;
/**
* Erase any broadcast receiver associated with this config key.
@@ -121,18 +116,18 @@
/**
* The Configs that have been set. Each config should
*/
- unordered_map<ConfigKey, StatsdConfig> mConfigs;
+ std::set<ConfigKey> mConfigs;
/**
* Each config key can be subscribed by up to one receiver, specified as the package name and
* class name.
*/
- unordered_map<ConfigKey, pair<string, string>> mConfigReceivers;
+ std::map<ConfigKey, std::pair<std::string, std::string>> mConfigReceivers;
/**
* The ConfigListeners that will be told about changes.
*/
- vector<sp<ConfigListener>> mListeners;
+ std::vector<sp<ConfigListener>> mListeners;
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
index e2745d2..9738760 100644
--- a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
+++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
@@ -70,7 +70,7 @@
int idx = 0;
do {
timeMs = std::stoull(pch);
- auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_FREQ_PULLED, timestamp);
+ auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_FREQ, timestamp);
ptr->write(uid);
ptr->write(idx);
ptr->write(timeMs);
diff --git a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
index e0572dc..f69b9b5 100644
--- a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
+++ b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
@@ -65,7 +65,7 @@
pch = strtok(buf, " ");
uint64_t sysTimeMs = std::stoull(pch);
- auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_PULLED, timestamp);
+ auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID, timestamp);
ptr->write(uid);
ptr->write(userTimeMs);
ptr->write(sysTimeMs);
diff --git a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
index 3ee636d..cb9f1cc 100644
--- a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
+++ b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
@@ -92,7 +92,7 @@
const PowerStatePlatformSleepState& state = states[i];
auto statePtr = make_shared<LogEvent>(
- android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED, timestamp);
+ android::util::PLATFORM_SLEEP_STATE, timestamp);
statePtr->write(state.name);
statePtr->write(state.residencyInMsecSinceBoot);
statePtr->write(state.totalTransitions);
@@ -104,7 +104,7 @@
(long long)state.totalTransitions, state.supportedOnlyInSuspend ? 1 : 0);
for (auto voter : state.voters) {
auto voterPtr =
- make_shared<LogEvent>(android::util::POWER_STATE_VOTER_PULLED, timestamp);
+ make_shared<LogEvent>(android::util::SLEEP_STATE_VOTER, timestamp);
voterPtr->write(state.name);
voterPtr->write(voter.name);
voterPtr->write(voter.totalTimeInMsecVotedForSinceBoot);
@@ -138,7 +138,7 @@
for (size_t j = 0; j < subsystem.states.size(); j++) {
const PowerStateSubsystemSleepState& state = subsystem.states[j];
auto subsystemStatePtr = make_shared<LogEvent>(
- android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED, timestamp);
+ android::util::SUBSYSTEM_SLEEP_STATE, timestamp);
subsystemStatePtr->write(subsystem.name);
subsystemStatePtr->write(state.name);
subsystemStatePtr->write(state.residencyInMsecSinceBoot);
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 2e803c9..00a1475 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -22,33 +22,41 @@
namespace os {
namespace statsd {
-class StatsPullerManager{
+class StatsPullerManager {
public:
- virtual ~StatsPullerManager() {}
+ virtual ~StatsPullerManager() {}
- virtual void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, long intervalMs) {
- mPullerManager.RegisterReceiver(tagId, receiver, intervalMs);
- };
+ virtual void RegisterReceiver(int tagId,
+ wp <PullDataReceiver> receiver,
+ long intervalMs) {
+ mPullerManager.RegisterReceiver(tagId, receiver, intervalMs);
+ };
- virtual void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver) {
- mPullerManager.UnRegisterReceiver(tagId, receiver);
- };
+ virtual void UnRegisterReceiver(int tagId, wp <PullDataReceiver> receiver) {
+ mPullerManager.UnRegisterReceiver(tagId, receiver);
+ };
- // Verify if we know how to pull for this matcher
- bool PullerForMatcherExists(int tagId) {
- return mPullerManager.PullerForMatcherExists(tagId);
- }
+ // Verify if we know how to pull for this matcher
+ bool PullerForMatcherExists(int tagId) {
+ return mPullerManager.PullerForMatcherExists(tagId);
+ }
- void OnAlarmFired() {
- mPullerManager.OnAlarmFired();
- }
+ void OnAlarmFired() {
+ mPullerManager.OnAlarmFired();
+ }
- virtual bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data) {
- return mPullerManager.Pull(tagId, data);
- }
+ virtual bool
+ Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ return mPullerManager.Pull(tagId, data);
+ }
+
+ virtual void SetTimeBaseSec(const long timeBaseSec) {
+ mPullerManager.SetTimeBaseSec(timeBaseSec);
+ }
private:
- StatsPullerManagerImpl& mPullerManager = StatsPullerManagerImpl::GetInstance();
+ StatsPullerManagerImpl
+ & mPullerManager = StatsPullerManagerImpl::GetInstance();
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index c4688a2..d707f85 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -44,30 +44,30 @@
namespace statsd {
StatsPullerManagerImpl::StatsPullerManagerImpl()
- : mCurrentPullingInterval(LONG_MAX), mPullStartTimeMs(get_pull_start_time_ms()) {
+ : mCurrentPullingInterval(LONG_MAX) {
shared_ptr<StatsPuller> statsCompanionServicePuller = make_shared<StatsCompanionServicePuller>();
shared_ptr<StatsPuller> resourcePowerManagerPuller = make_shared<ResourcePowerManagerPuller>();
shared_ptr<StatsPuller> cpuTimePerUidPuller = make_shared<CpuTimePerUidPuller>();
shared_ptr<StatsPuller> cpuTimePerUidFreqPuller = make_shared<CpuTimePerUidFreqPuller>();
- mPullers.insert({android::util::KERNEL_WAKELOCK_PULLED,
+ mPullers.insert({android::util::KERNEL_WAKELOCK,
statsCompanionServicePuller});
- mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED,
+ mPullers.insert({android::util::WIFI_BYTES_TRANSFER,
statsCompanionServicePuller});
- mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED,
+ mPullers.insert({android::util::MOBILE_BYTES_TRANSFER,
statsCompanionServicePuller});
- mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED_BY_FG_BG,
+ mPullers.insert({android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
statsCompanionServicePuller});
- mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED_BY_FG_BG,
+ mPullers.insert({android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
statsCompanionServicePuller});
- mPullers.insert({android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED,
+ mPullers.insert({android::util::PLATFORM_SLEEP_STATE,
resourcePowerManagerPuller});
- mPullers.insert({android::util::POWER_STATE_VOTER_PULLED,
+ mPullers.insert({android::util::SLEEP_STATE_VOTER,
resourcePowerManagerPuller});
- mPullers.insert({android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED,
+ mPullers.insert({android::util::SUBSYSTEM_SLEEP_STATE,
resourcePowerManagerPuller});
- mPullers.insert({android::util::CPU_TIME_PER_UID_PULLED, cpuTimePerUidPuller});
- mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ_PULLED, cpuTimePerUidFreqPuller});
+ mPullers.insert({android::util::CPU_TIME_PER_UID, cpuTimePerUidPuller});
+ mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ, cpuTimePerUidFreqPuller});
mStatsCompanionService = StatsService::getStatsCompanionService();
}
@@ -94,11 +94,6 @@
return mPullers.find(tagId) != mPullers.end();
}
-long StatsPullerManagerImpl::get_pull_start_time_ms() const {
- // TODO: limit and align pull intervals to 10min boundaries if this turns out to be a problem
- return time(nullptr) * 1000;
-}
-
void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
long intervalMs) {
AutoMutex _l(mReceiversLock);
@@ -114,12 +109,17 @@
receiverInfo.timeInfo.first = intervalMs;
receivers.push_back(receiverInfo);
+ // Round it to the nearest minutes. This is the limit of alarm manager.
+ // In practice, we should limit it higher.
+ long roundedIntervalMs = intervalMs/1000/60 * 1000 * 60;
// There is only one alarm for all pulled events. So only set it to the smallest denom.
- if (intervalMs < mCurrentPullingInterval) {
+ if (roundedIntervalMs < mCurrentPullingInterval) {
VLOG("Updating pulling interval %ld", intervalMs);
- mCurrentPullingInterval = intervalMs;
+ mCurrentPullingInterval = roundedIntervalMs;
+ long currentTimeMs = time(nullptr) * 1000;
+ long nextAlarmTimeMs = currentTimeMs + mCurrentPullingInterval - (currentTimeMs - mTimeBaseSec * 1000) % mCurrentPullingInterval;
if (mStatsCompanionService != nullptr) {
- mStatsCompanionService->setPullingAlarms(mPullStartTimeMs, mCurrentPullingInterval);
+ mStatsCompanionService->setPullingAlarms(nextAlarmTimeMs, mCurrentPullingInterval);
} else {
VLOG("Failed to update pulling interval");
}
@@ -146,7 +146,7 @@
void StatsPullerManagerImpl::OnAlarmFired() {
AutoMutex _l(mReceiversLock);
- uint64_t currentTimeMs = time(nullptr) * 1000;
+ uint64_t currentTimeMs = time(nullptr) /60 * 60 * 1000;
vector<pair<int, vector<ReceiverInfo*>>> needToPull =
vector<pair<int, vector<ReceiverInfo*>>>();
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
index 306cc32..7c59f66 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.h
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -47,6 +47,8 @@
bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data);
+ void SetTimeBaseSec(long timeBaseSec) {mTimeBaseSec = timeBaseSec;};
+
private:
StatsPullerManagerImpl();
@@ -73,11 +75,8 @@
// for pulled metrics, it is important for the buckets to be aligned to multiple of smallest
// bucket size. All pulled metrics start pulling based on this time, so that they can be
- // correctly attributed to the correct buckets. Pulled data attach a timestamp which is the
- // request time.
- const long mPullStartTimeMs;
-
- long get_pull_start_time_ms() const;
+ // correctly attributed to the correct buckets.
+ long mTimeBaseSec;
LogEvent parse_pulled_data(String16 data);
};
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index b02b9da..bf277f0 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -310,7 +310,7 @@
for (const auto& stats : alertStats) {
auto output = configStats.add_alert_stats();
output->set_name(stats.first);
- output->set_declared_times(stats.second);
+ output->set_alerted_times(stats.second);
VLOG("alert %s declared %d times", stats.first.c_str(), stats.second);
}
}
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 1032138..01487f0 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -69,6 +69,13 @@
return false;
}
+bool LogEvent::write(int64_t value) {
+ if (mContext) {
+ return android_log_write_int64(mContext, value) >= 0;
+ }
+ return false;
+}
+
bool LogEvent::write(uint64_t value) {
if (mContext) {
return android_log_write_int64(mContext, value) >= 0;
@@ -224,7 +231,7 @@
if (elem.type == EVENT_TYPE_INT) {
pair.set_value_int(elem.data.int32);
} else if (elem.type == EVENT_TYPE_LONG) {
- pair.set_value_int(elem.data.int64);
+ pair.set_value_long(elem.data.int64);
} else if (elem.type == EVENT_TYPE_STRING) {
pair.set_value_str(elem.data.string);
} else if (elem.type == EVENT_TYPE_FLOAT) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 7e8a96b..6ff6b87 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -105,6 +105,15 @@
*/
void init();
+ /**
+ * Set timestamp if the original timestamp is missing.
+ */
+ void setTimestampNs(uint64_t timestampNs) {mTimestampNs = timestampNs;}
+
+ int size() const {
+ return mElements.size();
+ }
+
private:
/**
* Don't copy, it's slower. If we really need this we can add it but let's try to
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 36ec6b9..bc12a78 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.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 "CountMetricProducer.h"
@@ -66,7 +66,7 @@
const int conditionIndex,
const sp<ConditionWizard>& wizard,
const uint64_t startTimeNs)
- : MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric) {
+ : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard) {
// TODO: evaluate initial conditions. and set mConditionMet.
if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
@@ -83,8 +83,6 @@
mConditionSliced = true;
}
- startNewProtoOutputStreamLocked(mStartTimeNs);
-
VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
@@ -93,28 +91,19 @@
VLOG("~CountMetricProducer() called");
}
-void CountMetricProducer::startNewProtoOutputStreamLocked(long long startTime) {
- mProto = std::make_unique<ProtoOutputStream>();
- mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
- mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
-}
-
-void CountMetricProducer::finish() {
-}
-
void CountMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
- VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
+ VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
}
-std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReportLocked() {
- long long endTime = time(nullptr) * NS_PER_SEC;
+void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
+ ProtoOutputStream* protoOutput) {
+ flushIfNeededLocked(dumpTimeNs);
- // Dump current bucket if it's stale.
- // If current bucket is still on-going, don't force dump current bucket.
- // In finish(), We can force dump current bucket.
- flushIfNeededLocked(endTime);
- VLOG("metric %s dump report now...", mMetric.name().c_str());
+ protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
+ 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 %s dump report now...", mName.c_str());
for (const auto& counter : mPastBuckets) {
const HashableDimensionKey& hashableKey = counter.first;
@@ -125,59 +114,53 @@
continue;
}
long long wrapperToken =
- mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
+ protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension (KeyValuePairs).
for (const auto& kv : it->second) {
- long long dimensionToken =
- mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
- mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
+ long long dimensionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
+ protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
if (kv.has_value_str()) {
- mProto->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
+ protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
} else if (kv.has_value_int()) {
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
} else if (kv.has_value_bool()) {
- mProto->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
+ protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
} else if (kv.has_value_float()) {
- mProto->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
+ protoOutput->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
}
- mProto->end(dimensionToken);
+ protoOutput->end(dimensionToken);
}
// Then fill bucket_info (CountBucketInfo).
for (const auto& bucket : counter.second) {
- long long bucketInfoToken =
- mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
- (long long)bucket.mBucketStartNs);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
- (long long)bucket.mBucketEndNs);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)bucket.mCount);
- mProto->end(bucketInfoToken);
+ long long bucketInfoToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
+ (long long)bucket.mBucketStartNs);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
+ (long long)bucket.mBucketEndNs);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_COUNT, (long long)bucket.mCount);
+ protoOutput->end(bucketInfoToken);
VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
(long long)bucket.mBucketEndNs, (long long)bucket.mCount);
}
- mProto->end(wrapperToken);
+ protoOutput->end(wrapperToken);
}
- mProto->end(mProtoToken);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
- (long long)mCurrentBucketStartTimeNs);
+ protoOutput->end(protoToken);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
- VLOG("metric %s dump report now...", mMetric.name().c_str());
- std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked();
-
- startNewProtoOutputStreamLocked(endTime);
mPastBuckets.clear();
-
- return buffer;
+ mStartTimeNs = mCurrentBucketStartTimeNs;
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
void CountMetricProducer::onConditionChangedLocked(const bool conditionMet,
const uint64_t eventTime) {
- VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
+ VLOG("Metric %s onConditionChanged", mName.c_str());
mCondition = conditionMet;
}
@@ -189,11 +172,10 @@
// 1. Report the tuple count if the tuple count > soft limit
if (mCurrentSlicedCounter->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mCurrentSlicedCounter->size() + 1;
- StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
- newTupleCount);
+ StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
- ALOGE("CountMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+ ALOGE("CountMetric %s dropping data for dimension key %s", mName.c_str(),
newKey.c_str());
return true;
}
@@ -205,7 +187,7 @@
void CountMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const map<string, HashableDimensionKey>& conditionKey, bool condition,
- const LogEvent& event, bool scheduledPull) {
+ const LogEvent& event) {
uint64_t eventTimeNs = event.GetTimestampNs();
flushIfNeededLocked(eventTimeNs);
@@ -235,7 +217,7 @@
mCurrentSlicedCounter->find(eventKey)->second);
}
- VLOG("metric %s %s->%lld", mMetric.name().c_str(), eventKey.c_str(),
+ VLOG("metric %s %s->%lld", mName.c_str(), eventKey.c_str(),
(long long)(*mCurrentSlicedCounter)[eventKey]);
}
@@ -254,7 +236,7 @@
info.mCount = counter.second;
auto& bucketList = mPastBuckets[counter.first];
bucketList.push_back(info);
- VLOG("metric %s, dump key value: %s -> %lld", mMetric.name().c_str(), counter.first.c_str(),
+ VLOG("metric %s, dump key value: %s -> %lld", mName.c_str(), counter.first.c_str(),
(long long)counter.second);
}
@@ -267,7 +249,7 @@
uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
mCurrentBucketNum += numBucketsForward;
- VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
+ VLOG("metric %s: new bucket start time: %lld", mName.c_str(),
(long long)mCurrentBucketStartTimeNs);
}
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 800a2b9..59995d2 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -48,8 +48,6 @@
virtual ~CountMetricProducer();
- void finish() override;
-
// TODO: Implement this later.
virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version)
override{};
@@ -60,11 +58,11 @@
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
- const LogEvent& event, bool scheduledPull) override;
+ const LogEvent& event) override;
private:
- // TODO: Pass a timestamp as a parameter in onDumpReport.
- std::unique_ptr<std::vector<uint8_t>> onDumpReportLocked() override;
+ void onDumpReportLocked(const uint64_t dumpTimeNs,
+ android::util::ProtoOutputStream* protoOutput) override;
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
@@ -78,11 +76,6 @@
// Util function to flush the old packet.
void flushIfNeededLocked(const uint64_t& newEventTime);
- // Util function to init/reset the proto output stream.
- void startNewProtoOutputStreamLocked(long long timestamp);
-
- const CountMetric mMetric;
-
// TODO: Add a lock to mPastBuckets.
std::unordered_map<HashableDimensionKey, std::vector<CountBucket>> mPastBuckets;
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index cedea30..220861d 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true
+#define DEBUG false
#include "Log.h"
#include "DurationMetricProducer.h"
@@ -68,8 +68,8 @@
const sp<ConditionWizard>& wizard,
const vector<KeyMatcher>& internalDimension,
const uint64_t startTimeNs)
- : MetricProducer(key, startTimeNs, conditionIndex, wizard),
- mMetric(metric),
+ : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard),
+ mAggregationType(metric.aggregation_type()),
mStartIndex(startIndex),
mStopIndex(stopIndex),
mStopAllIndex(stopAllIndex),
@@ -93,8 +93,6 @@
mConditionSliced = true;
}
- startNewProtoOutputStreamLocked(mStartTimeNs);
-
VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
@@ -114,34 +112,22 @@
return new AnomalyTracker(alert, mConfigKey);
}
-void DurationMetricProducer::startNewProtoOutputStreamLocked(long long startTime) {
- mProto = std::make_unique<ProtoOutputStream>();
- mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
- mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
-}
-
unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
const HashableDimensionKey& eventKey) const {
- switch (mMetric.aggregation_type()) {
+ switch (mAggregationType) {
case DurationMetric_AggregationType_SUM:
return make_unique<OringDurationTracker>(
- mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested,
+ mConfigKey, mName, eventKey, mWizard, mConditionTrackerIndex, mNested,
mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers);
case DurationMetric_AggregationType_MAX_SPARSE:
return make_unique<MaxDurationTracker>(
- mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested,
+ mConfigKey, mName, eventKey, mWizard, mConditionTrackerIndex, mNested,
mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers);
}
}
-void DurationMetricProducer::finish() {
- // TODO: write the StatsLogReport to dropbox using
- // DropboxWriter.
-}
-
void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
- VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
+ VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
flushIfNeededLocked(eventTime);
// Now for each of the on-going event, check if the condition has changed for them.
for (auto& pair : mCurrentSlicedDuration) {
@@ -151,7 +137,7 @@
void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
const uint64_t eventTime) {
- VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
+ VLOG("Metric %s onConditionChanged", mName.c_str());
mCondition = conditionMet;
flushIfNeededLocked(eventTime);
// TODO: need to populate the condition change time from the event which triggers the condition
@@ -161,14 +147,15 @@
}
}
-std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReportLocked() {
- long long endTime = time(nullptr) * NS_PER_SEC;
+void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
+ ProtoOutputStream* protoOutput) {
+ flushIfNeededLocked(dumpTimeNs);
- // Dump current bucket if it's stale.
- // If current bucket is still on-going, don't force dump current bucket.
- // In finish(), We can force dump current bucket.
- flushIfNeededLocked(endTime);
- VLOG("metric %s dump report now...", mMetric.name().c_str());
+ protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
+ 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 %s dump report now...", mName.c_str());
for (const auto& pair : mPastBuckets) {
const HashableDimensionKey& hashableKey = pair.first;
@@ -180,49 +167,46 @@
}
long long wrapperToken =
- mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
+ protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension (KeyValuePairs).
for (const auto& kv : it->second) {
- long long dimensionToken =
- mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
- mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
+ long long dimensionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
+ protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
if (kv.has_value_str()) {
- mProto->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
+ protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
} else if (kv.has_value_int()) {
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
} else if (kv.has_value_bool()) {
- mProto->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
+ protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
} else if (kv.has_value_float()) {
- mProto->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
+ protoOutput->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
}
- mProto->end(dimensionToken);
+ protoOutput->end(dimensionToken);
}
// Then fill bucket_info (DurationBucketInfo).
for (const auto& bucket : pair.second) {
- long long bucketInfoToken =
- mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
- (long long)bucket.mBucketStartNs);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
- (long long)bucket.mBucketEndNs);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_DURATION, (long long)bucket.mDuration);
- mProto->end(bucketInfoToken);
+ long long bucketInfoToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
+ (long long)bucket.mBucketStartNs);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
+ (long long)bucket.mBucketEndNs);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DURATION, (long long)bucket.mDuration);
+ protoOutput->end(bucketInfoToken);
VLOG("\t bucket [%lld - %lld] duration: %lld", (long long)bucket.mBucketStartNs,
(long long)bucket.mBucketEndNs, (long long)bucket.mDuration);
}
- mProto->end(wrapperToken);
+ protoOutput->end(wrapperToken);
}
- mProto->end(mProtoToken);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
- (long long)mCurrentBucketStartTimeNs);
- std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked();
- startNewProtoOutputStreamLocked(endTime);
+ protoOutput->end(protoToken);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
mPastBuckets.clear();
- return buffer;
+ mStartTimeNs = mCurrentBucketStartTimeNs;
}
void DurationMetricProducer::flushIfNeededLocked(const uint64_t& eventTime) {
@@ -252,11 +236,10 @@
// 1. Report the tuple count if the tuple count > soft limit
if (mCurrentSlicedDuration.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mCurrentSlicedDuration.size() + 1;
- StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
- newTupleCount);
+ StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
- ALOGE("DurationMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+ ALOGE("DurationMetric %s dropping data for dimension key %s", mName.c_str(),
newKey.c_str());
return true;
}
@@ -267,7 +250,7 @@
void DurationMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const map<string, HashableDimensionKey>& conditionKeys, bool condition,
- const LogEvent& event, bool scheduledPull) {
+ const LogEvent& event) {
flushIfNeededLocked(event.GetTimestampNs());
if (matcherIndex == mStopAllIndex) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 4bf9d1c..e7aca7f 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -47,8 +47,6 @@
virtual sp<AnomalyTracker> createAnomalyTracker(const Alert &alert) override;
- void finish() override;
-
// TODO: Implement this later.
virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version)
override{};
@@ -59,11 +57,11 @@
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKeys, bool condition,
- const LogEvent& event, bool scheduledPull) override;
+ const LogEvent& event) override;
private:
- // TODO: Pass a timestamp as a parameter in onDumpReport.
- std::unique_ptr<std::vector<uint8_t>> onDumpReportLocked() override;
+ void onDumpReportLocked(const uint64_t dumpTimeNs,
+ android::util::ProtoOutputStream* protoOutput) override;
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
@@ -77,10 +75,7 @@
// Util function to flush the old packet.
void flushIfNeededLocked(const uint64_t& eventTime);
- // Util function to init/reset the proto output stream.
- void startNewProtoOutputStreamLocked(long long timestamp);
-
- const DurationMetric mMetric;
+ const DurationMetric_AggregationType mAggregationType;
// Index of the SimpleAtomMatcher which defines the start.
const size_t mStartIndex;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 8bdc9e3..6a072b0 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.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 "EventMetricProducer.h"
@@ -55,14 +55,14 @@
const int conditionIndex,
const sp<ConditionWizard>& wizard,
const uint64_t startTimeNs)
- : MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric) {
+ : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard) {
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
metric.links().end());
mConditionSliced = true;
}
- startNewProtoOutputStreamLocked(mStartTimeNs);
+ startNewProtoOutputStreamLocked();
VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
@@ -72,45 +72,57 @@
VLOG("~EventMetricProducer() called");
}
-void EventMetricProducer::startNewProtoOutputStreamLocked(long long startTime) {
+void EventMetricProducer::startNewProtoOutputStreamLocked() {
mProto = std::make_unique<ProtoOutputStream>();
- // TODO: We need to auto-generate the field IDs for StatsLogReport, EventMetricData,
- // and StatsEvent.
- mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
- mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS);
-}
-
-void EventMetricProducer::finish() {
}
void EventMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
}
-std::unique_ptr<std::vector<uint8_t>> EventMetricProducer::onDumpReportLocked() {
- long long endTime = time(nullptr) * NS_PER_SEC;
- mProto->end(mProtoToken);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime);
+std::unique_ptr<std::vector<uint8_t>> serializeProtoLocked(ProtoOutputStream& protoOutput) {
+ size_t bufferSize = protoOutput.size();
- size_t bufferSize = mProto->size();
- VLOG("metric %s dump report now... proto size: %zu ", mMetric.name().c_str(), bufferSize);
- std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked();
+ std::unique_ptr<std::vector<uint8_t>> buffer(new std::vector<uint8_t>(bufferSize));
- startNewProtoOutputStreamLocked(endTime);
+ size_t pos = 0;
+ auto it = protoOutput.data();
+ while (it.readBuffer() != NULL) {
+ size_t toRead = it.currentToRead();
+ std::memcpy(&((*buffer)[pos]), it.readBuffer(), toRead);
+ pos += toRead;
+ it.rp()->move(toRead);
+ }
return buffer;
}
+void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
+ ProtoOutputStream* protoOutput) {
+ protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
+ 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 %s dump report now... proto size: %zu ", mName.c_str(), bufferSize);
+ std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked(*mProto);
+
+ protoOutput->write(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS,
+ reinterpret_cast<char*>(buffer.get()->data()), buffer.get()->size());
+
+ startNewProtoOutputStreamLocked();
+ mStartTimeNs = dumpTimeNs;
+}
+
void EventMetricProducer::onConditionChangedLocked(const bool conditionMet,
const uint64_t eventTime) {
- VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
+ VLOG("Metric %s onConditionChanged", mName.c_str());
mCondition = conditionMet;
}
void EventMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
- const LogEvent& event, bool scheduledPull) {
+ const LogEvent& event) {
if (!condition) {
return;
}
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index da3b3ca..d720ead 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -40,8 +40,6 @@
virtual ~EventMetricProducer();
- void finish() override;
-
// TODO: Implement this later.
virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version)
override{};
@@ -49,16 +47,16 @@
virtual void notifyAppRemoved(const string& apk, const int uid) override{};
protected:
- void startNewProtoOutputStreamLocked(long long timestamp);
+ void startNewProtoOutputStreamLocked();
private:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
- const LogEvent& event, bool scheduledPull) override;
+ const LogEvent& event) override;
- // TODO: Pass a timestamp as a parameter in onDumpReport.
- std::unique_ptr<std::vector<uint8_t>> onDumpReportLocked() override;
+ void onDumpReportLocked(const uint64_t dumpTimeNs,
+ android::util::ProtoOutputStream* protoOutput) override;
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
@@ -69,7 +67,9 @@
// Internal function to calculate the current used bytes.
size_t byteSizeLocked() const override;
- const EventMetric mMetric;
+ // Maps to a EventMetricDataWrapper. Storing atom events in ProtoOutputStream
+ // is more space efficient than storing LogEvent.
+ std::unique_ptr<android::util::ProtoOutputStream> mProto;
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 1f6bd58b..6402633 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "GaugeMetricProducer.h"
#include "guardrail/StatsdStats.h"
-#include "stats_util.h"
#include <cutils/log.h>
-#include <limits.h>
-#include <stdlib.h>
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
@@ -37,6 +34,8 @@
using std::string;
using std::unordered_map;
using std::vector;
+using std::make_shared;
+using std::shared_ptr;
namespace android {
namespace os {
@@ -61,21 +60,27 @@
// for GaugeBucketInfo
const int FIELD_ID_START_BUCKET_NANOS = 1;
const int FIELD_ID_END_BUCKET_NANOS = 2;
-const int FIELD_ID_GAUGE = 3;
+const int FIELD_ID_ATOM = 3;
GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
const int conditionIndex,
- const sp<ConditionWizard>& wizard, const int pullTagId,
- const int64_t startTimeNs)
- : MetricProducer(key, startTimeNs, conditionIndex, wizard),
- mMetric(metric),
- mPullTagId(pullTagId) {
+ const sp<ConditionWizard>& wizard, const int atomTagId,
+ const int pullTagId, const uint64_t startTimeNs,
+ shared_ptr<StatsPullerManager> statsPullerManager)
+ : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard),
+ mStatsPullerManager(statsPullerManager),
+ mPullTagId(pullTagId),
+ mAtomTagId(atomTagId) {
if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
} else {
mBucketSizeNs = kDefaultGaugemBucketSizeNs;
}
+ for (int i = 0; i < metric.gauge_fields().field_num_size(); i++) {
+ mGaugeFields.push_back(metric.gauge_fields().field_num(i));
+ }
+
// TODO: use UidMap if uid->pkg_name is required
mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
@@ -87,40 +92,39 @@
// Kicks off the puller immediately.
if (mPullTagId != -1) {
- mStatsPullerManager.RegisterReceiver(mPullTagId, this,
+ mStatsPullerManager->RegisterReceiver(mPullTagId, this,
metric.bucket().bucket_size_millis());
}
- startNewProtoOutputStreamLocked(mStartTimeNs);
-
VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
+// for testing
+GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
+ const int conditionIndex,
+ const sp<ConditionWizard>& wizard, const int pullTagId,
+ const int atomTagId, const int64_t startTimeNs)
+ : GaugeMetricProducer(key, metric, conditionIndex, wizard, pullTagId, atomTagId, startTimeNs,
+ make_shared<StatsPullerManager>()) {
+}
+
GaugeMetricProducer::~GaugeMetricProducer() {
VLOG("~GaugeMetricProducer() called");
if (mPullTagId != -1) {
- mStatsPullerManager.UnRegisterReceiver(mPullTagId, this);
+ mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
}
}
-void GaugeMetricProducer::startNewProtoOutputStreamLocked(long long startTime) {
- mProto = std::make_unique<ProtoOutputStream>();
- mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
- mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
-}
+void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
+ ProtoOutputStream* protoOutput) {
+ VLOG("gauge metric %s dump report now...", mName.c_str());
-void GaugeMetricProducer::finish() {
-}
+ flushIfNeededLocked(dumpTimeNs);
-std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReportLocked() {
- VLOG("gauge metric %s dump report now...", mMetric.name().c_str());
-
- // Dump current bucket if it's stale.
- // If current bucket is still on-going, don't force dump current bucket.
- // In finish(), We can force dump current bucket.
- flushIfNeededLocked(time(nullptr) * NS_PER_SEC);
+ protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
+ 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;
@@ -132,57 +136,67 @@
VLOG(" dimension key %s", hashableKey.c_str());
long long wrapperToken =
- mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
+ protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension (KeyValuePairs).
for (const auto& kv : it->second) {
- long long dimensionToken =
- mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
- mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
+ long long dimensionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
+ protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
if (kv.has_value_str()) {
- mProto->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
+ protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
} else if (kv.has_value_int()) {
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
} else if (kv.has_value_bool()) {
- mProto->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
+ protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
} else if (kv.has_value_float()) {
- mProto->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
+ protoOutput->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
}
- mProto->end(dimensionToken);
+ protoOutput->end(dimensionToken);
}
// Then fill bucket_info (GaugeBucketInfo).
for (const auto& bucket : pair.second) {
- long long bucketInfoToken =
- mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
- (long long)bucket.mBucketStartNs);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
- (long long)bucket.mBucketEndNs);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_GAUGE, (long long)bucket.mGauge);
- mProto->end(bucketInfoToken);
- VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
- (long long)bucket.mBucketEndNs, (long long)bucket.mGauge);
+ long long bucketInfoToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
+ (long long)bucket.mBucketStartNs);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
+ (long long)bucket.mBucketEndNs);
+ long long atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM);
+ long long eventToken = protoOutput->start(FIELD_TYPE_MESSAGE | mAtomTagId);
+ for (const auto& pair : bucket.mEvent->kv) {
+ if (pair.has_value_int()) {
+ protoOutput->write(FIELD_TYPE_INT32 | pair.key(), pair.value_int());
+ } else if (pair.has_value_long()) {
+ protoOutput->write(FIELD_TYPE_INT64 | pair.key(), pair.value_long());
+ } else if (pair.has_value_str()) {
+ protoOutput->write(FIELD_TYPE_STRING | pair.key(), pair.value_str());
+ } else if (pair.has_value_long()) {
+ protoOutput->write(FIELD_TYPE_FLOAT | pair.key(), pair.value_float());
+ } else if (pair.has_value_bool()) {
+ protoOutput->write(FIELD_TYPE_BOOL | pair.key(), pair.value_bool());
+ }
+ }
+ protoOutput->end(eventToken);
+ protoOutput->end(atomToken);
+ protoOutput->end(bucketInfoToken);
+ VLOG("\t bucket [%lld - %lld] content: %s", (long long)bucket.mBucketStartNs,
+ (long long)bucket.mBucketEndNs, bucket.mEvent->ToString().c_str());
}
- mProto->end(wrapperToken);
+ protoOutput->end(wrapperToken);
}
- mProto->end(mProtoToken);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
- (long long)mCurrentBucketStartTimeNs);
+ protoOutput->end(protoToken);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
- std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked();
-
- startNewProtoOutputStreamLocked(time(nullptr) * NS_PER_SEC);
mPastBuckets.clear();
-
- return buffer;
-
+ mStartTimeNs = mCurrentBucketStartTimeNs;
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet,
const uint64_t eventTime) {
- VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
+ VLOG("Metric %s onConditionChanged", mName.c_str());
flushIfNeededLocked(eventTime);
mCondition = conditionMet;
@@ -190,6 +204,7 @@
if (mPullTagId == -1) {
return;
}
+ // No need to pull again. Either scheduled pull or condition on true happened
if (!mCondition) {
return;
}
@@ -198,36 +213,41 @@
return;
}
vector<std::shared_ptr<LogEvent>> allData;
- if (!mStatsPullerManager.Pull(mPullTagId, &allData)) {
+ if (!mStatsPullerManager->Pull(mPullTagId, &allData)) {
ALOGE("Stats puller failed for tag: %d", mPullTagId);
return;
}
for (const auto& data : allData) {
- onMatchedLogEventLocked(0, *data, false /*scheduledPull*/);
+ onMatchedLogEventLocked(0, *data);
}
flushIfNeededLocked(eventTime);
}
void GaugeMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
- VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
+ VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
}
-int64_t GaugeMetricProducer::getGauge(const LogEvent& event) {
- status_t err = NO_ERROR;
- int64_t val = event.GetLong(mMetric.gauge_field(), &err);
- if (err == NO_ERROR) {
- return val;
+shared_ptr<EventKV> GaugeMetricProducer::getGauge(const LogEvent& event) {
+ shared_ptr<EventKV> ret = make_shared<EventKV>();
+ if (mGaugeFields.size() == 0) {
+ for (int i = 1; i <= event.size(); i++) {
+ ret->kv.push_back(event.GetKeyValueProto(i));
+ }
} else {
- VLOG("Can't find value in message.");
- return -1;
+ for (int i = 0; i < (int)mGaugeFields.size(); i++) {
+ ret->kv.push_back(event.GetKeyValueProto(mGaugeFields[i]));
+ }
}
+ return ret;
}
void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
std::lock_guard<std::mutex> lock(mMutex);
-
+ if (allData.size() == 0) {
+ return;
+ }
for (const auto& data : allData) {
- onMatchedLogEventLocked(0, *data, true /*scheduledPull*/);
+ onMatchedLogEventLocked(0, *data);
}
}
@@ -238,11 +258,10 @@
// 1. Report the tuple count if the tuple count > soft limit
if (mCurrentSlicedBucket->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mCurrentSlicedBucket->size() + 1;
- StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
- newTupleCount);
+ StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
- ALOGE("GaugeMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+ ALOGE("GaugeMetric %s dropping data for dimension key %s", mName.c_str(),
newKey.c_str());
return true;
}
@@ -254,7 +273,7 @@
void GaugeMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const map<string, HashableDimensionKey>& conditionKey, bool condition,
- const LogEvent& event, bool scheduledPull) {
+ const LogEvent& event) {
if (condition == false) {
return;
}
@@ -264,25 +283,48 @@
(long long)mCurrentBucketStartTimeNs);
return;
}
-
- // When the event happens in a new bucket, flush the old buckets.
- if (eventTimeNs >= mCurrentBucketStartTimeNs + mBucketSizeNs) {
- flushIfNeededLocked(eventTimeNs);
- }
+ flushIfNeededLocked(eventTimeNs);
// For gauge metric, we just simply use the first gauge in the given bucket.
- if (!mCurrentSlicedBucket->empty()) {
+ if (mCurrentSlicedBucket->find(eventKey) != mCurrentSlicedBucket->end()) {
return;
}
- const long gauge = getGauge(event);
- if (gauge >= 0) {
- if (hitGuardRailLocked(eventKey)) {
- return;
- }
- (*mCurrentSlicedBucket)[eventKey] = gauge;
+ shared_ptr<EventKV> gauge = getGauge(event);
+ if (hitGuardRailLocked(eventKey)) {
+ return;
}
- for (auto& tracker : mAnomalyTrackers) {
- tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, gauge);
+ (*mCurrentSlicedBucket)[eventKey] = gauge;
+ // Anomaly detection on gauge metric only works when there is one numeric
+ // field specified.
+ if (mAnomalyTrackers.size() > 0) {
+ if (gauge->kv.size() == 1) {
+ KeyValuePair pair = gauge->kv[0];
+ long gaugeVal = 0;
+ if (pair.has_value_int()) {
+ gaugeVal = (long)pair.value_int();
+ } else if (pair.has_value_long()) {
+ gaugeVal = pair.value_long();
+ }
+ for (auto& tracker : mAnomalyTrackers) {
+ tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
+ gaugeVal);
+ }
+ }
+ }
+}
+
+void GaugeMetricProducer::updateCurrentSlicedBucketForAnomaly() {
+ mCurrentSlicedBucketForAnomaly->clear();
+ status_t err = NO_ERROR;
+ for (const auto& slice : *mCurrentSlicedBucket) {
+ KeyValuePair pair = slice.second->kv[0];
+ long gaugeVal = 0;
+ if (pair.has_value_int()) {
+ gaugeVal = (long)pair.value_int();
+ } else if (pair.has_value_long()) {
+ gaugeVal = pair.value_long();
+ }
+ (*mCurrentSlicedBucketForAnomaly)[slice.first] = gaugeVal;
}
}
@@ -293,6 +335,8 @@
// the GaugeMetricProducer while holding the lock.
void GaugeMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) {
if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) {
+ VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
+ (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
return;
}
@@ -302,25 +346,28 @@
info.mBucketNum = mCurrentBucketNum;
for (const auto& slice : *mCurrentSlicedBucket) {
- info.mGauge = slice.second;
+ info.mEvent = slice.second;
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(info);
- VLOG("gauge metric %s, dump key value: %s -> %lld", mMetric.name().c_str(),
- slice.first.c_str(), (long long)slice.second);
+ VLOG("gauge metric %s, dump key value: %s -> %s", mName.c_str(),
+ slice.first.c_str(), slice.second->ToString().c_str());
}
// Reset counters
- for (auto& tracker : mAnomalyTrackers) {
- tracker->addPastBucket(mCurrentSlicedBucket, mCurrentBucketNum);
+ if (mAnomalyTrackers.size() > 0) {
+ updateCurrentSlicedBucketForAnomaly();
+ for (auto& tracker : mAnomalyTrackers) {
+ tracker->addPastBucket(mCurrentSlicedBucketForAnomaly, mCurrentBucketNum);
+ }
}
- mCurrentSlicedBucket = std::make_shared<DimToValMap>();
+ mCurrentSlicedBucket = std::make_shared<DimToEventKVMap>();
// Adjusts the bucket start time
int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
mCurrentBucketNum += numBucketsForward;
- VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
+ VLOG("metric %s: new bucket start time: %lld", mName.c_str(),
(long long)mCurrentBucketStartTimeNs);
}
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 36705b1..4a037ff 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -26,7 +26,7 @@
#include "../matchers/matcher_util.h"
#include "MetricProducer.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "stats_util.h"
+#include "../stats_util.h"
namespace android {
namespace os {
@@ -35,7 +35,7 @@
struct GaugeBucket {
int64_t mBucketStartNs;
int64_t mBucketEndNs;
- int64_t mGauge;
+ std::shared_ptr<EventKV> mEvent;
uint64_t mBucketNum;
};
@@ -49,15 +49,13 @@
// for all metrics.
GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& countMetric,
const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int pullTagId, const int64_t startTimeNs);
+ const int pullTagId, const int atomTagId, const int64_t startTimeNs);
virtual ~GaugeMetricProducer();
// Handles when the pulled data arrives.
void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;
- void finish() override;
-
// TODO: Implement this later.
virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version)
override{};
@@ -68,11 +66,17 @@
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
- const LogEvent& event, bool scheduledPull) override;
+ const LogEvent& event) override;
private:
- // TODO: Pass a timestamp as a parameter in onDumpReport.
- std::unique_ptr<std::vector<uint8_t>> onDumpReportLocked() override;
+ void onDumpReportLocked(const uint64_t dumpTimeNs,
+ android::util::ProtoOutputStream* protoOutput) override;
+
+ // for testing
+ GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
+ const int conditionIndex, const sp<ConditionWizard>& wizard,
+ const int pullTagId, const int atomTagId, const uint64_t startTimeNs,
+ std::shared_ptr<StatsPullerManager> statsPullerManager);
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
@@ -86,15 +90,10 @@
// Util function to flush the old packet.
void flushIfNeededLocked(const uint64_t& eventTime);
- // Util function to init/reset the proto output stream.
- void startNewProtoOutputStreamLocked(long long timestamp);
+ // The default bucket size for gauge metric is 1 hr.
+ static const uint64_t kDefaultGaugemBucketSizeNs = 60ULL * 60 * 1000 * 1000 * 1000;
- // The default bucket size for gauge metric is 1 second.
- static const uint64_t kDefaultGaugemBucketSizeNs = 1000 * 1000 * 1000;
-
- const GaugeMetric mMetric;
-
- StatsPullerManager mStatsPullerManager;
+ std::shared_ptr<StatsPullerManager> mStatsPullerManager;
// tagId for pulled data. -1 if this is not pulled
const int mPullTagId;
@@ -103,9 +102,21 @@
std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
// The current bucket.
- std::shared_ptr<DimToValMap> mCurrentSlicedBucket = std::make_shared<DimToValMap>();
+ std::shared_ptr<DimToEventKVMap> mCurrentSlicedBucket = std::make_shared<DimToEventKVMap>();
- int64_t getGauge(const LogEvent& event);
+ // The current bucket for anomaly detection.
+ std::shared_ptr<DimToValMap> mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
+
+ // Translate Atom based bucket to single numeric value bucket for anomaly
+ void updateCurrentSlicedBucketForAnomaly();
+
+ int mAtomTagId;
+
+ // Whitelist of fields to report. Empty means all are reported.
+ std::vector<int> mGaugeFields;
+
+ // apply a whitelist on the original input
+ std::shared_ptr<EventKV> getGauge(const LogEvent& event);
// Util function to check whether the specified dimension hits the guardrail.
bool hitGuardRailLocked(const HashableDimensionKey& newKey);
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 7e78a85..f38f3df 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -21,8 +21,7 @@
using std::map;
-void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event,
- bool scheduledPull) {
+void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
uint64_t eventTimeNs = event.GetTimestampNs();
// this is old event, maybe statsd restarted?
if (eventTimeNs < mStartTimeNs) {
@@ -60,25 +59,7 @@
condition = mCondition;
}
- onMatchedLogEventInternalLocked(matcherIndex, eventKey, conditionKeys, condition, event,
- scheduledPull);
-}
-
-std::unique_ptr<std::vector<uint8_t>> MetricProducer::serializeProtoLocked() {
- size_t bufferSize = mProto->size();
-
- std::unique_ptr<std::vector<uint8_t>> buffer(new std::vector<uint8_t>(bufferSize));
-
- size_t pos = 0;
- auto it = mProto->data();
- while (it.readBuffer() != NULL) {
- size_t toRead = it.currentToRead();
- std::memcpy(&((*buffer)[pos]), it.readBuffer(), toRead);
- pos += toRead;
- it.rp()->move(toRead);
- }
-
- return buffer;
+ onMatchedLogEventInternalLocked(matcherIndex, eventKey, conditionKeys, condition, event);
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index adeb3cd..d4a2195 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -38,9 +38,10 @@
// be a no-op.
class MetricProducer : public virtual PackageInfoListener {
public:
- MetricProducer(const ConfigKey& key, const int64_t startTimeNs, const int conditionIndex,
- const sp<ConditionWizard>& wizard)
- : mConfigKey(key),
+ MetricProducer(const std::string& name, const ConfigKey& key, const int64_t startTimeNs,
+ const int conditionIndex, const sp<ConditionWizard>& wizard)
+ : mName(name),
+ mConfigKey(key),
mStartTimeNs(startTimeNs),
mCurrentBucketStartTimeNs(startTimeNs),
mCurrentBucketNum(0),
@@ -54,9 +55,9 @@
virtual ~MetricProducer(){};
// Consume the parsed stats log entry that already matched the "what" of the metric.
- void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event, bool scheduledPull) {
+ void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
std::lock_guard<std::mutex> lock(mMutex);
- onMatchedLogEventLocked(matcherIndex, event, scheduledPull);
+ onMatchedLogEventLocked(matcherIndex, event);
}
void onConditionChanged(const bool condition, const uint64_t eventTime) {
@@ -74,16 +75,10 @@
return mConditionSliced;
};
- // This is called when the metric collecting is done, e.g., when there is a new configuration
- // coming. MetricProducer should do the clean up, and dump existing data to dropbox.
- virtual void finish() = 0;
-
- // TODO: Pass a timestamp as a parameter in onDumpReport and update all its
- // implementations.
- // onDumpReport returns the proto-serialized output and clears the previously stored contents.
- std::unique_ptr<std::vector<uint8_t>> onDumpReport() {
+ // Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp.
+ void onDumpReport(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) {
std::lock_guard<std::mutex> lock(mMutex);
- return onDumpReportLocked();
+ return onDumpReportLocked(dumpTimeNs, protoOutput);
}
// Returns the memory in bytes currently used to store this metric's data. Does not change
@@ -110,12 +105,16 @@
protected:
virtual void onConditionChangedLocked(const bool condition, const uint64_t eventTime) = 0;
virtual void onSlicedConditionMayChangeLocked(const uint64_t eventTime) = 0;
- virtual std::unique_ptr<std::vector<uint8_t>> onDumpReportLocked() = 0;
+ virtual void onDumpReportLocked(const uint64_t dumpTimeNs,
+ android::util::ProtoOutputStream* protoOutput) = 0;
virtual size_t byteSizeLocked() const = 0;
+ const std::string mName;
+
const ConfigKey mConfigKey;
- const uint64_t mStartTimeNs;
+ // The start time for the current in memory metrics data.
+ uint64_t mStartTimeNs;
uint64_t mCurrentBucketStartTimeNs;
@@ -159,21 +158,12 @@
virtual void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
- const LogEvent& event, bool scheduledPull) = 0;
+ const LogEvent& event) = 0;
// Consume the parsed stats log entry that already matched the "what" of the metric.
- void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event,
- bool scheduledPull);
+ void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
- std::unique_ptr<android::util::ProtoOutputStream> mProto;
-
- long long mProtoToken;
-
- // Read/Write mutex to make the producer thread-safe.
- // TODO(yanglu): replace with std::shared_mutex when available in libc++.
mutable std::mutex mMutex;
-
- std::unique_ptr<std::vector<uint8_t>> serializeProtoLocked();
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 9fdc6fa..a5900f4 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -27,6 +27,11 @@
#include "stats_util.h"
#include <log/logprint.h>
+
+using android::util::FIELD_COUNT_REPEATED;
+using android::util::FIELD_TYPE_MESSAGE;
+using android::util::ProtoOutputStream;
+
using std::make_unique;
using std::set;
using std::string;
@@ -37,17 +42,14 @@
namespace os {
namespace statsd {
-MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config) : mConfigKey(key) {
+const int FIELD_ID_METRICS = 1;
+
+MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec) : mConfigKey(key) {
mConfigValid =
- initStatsdConfig(key, config, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
+ initStatsdConfig(key, config, timeBaseSec, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
mTrackerToMetricMap, mTrackerToConditionMap);
- // TODO: add alert size.
- // no matter whether this config is valid, log it in the stats.
- StatsdStats::getInstance().noteConfigReceived(key, mAllMetricProducers.size(),
- mAllConditionTrackers.size(),
- mAllAtomMatchers.size(), 0, mConfigValid);
// Guardrail. Reject the config if it's too big.
if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig ||
mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig ||
@@ -55,6 +57,12 @@
ALOGE("This config is too big! Reject!");
mConfigValid = false;
}
+
+ // TODO: add alert size.
+ // no matter whether this config is valid, log it in the stats.
+ StatsdStats::getInstance().noteConfigReceived(key, mAllMetricProducers.size(),
+ mAllConditionTrackers.size(),
+ mAllAtomMatchers.size(), 0, mConfigValid);
}
MetricsManager::~MetricsManager() {
@@ -65,21 +73,17 @@
return mConfigValid;
}
-void MetricsManager::finish() {
- for (auto& metricProducer : mAllMetricProducers) {
- metricProducer->finish();
- }
-}
-
-vector<std::unique_ptr<vector<uint8_t>>> MetricsManager::onDumpReport() {
+void MetricsManager::onDumpReport(ProtoOutputStream* protoOutput) {
VLOG("=========================Metric Reports Start==========================");
+ uint64_t dumpTimeStampNs = time(nullptr) * NS_PER_SEC;
// one StatsLogReport per MetricProduer
- vector<std::unique_ptr<vector<uint8_t>>> reportList;
for (auto& metric : mAllMetricProducers) {
- reportList.push_back(metric->onDumpReport());
+ long long token =
+ protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS);
+ metric->onDumpReport(dumpTimeStampNs, protoOutput);
+ protoOutput->end(token);
}
VLOG("=========================Metric Reports End==========================");
- return reportList;
}
// Consume the stats log if it's interesting to this metric.
@@ -159,8 +163,7 @@
auto& metricList = pair->second;
for (const int metricIndex : metricList) {
// pushed metrics are never scheduled pulls
- mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event,
- false /* schedulePull */);
+ mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event);
}
}
}
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 86c4733..738adc9 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -34,7 +34,7 @@
// A MetricsManager is responsible for managing metrics from one single config source.
class MetricsManager {
public:
- MetricsManager(const ConfigKey& configKey, const StatsdConfig& config);
+ MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const long timeBaseSec);
virtual ~MetricsManager();
@@ -43,16 +43,13 @@
void onLogEvent(const LogEvent& event);
- // Called when everything should wrap up. We are about to finish (e.g., new config comes).
- void finish();
-
void onAnomalyAlarmFired(const uint64_t timestampNs,
unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& anomalySet);
void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor);
// Config source owner can call onDumpReport() to get all the metrics collected.
- virtual std::vector<std::unique_ptr<std::vector<uint8_t>>> onDumpReport();
+ virtual void onDumpReport(android::util::ProtoOutputStream* protoOutput);
// Computes the total byte size of all metrics managed by a single config source.
// Does not change the state.
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 977aa88..7efa6cd 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.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 "ValueMetricProducer.h"
@@ -73,13 +73,13 @@
const sp<ConditionWizard>& wizard, const int pullTagId,
const uint64_t startTimeNs,
shared_ptr<StatsPullerManager> statsPullerManager)
- : MetricProducer(key, startTimeNs, conditionIndex, wizard),
- mMetric(metric),
+ : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard),
+ mValueField(metric.value_field()),
mStatsPullerManager(statsPullerManager),
mPullTagId(pullTagId) {
// TODO: valuemetric for pushed events may need unlimited bucket length
if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
- mBucketSizeNs = mMetric.bucket().bucket_size_millis() * 1000 * 1000;
+ mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
} else {
mBucketSizeNs = kDefaultBucketSizeMillis * 1000 * 1000;
}
@@ -97,9 +97,6 @@
mStatsPullerManager->RegisterReceiver(mPullTagId, this,
metric.bucket().bucket_size_millis());
}
-
- startNewProtoOutputStreamLocked(mStartTimeNs);
-
VLOG("value metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
@@ -120,24 +117,17 @@
}
}
-void ValueMetricProducer::startNewProtoOutputStreamLocked(long long startTime) {
- mProto = std::make_unique<ProtoOutputStream>();
- mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
- mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
-}
-
-void ValueMetricProducer::finish() {
- // TODO: write the StatsLogReport to dropbox using
- // DropboxWriter.
-}
-
void ValueMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
- VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
+ VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
}
-std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReportLocked() {
- VLOG("metric %s dump report now...", mMetric.name().c_str());
+void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
+ ProtoOutputStream* protoOutput) {
+ VLOG("metric %s dump report now...", mName.c_str());
+ flushIfNeededLocked(dumpTimeNs);
+ protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
+ 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;
@@ -148,62 +138,63 @@
continue;
}
long long wrapperToken =
- mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
+ protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension (KeyValuePairs).
for (const auto& kv : it->second) {
- long long dimensionToken =
- mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
- mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
+ long long dimensionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
+ protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
if (kv.has_value_str()) {
- mProto->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
+ protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
} else if (kv.has_value_int()) {
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
} else if (kv.has_value_bool()) {
- mProto->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
+ protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
} else if (kv.has_value_float()) {
- mProto->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
+ protoOutput->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
}
- mProto->end(dimensionToken);
+ protoOutput->end(dimensionToken);
}
// Then fill bucket_info (ValueBucketInfo).
for (const auto& bucket : pair.second) {
- long long bucketInfoToken =
- mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
- (long long)bucket.mBucketStartNs);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
- (long long)bucket.mBucketEndNs);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE, (long long)bucket.mValue);
- mProto->end(bucketInfoToken);
+ long long bucketInfoToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
+ (long long)bucket.mBucketStartNs);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
+ (long long)bucket.mBucketEndNs);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE, (long long)bucket.mValue);
+ protoOutput->end(bucketInfoToken);
VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
(long long)bucket.mBucketEndNs, (long long)bucket.mValue);
}
- mProto->end(wrapperToken);
+ protoOutput->end(wrapperToken);
}
- mProto->end(mProtoToken);
- mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
- (long long)mCurrentBucketStartTimeNs);
+ protoOutput->end(protoToken);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
- VLOG("metric %s dump report now...", mMetric.name().c_str());
- std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked();
-
- startNewProtoOutputStreamLocked(time(nullptr) * NS_PER_SEC);
+ VLOG("metric %s dump report now...", mName.c_str());
mPastBuckets.clear();
-
- return buffer;
-
+ mStartTimeNs = mCurrentBucketStartTimeNs;
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
void ValueMetricProducer::onConditionChangedLocked(const bool condition, const uint64_t eventTime) {
mCondition = condition;
+ if (eventTime < mCurrentBucketStartTimeNs) {
+ VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTime,
+ (long long)mCurrentBucketStartTimeNs);
+ return;
+ }
+
+ flushIfNeededLocked(eventTime);
+
if (mPullTagId != -1) {
if (mCondition == true) {
- mStatsPullerManager->RegisterReceiver(mPullTagId, this,
- mMetric.bucket().bucket_size_millis());
+ mStatsPullerManager->RegisterReceiver(mPullTagId, this, mBucketSizeNs / 1000 / 1000);
} else if (mCondition == false) {
mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
}
@@ -214,9 +205,8 @@
return;
}
for (const auto& data : allData) {
- onMatchedLogEventLocked(0, *data, false);
+ onMatchedLogEventLocked(0, *data);
}
- flushIfNeededLocked(eventTime);
}
return;
}
@@ -225,19 +215,26 @@
void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
std::lock_guard<std::mutex> lock(mMutex);
- if (mCondition == true || !mMetric.has_condition()) {
+ if (mCondition == true || mConditionTrackerIndex < 0) {
if (allData.size() == 0) {
return;
}
- uint64_t eventTime = allData.at(0)->GetTimestampNs();
- // alarm is not accurate and might drift.
- if (eventTime > mCurrentBucketStartTimeNs + mBucketSizeNs * 3 / 2) {
- flushIfNeededLocked(eventTime);
- }
+ // For scheduled pulled data, the effective event time is snap to the nearest
+ // bucket boundary to make bucket finalize.
+ uint64_t realEventTime = allData.at(0)->GetTimestampNs();
+ uint64_t eventTime = mStartTimeNs + ((realEventTime - mStartTimeNs)/mBucketSizeNs) * mBucketSizeNs;
+
+ mCondition = false;
for (const auto& data : allData) {
- onMatchedLogEventLocked(0, *data, true);
+ data->setTimestampNs(eventTime-1);
+ onMatchedLogEventLocked(0, *data);
}
- flushIfNeededLocked(eventTime);
+
+ mCondition = true;
+ for (const auto& data : allData) {
+ data->setTimestampNs(eventTime);
+ onMatchedLogEventLocked(0, *data);
+ }
}
}
@@ -249,11 +246,10 @@
}
if (mCurrentSlicedBucket.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mCurrentSlicedBucket.size() + 1;
- StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
- newTupleCount);
+ StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
- ALOGE("ValueMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+ ALOGE("ValueMetric %s dropping data for dimension key %s", mName.c_str(),
newKey.c_str());
return true;
}
@@ -265,7 +261,7 @@
void ValueMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const map<string, HashableDimensionKey>& conditionKey, bool condition,
- const LogEvent& event, bool scheduledPull) {
+ const LogEvent& event) {
uint64_t eventTimeNs = event.GetTimestampNs();
if (eventTimeNs < mCurrentBucketStartTimeNs) {
VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
@@ -273,6 +269,8 @@
return;
}
+ flushIfNeededLocked(eventTimeNs);
+
if (hitGuardRailLocked(eventKey)) {
return;
}
@@ -280,42 +278,31 @@
long value = get_value(event);
- if (mPullTagId != -1) {
- if (scheduledPull) {
- // scheduled pull always sets beginning of current bucket and end
- // of next bucket
- if (interval.raw.size() > 0) {
- interval.raw.back().second = value;
- } else {
- interval.raw.push_back(make_pair(value, value));
- }
- Interval& nextInterval = mNextSlicedBucket[eventKey];
- if (nextInterval.raw.size() == 0) {
- nextInterval.raw.push_back(make_pair(value, 0));
- } else {
- nextInterval.raw.front().first = value;
- }
+ if (mPullTagId != -1) { // for pulled events
+ if (mCondition == true) {
+ interval.start = value;
+ interval.startUpdated = true;
} else {
- if (mCondition == true) {
- interval.raw.push_back(make_pair(value, 0));
+ if (interval.startUpdated) {
+ interval.sum += (value - interval.start);
+ interval.startUpdated = false;
} else {
- if (interval.raw.size() != 0) {
- interval.raw.back().second = value;
- } else {
- interval.tainted = true;
- VLOG("Data on condition true missing!");
- }
+ VLOG("No start for matching end %ld", value);
+ interval.tainted += 1;
}
}
- } else {
- flushIfNeededLocked(eventTimeNs);
- interval.raw.push_back(make_pair(value, 0));
+ } else { // for pushed events
+ interval.sum += value;
+ }
+
+ for (auto& tracker : mAnomalyTrackers) {
+ tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, interval.sum);
}
}
long ValueMetricProducer::get_value(const LogEvent& event) {
status_t err = NO_ERROR;
- long val = event.GetLong(mMetric.value_field(), &err);
+ long val = event.GetLong(mValueField, &err);
if (err == NO_ERROR) {
return val;
} else {
@@ -339,27 +326,22 @@
int tainted = 0;
for (const auto& slice : mCurrentSlicedBucket) {
- long value = 0;
- if (mPullTagId != -1) {
- for (const auto& pair : slice.second.raw) {
- value += (pair.second - pair.first);
- }
- } else {
- for (const auto& pair : slice.second.raw) {
- value += pair.first;
- }
- }
tainted += slice.second.tainted;
- info.mValue = value;
- VLOG(" %s, %ld, %d", slice.first.c_str(), value, tainted);
+ info.mValue = slice.second.sum;
// 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);
// Reset counters
- mCurrentSlicedBucket.swap(mNextSlicedBucket);
- mNextSlicedBucket.clear();
+ mCurrentSlicedBucket.clear();
int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
@@ -368,7 +350,7 @@
if (numBucketsForward > 1) {
VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
}
- VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
+ VLOG("metric %s: new bucket start time: %lld", mName.c_str(),
(long long)mCurrentBucketStartTimeNs);
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index a2efd3f..51af83d 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -19,6 +19,7 @@
#include <gtest/gtest_prod.h>
#include <utils/threads.h>
#include <list>
+#include "../anomaly/AnomalyTracker.h"
#include "../condition/ConditionTracker.h"
#include "../external/PullDataReceiver.h"
#include "../external/StatsPullerManager.h"
@@ -44,8 +45,6 @@
virtual ~ValueMetricProducer();
- void finish() override;
-
void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;
// TODO: Implement this later.
@@ -58,11 +57,11 @@
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
- const LogEvent& event, bool scheduledPull) override;
+ const LogEvent& event) override;
private:
- // TODO: Pass a timestamp as a parameter in onDumpReport.
- std::unique_ptr<std::vector<uint8_t>> onDumpReportLocked() override;
+ void onDumpReportLocked(const uint64_t dumpTimeNs,
+ android::util::ProtoOutputStream* protoOutput) override;
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
@@ -76,10 +75,7 @@
// Util function to flush the old packet.
void flushIfNeededLocked(const uint64_t& eventTime);
- // Util function to init/reset the proto output stream.
- void startNewProtoOutputStreamLocked(long long timestamp);
-
- const ValueMetric mMetric;
+ const int32_t mValueField;
std::shared_ptr<StatsPullerManager> mStatsPullerManager;
@@ -94,14 +90,19 @@
// internal state of a bucket.
typedef struct {
- std::vector<std::pair<long, long>> raw;
- bool tainted;
+ // Pulled data always come in pair of <start, end>. This holds the value
+ // for start. The diff (end - start) is added to sum.
+ long start;
+ // Whether the start data point is updated
+ bool startUpdated;
+ // If end data point comes before the start, record this pair as tainted
+ // and the value is not added to the running sum.
+ int tainted;
+ // Running sum of known pairs in this bucket
+ long sum;
} Interval;
std::unordered_map<HashableDimensionKey, Interval> mCurrentSlicedBucket;
- // If condition is true and pulling on schedule, the previous bucket value needs to be carried
- // over to the next bucket.
- std::unordered_map<HashableDimensionKey, Interval> mNextSlicedBucket;
// Save the past buckets and we can clear when the StatsLogReport is dumped.
// TODO: Add a lock to mPastBuckets.
@@ -117,6 +118,7 @@
FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
+ FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 08c9135..95c8a59 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true
+#define DEBUG false
#include "Log.h"
#include "MaxDurationTracker.h"
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index 8122744..36e25edf 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define DEBUG true
+#define DEBUG false
#include "Log.h"
#include "OringDurationTracker.h"
#include "guardrail/StatsdStats.h"
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 943becb..5d0e97e 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -188,7 +188,7 @@
return true;
}
-bool initMetrics(const ConfigKey& key, const StatsdConfig& config,
+bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec,
const unordered_map<string, int>& logTrackerMap,
const unordered_map<string, int>& conditionTrackerMap,
const vector<sp<LogMatchingTracker>>& allAtomMatchers,
@@ -202,7 +202,13 @@
config.event_metric_size() + config.value_metric_size();
allMetricProducers.reserve(allMetricsCount);
StatsPullerManager statsPullerManager;
- uint64_t startTimeNs = time(nullptr) * NS_PER_SEC;
+ // Align all buckets to same instant in MIN_BUCKET_SIZE_SEC, so that avoid alarm
+ // clock will not grow very aggressive. New metrics will be delayed up to
+ // MIN_BUCKET_SIZE_SEC before starting.
+ long currentTimeSec = time(nullptr);
+ uint64_t startTimeNs = (currentTimeSec - kMinBucketSizeSec -
+ (currentTimeSec - timeBaseSec) % kMinBucketSizeSec) *
+ NS_PER_SEC;
// Build MetricProducers for each metric defined in config.
// build CountMetricProducer
@@ -370,15 +376,11 @@
sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex);
// If it is pulled atom, it should be simple matcher with one tagId.
- int pullTagId = -1;
- for (int tagId : atomMatcher->getTagIds()) {
- if (statsPullerManager.PullerForMatcherExists(tagId)) {
- if (atomMatcher->getTagIds().size() != 1) {
- return false;
- }
- pullTagId = tagId;
- }
+ if (atomMatcher->getTagIds().size() != 1) {
+ return false;
}
+ int atomTagId = *(atomMatcher->getTagIds().begin());
+ int pullTagId = statsPullerManager.PullerForMatcherExists(atomTagId) ? atomTagId : -1;
int conditionIndex = -1;
if (metric.has_condition()) {
@@ -404,7 +406,20 @@
for (int i = 0; i < config.gauge_metric_size(); i++) {
const GaugeMetric& metric = config.gauge_metric(i);
if (!metric.has_what()) {
- ALOGW("cannot find \"what\" in ValueMetric \"%s\"", metric.name().c_str());
+ ALOGW("cannot find \"what\" in GaugeMetric \"%s\"", metric.name().c_str());
+ return false;
+ }
+
+ if ((!metric.gauge_fields().has_include_all() ||
+ (metric.gauge_fields().include_all() == false)) &&
+ metric.gauge_fields().field_num_size() == 0) {
+ ALOGW("Incorrect field filter setting in GaugeMetric %s", metric.name().c_str());
+ return false;
+ }
+ if ((metric.gauge_fields().has_include_all() &&
+ metric.gauge_fields().include_all() == true) &&
+ metric.gauge_fields().field_num_size() > 0) {
+ ALOGW("Incorrect field filter setting in GaugeMetric %s", metric.name().c_str());
return false;
}
@@ -419,15 +434,11 @@
sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex);
// If it is pulled atom, it should be simple matcher with one tagId.
- int pullTagId = -1;
- for (int tagId : atomMatcher->getTagIds()) {
- if (statsPullerManager.PullerForMatcherExists(tagId)) {
- if (atomMatcher->getTagIds().size() != 1) {
- return false;
- }
- pullTagId = tagId;
- }
+ if (atomMatcher->getTagIds().size() != 1) {
+ return false;
}
+ int atomTagId = *(atomMatcher->getTagIds().begin());
+ int pullTagId = statsPullerManager.PullerForMatcherExists(atomTagId) ? atomTagId : -1;
int conditionIndex = -1;
if (metric.has_condition()) {
@@ -444,8 +455,8 @@
}
}
- sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(key, metric, conditionIndex,
- wizard, pullTagId, startTimeNs);
+ sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
+ key, metric, conditionIndex, wizard, pullTagId, atomTagId, startTimeNs);
allMetricProducers.push_back(gaugeProducer);
}
return true;
@@ -478,7 +489,7 @@
return true;
}
-bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, set<int>& allTagIds,
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec, set<int>& allTagIds,
vector<sp<LogMatchingTracker>>& allAtomMatchers,
vector<sp<ConditionTracker>>& allConditionTrackers,
vector<sp<MetricProducer>>& allMetricProducers,
@@ -502,7 +513,7 @@
return false;
}
- if (!initMetrics(key, config, logTrackerMap, conditionTrackerMap, allAtomMatchers,
+ if (!initMetrics(key, config, timeBaseSec, logTrackerMap, conditionTrackerMap, allAtomMatchers,
allConditionTrackers, allMetricProducers, conditionToMetricMap,
trackerToMetricMap, metricProducerMap)) {
ALOGE("initMetricProducers failed");
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 8e9c8e3..3337332 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -68,6 +68,7 @@
// input:
// [key]: the config key that this config belongs to
// [config]: the input config
+// [timeBaseSec]: start time base for all metrics
// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
// [conditionTrackerMap]: condition name to index mapping
// output:
@@ -76,7 +77,7 @@
// the list of MetricProducer index
// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
bool initMetrics(
- const ConfigKey& key, const StatsdConfig& config,
+ const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec,
const std::unordered_map<std::string, int>& logTrackerMap,
const std::unordered_map<std::string, int>& conditionTrackerMap,
const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
@@ -88,7 +89,7 @@
// Initialize MetricsManager from StatsdConfig.
// Parameters are the members of MetricsManager. See MetricsManager for declaration.
-bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, std::set<int>& allTagIds,
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec, std::set<int>& allTagIds,
std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
std::vector<sp<ConditionTracker>>& allConditionTrackers,
std::vector<sp<MetricProducer>>& allMetricProducers,
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 6e7a613..3018be1 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -31,10 +31,9 @@
namespace os {
namespace statsd {
-UidMap::UidMap() : mBytesUsed(0) {
-}
-UidMap::~UidMap() {
-}
+UidMap::UidMap() : mBytesUsed(0) {}
+
+UidMap::~UidMap() {}
bool UidMap::hasApp(int uid, const string& packageName) const {
lock_guard<mutex> lock(mMutex);
@@ -48,6 +47,27 @@
return false;
}
+string UidMap::normalizeAppName(const string& appName) const {
+ string normalizedName = appName;
+ std::transform(normalizedName.begin(), normalizedName.end(), normalizedName.begin(), ::tolower);
+ return normalizedName;
+}
+
+std::set<string> UidMap::getAppNamesFromUid(const int32_t& uid, bool returnNormalized) const {
+ lock_guard<mutex> lock(mMutex);
+ return getAppNamesFromUidLocked(uid,returnNormalized);
+}
+
+std::set<string> UidMap::getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const {
+ std::set<string> names;
+ auto range = mMap.equal_range(uid);
+ for (auto it = range.first; it != range.second; ++it) {
+ names.insert(returnNormalized ?
+ normalizeAppName(it->second.packageName) : it->second.packageName);
+ }
+ return names;
+}
+
int64_t UidMap::getAppVersion(int uid, const string& packageName) const {
lock_guard<mutex> lock(mMutex);
@@ -97,17 +117,17 @@
const int64_t& versionCode) {
lock_guard<mutex> lock(mMutex);
- string app = string(String8(app_16).string());
+ string appName = string(String8(app_16).string());
// Notify any interested producers that this app has updated
for (auto it : mSubscribers) {
- it->notifyAppUpgrade(app, uid, versionCode);
+ it->notifyAppUpgrade(appName, uid, versionCode);
}
auto log = mOutput.add_changes();
log->set_deletion(false);
log->set_timestamp_nanos(timestamp);
- log->set_app(app);
+ log->set_app(appName);
log->set_uid(uid);
log->set_version(versionCode);
mBytesUsed += log->ByteSize();
@@ -117,16 +137,15 @@
auto range = mMap.equal_range(int(uid));
for (auto it = range.first; it != range.second; ++it) {
- if (it->second.packageName == app) {
+ // If we find the exact same app name and uid, update the app version directly.
+ if (it->second.packageName == appName) {
it->second.versionCode = versionCode;
return;
}
- VLOG("updateApp failed to find the app %s with uid %i to update", app.c_str(), uid);
- return;
}
// Otherwise, we need to add an app at this uid.
- mMap.insert(make_pair(uid, AppData(app, versionCode)));
+ mMap.insert(make_pair(uid, AppData(appName, versionCode)));
}
void UidMap::ensureBytesUsedBelowLimit() {
@@ -154,6 +173,7 @@
void UidMap::removeApp(const String16& app_16, const int32_t& uid) {
removeApp(time(nullptr) * NS_PER_SEC, app_16, uid);
}
+
void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid) {
lock_guard<mutex> lock(mMutex);
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 9e1ad69..487fdf9 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef STATSD_UIDMAP_H
-#define STATSD_UIDMAP_H
+#pragma once
#include "config/ConfigKey.h"
#include "config/ConfigListener.h"
@@ -66,6 +65,9 @@
// Returns true if the given uid contains the specified app (eg. com.google.android.gms).
bool hasApp(int uid, const string& packageName) const;
+ // Returns the app names from uid.
+ std::set<string> getAppNamesFromUid(const int32_t& uid, bool returnNormalized) const;
+
int64_t getAppVersion(int uid, const string& packageName) const;
// Helper for debugging contents of this uid map. Can be triggered with:
@@ -103,6 +105,9 @@
size_t getBytesUsed();
private:
+ std::set<string> getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const;
+ string normalizeAppName(const string& appName) const;
+
void updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
const vector<int64_t>& versionCode, const vector<String16>& packageName);
@@ -160,4 +165,3 @@
} // namespace os
} // namespace android
-#endif // STATSD_UIDMAP_H
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index f8b91fe..3c85c57 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -22,16 +22,17 @@
option java_package = "com.android.os";
option java_outer_classname = "StatsLog";
-import "frameworks/base/cmds/statsd/src/atoms_copy.proto";
+import "frameworks/base/cmds/statsd/src/atoms.proto";
message KeyValuePair {
optional int32 key = 1;
oneof value {
string value_str = 2;
- int64 value_int = 3;
- bool value_bool = 4;
- float value_float = 5;
+ int32 value_int = 3;
+ int64 value_long = 4;
+ bool value_bool = 5;
+ float value_float = 6;
}
}
@@ -88,7 +89,7 @@
optional int64 end_bucket_nanos = 2;
- optional int64 gauge = 3;
+ optional Atom atom = 3;
}
message GaugeMetricData {
@@ -195,7 +196,7 @@
message AlertStats {
optional string name = 1;
- optional int32 declared_times = 2;
+ optional int32 alerted_times = 2;
}
message ConfigStats {
diff --git a/cmds/statsd/src/stats_util.cpp b/cmds/statsd/src/stats_util.cpp
index fcce2ff..7527a64 100644
--- a/cmds/statsd/src/stats_util.cpp
+++ b/cmds/statsd/src/stats_util.cpp
@@ -15,119 +15,11 @@
*/
#include "stats_util.h"
-#include <log/log_event_list.h>
namespace android {
namespace os {
namespace statsd {
-static inline uint32_t get4LE(const char* src) {
- return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-}
-
-int getTagId(log_msg msg) {
- return get4LE(msg.msg());
-}
-
-EventMetricData parse(log_msg msg) {
- // dump all statsd logs to dropbox for now.
- // TODO: Add filtering, aggregation, etc.
- EventMetricData eventMetricData;
-
- // set tag.
- int tag = getTagId(msg);
- // TODO: Replace the following line when we can serialize on the fly.
- // eventMetricData.set_tag(tag);
-
- // set timestamp of the event.
- eventMetricData.set_timestamp_nanos(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec);
-
- // start iterating k,v pairs.
- android_log_context context =
- create_android_log_parser(const_cast<log_msg*>(&msg)->msg() + sizeof(uint32_t),
- const_cast<log_msg*>(&msg)->len() - sizeof(uint32_t));
- android_log_list_element elem;
-
- if (context) {
- memset(&elem, 0, sizeof(elem));
- size_t index = 0;
- int32_t key = -1;
-
- do {
- elem = android_log_read_next(context);
- switch ((int)elem.type) {
- case EVENT_TYPE_INT:
- if (index % 2 == 0) {
- key = elem.data.int32;
- } else {
- // TODO: Fix the following lines when we can serialize on the fly.
- /*
- int32_t val = elem.data.int32;
- KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
- keyValuePair->set_key(key);
- keyValuePair->set_value_int(val);
- */
- }
- index++;
- break;
- case EVENT_TYPE_FLOAT:
- if (index % 2 == 1) {
- // TODO: Fix the following lines when we can serialize on the fly.
- /*
- float val = elem.data.float32;
- KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
- keyValuePair->set_key(key);
- keyValuePair->set_value_float(val);
- */
- }
- index++;
- break;
- case EVENT_TYPE_STRING:
- if (index % 2 == 1) {
- // TODO: Fix the following lines when we can serialize on the fly.
- /*
- char* val = elem.data.string;
- KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
- keyValuePair->set_key(key);
- keyValuePair->set_value_str(val);
- */
- }
- index++;
- break;
- case EVENT_TYPE_LONG:
- if (index % 2 == 1) {
- // TODO: Fix the following lines when we can serialize on the fly.
- /*
- int64_t val = elem.data.int64;
- KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
- keyValuePair->set_key(key);
- keyValuePair->set_value_int(val);
- */
- }
- index++;
- break;
- case EVENT_TYPE_LIST:
- break;
- case EVENT_TYPE_LIST_STOP:
- break;
- case EVENT_TYPE_UNKNOWN:
- break;
- default:
- elem.complete = true;
- break;
- }
-
- if (elem.complete) {
- break;
- }
- } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
-
- android_log_destroy(&context);
- }
-
- return eventMetricData;
-}
-
// There is no existing hash function for the dimension key ("repeated KeyValuePair").
// Temporarily use a string concatenation as the hashable key.
// TODO: Find a better hash function for std::vector<KeyValuePair>.
@@ -143,6 +35,9 @@
case KeyValuePair::ValueCase::kValueInt:
flattened += std::to_string(pair.value_int());
break;
+ case KeyValuePair::ValueCase::kValueLong:
+ flattened += std::to_string(pair.value_long());
+ break;
case KeyValuePair::ValueCase::kValueBool:
flattened += std::to_string(pair.value_bool());
break;
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index b7d8f97..8fd1ea8c 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -16,11 +16,9 @@
#pragma once
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include <sstream>
#include "logd/LogReader.h"
-#include "storage/DropboxWriter.h"
-
-#include <log/logprint.h>
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include <unordered_map>
@@ -29,7 +27,9 @@
namespace statsd {
#define DEFAULT_DIMENSION_KEY ""
-#define MATCHER_NOT_FOUND -2
+
+// Minimum bucket size in seconds
+const long kMinBucketSizeSec = 5 * 60;
typedef std::string HashableDimensionKey;
@@ -37,6 +37,37 @@
typedef std::unordered_map<HashableDimensionKey, int64_t> DimToValMap;
+/*
+ * In memory rep for LogEvent. Uses much less memory than LogEvent
+ */
+typedef struct EventKV {
+ std::vector<KeyValuePair> kv;
+ string ToString() const {
+ std::ostringstream result;
+ result << "{ ";
+ const size_t N = kv.size();
+ for (size_t i = 0; i < N; i++) {
+ result << " ";
+ result << (i + 1);
+ result << "->";
+ const auto& pair = kv[i];
+ if (pair.has_value_int()) {
+ result << pair.value_int();
+ } else if (pair.has_value_long()) {
+ result << pair.value_long();
+ } else if (pair.has_value_float()) {
+ result << pair.value_float();
+ } else if (pair.has_value_str()) {
+ result << pair.value_str().c_str();
+ }
+ }
+ result << " }";
+ return result.str();
+ }
+} EventKV;
+
+typedef std::unordered_map<HashableDimensionKey, std::shared_ptr<EventKV>> DimToEventKVMap;
+
EventMetricData parse(log_msg msg);
int getTagId(log_msg msg);
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index c9654af..a30b5f8 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -120,6 +120,11 @@
repeated KeyMatcher key_in_condition = 3;
}
+message FieldFilter {
+ optional bool include_all = 1;
+ repeated int32 field_num = 2;
+}
+
message EventMetric {
optional string name = 1;
@@ -170,7 +175,7 @@
optional string what = 2;
- optional int32 gauge_field = 3;
+ optional FieldFilter gauge_fields = 3;
optional string condition = 4;
diff --git a/cmds/statsd/src/storage/DropboxReader.cpp b/cmds/statsd/src/storage/DropboxReader.cpp
deleted file mode 100644
index c561959..0000000
--- a/cmds/statsd/src/storage/DropboxReader.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <android-base/file.h>
-#include <android/os/DropBoxManager.h>
-#include <androidfw/ZipUtils.h>
-
-#include "storage/DropboxReader.h"
-
-using android::base::unique_fd;
-using android::binder::Status;
-using android::os::DropBoxManager;
-using android::sp;
-using android::String16;
-using android::ZipUtils;
-using std::vector;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-status_t DropboxReader::readStatsLogs(FILE* out, const string& tag, long msec) {
- sp<DropBoxManager> dropbox = new DropBoxManager();
- StatsLogReport logReport;
-
- long timestamp = msec;
- // instead of while(true), put a hard limit 1000. Dropbox won't have more than 1000 files.
- for (int i = 0; i < 1000; i++) {
- DropBoxManager::Entry entry;
- Status status = dropbox->getNextEntry(String16(tag.c_str()), timestamp, &entry);
- if (!status.isOk()) {
- ALOGD("No more entries, or failed to read. We can't tell unfortunately.");
- return android::OK;
- }
-
- const unique_fd& fd = entry.getFd();
-
- // use this timestamp for next query.
- timestamp = entry.getTimestamp();
-
- if (entry.getFlags() & DropBoxManager::IS_GZIPPED) {
- if (!parseFromGzipFile(fd, logReport)) {
- // Failed to parse from the file. Continue to fetch the next entry.
- continue;
- }
- } else {
- if (!parseFromFile(fd, logReport)) {
- // Failed to parse from the file. Continue to fetch the next entry.
- continue;
- }
- }
-
- printLog(out, logReport);
- }
- return android::OK;
-}
-
-bool DropboxReader::parseFromGzipFile(const unique_fd& fd, StatsLogReport& logReport) {
- FILE* file = fdopen(fd, "r");
- bool result = false;
- bool scanResult;
- int method;
- long compressedLen;
- long uncompressedLen;
- unsigned long crc32;
- scanResult = ZipUtils::examineGzip(file, &method, &uncompressedLen, &compressedLen, &crc32);
- if (scanResult && method == kCompressDeflated) {
- vector<uint8_t> buf(uncompressedLen);
- if (ZipUtils::inflateToBuffer(file, &buf[0], uncompressedLen, compressedLen)) {
- if (logReport.ParseFromArray(&buf[0], uncompressedLen)) {
- result = true;
- }
- }
- } else {
- ALOGE("This isn't a valid deflated gzip file");
- }
- fclose(file);
- return result;
-}
-
-// parse a non zipped file.
-bool DropboxReader::parseFromFile(const unique_fd& fd, StatsLogReport& logReport) {
- string content;
- if (!android::base::ReadFdToString(fd, &content)) {
- ALOGE("Failed to read file");
- return false;
- }
- if (!logReport.ParseFromString(content)) {
- ALOGE("failed to parse log entry from data");
- return false;
- }
- return true;
-}
-
-void DropboxReader::printLog(FILE* out, const StatsLogReport& logReport) {
- fprintf(out, "start_time_ns=%lld, end_time_ns=%lld, ", logReport.start_report_nanos(),
- logReport.end_report_nanos());
- for (int i = 0; i < logReport.event_metrics().data_size(); i++) {
- EventMetricData eventMetricData = logReport.event_metrics().data(i);
- // TODO: Pretty-print the proto.
- // fprintf(out, "EventMetricData=%s", eventMetricData.SerializeAsString().c_str());
- }
- fprintf(out, "\n");
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/storage/DropboxReader.h b/cmds/statsd/src/storage/DropboxReader.h
deleted file mode 100644
index a5a28d9..0000000
--- a/cmds/statsd/src/storage/DropboxReader.h
+++ /dev/null
@@ -1,52 +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.
- */
-
-#ifndef DROPBOX_READER_H
-#define DROPBOX_READER_H
-
-#include <frameworks/base/cmds/statsd/src/stats_log.pb.h>
-
-#include <stdint.h>
-#include <stdio.h>
-
-using android::base::unique_fd;
-using android::status_t;
-using std::string;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class DropboxReader {
-public:
- // msec is the start timestamp.
- static status_t readStatsLogs(FILE* out, const string& tag, long msec);
-
-private:
- static bool parseFromFile(const unique_fd& fd, StatsLogReport& logReport);
- static bool parseFromGzipFile(const unique_fd& fd, StatsLogReport& logReport);
- static void printLog(FILE* out, const StatsLogReport& logReport);
- enum {
- kCompressStored = 0, // no compression
- kCompressDeflated = 8, // standard deflate
- };
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // DROPBOX_READER_H
diff --git a/cmds/statsd/src/storage/DropboxWriter.cpp b/cmds/statsd/src/storage/DropboxWriter.cpp
deleted file mode 100644
index e59bdbd..0000000
--- a/cmds/statsd/src/storage/DropboxWriter.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android/os/DropBoxManager.h>
-
-#include "storage/DropboxWriter.h"
-
-using android::binder::Status;
-using android::os::DropBoxManager;
-using android::sp;
-using android::String16;
-using std::vector;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-DropboxWriter::DropboxWriter(const string& tag) : mTag(tag), mLogReport(), mBufferSize(0) {
-}
-
-void DropboxWriter::addEventMetricData(const EventMetricData& eventMetricData) {
- flushIfNecessary(eventMetricData);
- EventMetricData* newEntry = mLogReport.mutable_event_metrics()->add_data();
- newEntry->CopyFrom(eventMetricData);
- mBufferSize += eventMetricData.ByteSize();
-}
-
-void DropboxWriter::flushIfNecessary(const EventMetricData& eventMetricData) {
- if (eventMetricData.ByteSize() + mBufferSize > kMaxSerializedBytes) {
- flush();
- }
-}
-
-void DropboxWriter::flush() {
- // now we get an exact byte size of the output
- const int numBytes = mLogReport.ByteSize();
- vector<uint8_t> buffer(numBytes);
- sp<DropBoxManager> dropbox = new DropBoxManager();
- mLogReport.SerializeToArray(&buffer[0], numBytes);
- Status status = dropbox->addData(String16(mTag.c_str()), &buffer[0], numBytes, 0 /* no flag */);
- if (!status.isOk()) {
- ALOGE("failed to write to dropbox");
- // TODO: What to do if flush fails??
- }
- mLogReport.Clear();
- mBufferSize = 0;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/storage/DropboxWriter.h b/cmds/statsd/src/storage/DropboxWriter.h
deleted file mode 100644
index d72f103..0000000
--- a/cmds/statsd/src/storage/DropboxWriter.h
+++ /dev/null
@@ -1,72 +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.
- */
-
-#ifndef DROPBOX_WRITER_H
-#define DROPBOX_WRITER_H
-
-#include <utils/RefBase.h>
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
-
-using std::string;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class DropboxWriter : public virtual RefBase {
-public:
- /* tag will be part of the file name, and used as the key to build the file index inside
- DropBoxManagerService.
- */
- DropboxWriter(const string& tag);
-
- void addEventMetricData(const EventMetricData& eventMetricData);
-
- /* Request a flush to dropbox. */
- void flush();
-
-private:
- /* Max *serialized* size of the logs kept in memory before flushing to dropbox.
- Proto lite does not implement the SpaceUsed() function which gives the in memory byte size.
- So we cap memory usage by limiting the serialized size. Note that protobuf's in memory size
- is higher than its serialized size. DropboxManager will compress the file when the data is
- larger than 4KB. So the final file size is less than this number.
- */
- static const size_t kMaxSerializedBytes = 16 * 1024;
-
- const string mTag;
-
- /* Data that was captured for a single metric over a given interval of time. */
- StatsLogReport mLogReport;
-
- /* Current *serialized* size of the logs kept in memory.
- To save computation, we will not calculate the size of the StatsLogReport every time when a
- new entry is added, which would recursively call ByteSize() on every log entry. Instead, we
- keep the sum of all individual stats log entry sizes. The size of a proto is approximately
- the sum of the size of all member protos.
- */
- size_t mBufferSize = 0;
-
- /* Check if the buffer size exceeds the max buffer size when the new entry is added, and flush
- the logs to dropbox if true. */
- void flushIfNecessary(const EventMetricData& eventMetricData);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // DROPBOX_WRITER_H
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 62f06a7..9919abf 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -23,14 +23,15 @@
#include <android-base/file.h>
#include <dirent.h>
-using android::util::FIELD_COUNT_REPEATED;
-using android::util::FIELD_TYPE_MESSAGE;
-
namespace android {
namespace os {
namespace statsd {
-#define STATS_SERVICE_DIR "/data/system/stats-service"
+using android::util::FIELD_COUNT_REPEATED;
+using android::util::FIELD_TYPE_MESSAGE;
+using std::map;
+
+#define STATS_SERVICE_DIR "/data/misc/stats-service"
// for ConfigMetricsReportList
const int FIELD_ID_REPORTS = 2;
@@ -170,7 +171,7 @@
}
}
-void StorageManager::readConfigFromDisk(unordered_map<ConfigKey, StatsdConfig>& configsMap) {
+void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap) {
unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
if (dir == NULL) {
VLOG("no default config on disk");
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index 4c9abe5..caf5b8b 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -66,7 +66,7 @@
/**
* Call to load the saved configs from disk.
*/
- static void readConfigFromDisk(unordered_map<ConfigKey, StatsdConfig>& configsMap);
+ static void readConfigFromDisk(std::map<ConfigKey, StatsdConfig>& configsMap);
};
} // namespace statsd
diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp
index 696fddf..b1924ea 100644
--- a/cmds/statsd/tests/ConfigManager_test.cpp
+++ b/cmds/statsd/tests/ConfigManager_test.cpp
@@ -63,7 +63,7 @@
TEST(ConfigManagerTest, TestFakeConfig) {
auto metricsManager =
- std::make_unique<MetricsManager>(ConfigKey(0, "test"), build_fake_config());
+ std::make_unique<MetricsManager>(ConfigKey(0, "test"), build_fake_config(), 1000);
EXPECT_TRUE(metricsManager->isConfigValid());
}
@@ -88,12 +88,6 @@
{
InSequence s;
- // The built-in fake one.
- // TODO: Remove this when we get rid of the fake one, and make this
- // test loading one from disk somewhere.
- EXPECT_CALL(*(listener.get()),
- OnConfigUpdated(ConfigKeyEq(1000, "fake"), StatsdConfigEq("CONFIG_12345")))
- .RetiresOnSaturation();
manager->Startup();
// Add another one
@@ -147,7 +141,7 @@
StatsdConfig config;
- EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, _)).Times(6);
+ EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, _)).Times(5);
EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, "xxx")));
EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, "yyy")));
EXPECT_CALL(*(listener.get()), OnConfigRemoved(ConfigKeyEq(2, "zzz")));
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index c6a5310..3c8ccab 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -42,6 +42,8 @@
const ConfigKey kConfigKey(0, "test");
+const long timeBaseSec = 1000;
+
StatsdConfig buildGoodConfig() {
StatsdConfig config;
config.set_name("12345");
@@ -275,7 +277,7 @@
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- EXPECT_TRUE(initStatsdConfig(kConfigKey, config, allTagIds, allAtomMatchers,
+ EXPECT_TRUE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
EXPECT_EQ(1u, allMetricProducers.size());
@@ -293,7 +295,7 @@
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
}
@@ -309,7 +311,7 @@
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
}
@@ -324,7 +326,7 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
}
@@ -339,7 +341,7 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
}
@@ -355,7 +357,7 @@
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
}
@@ -371,7 +373,7 @@
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
}
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index ff04d95..a5c8875 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -32,6 +32,8 @@
namespace os {
namespace statsd {
+using android::util::ProtoOutputStream;
+
#ifdef __ANDROID__
/**
@@ -39,11 +41,11 @@
*/
class MockMetricsManager : public MetricsManager {
public:
- MockMetricsManager() : MetricsManager(ConfigKey(1, "key"), StatsdConfig()) {
+ MockMetricsManager() : MetricsManager(ConfigKey(1, "key"), StatsdConfig(), 1000) {
}
MOCK_METHOD0(byteSize, size_t());
- MOCK_METHOD0(onDumpReport, std::vector<std::unique_ptr<std::vector<uint8_t>>>());
+ MOCK_METHOD1(onDumpReport, void(ProtoOutputStream* output));
};
TEST(StatsLogProcessorTest, TestRateLimitByteSize) {
@@ -101,7 +103,7 @@
.Times(1)
.WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2)));
- EXPECT_CALL(mockMetricsManager, onDumpReport()).Times(1);
+ EXPECT_CALL(mockMetricsManager, onDumpReport(_)).Times(1);
// Expect to call the onDumpReport and skip the broadcast.
p.flushIfNecessary(1, key, mockMetricsManager);
@@ -114,4 +116,4 @@
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index 5b2cedd..3fa96d3 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -74,6 +74,14 @@
EXPECT_TRUE(m.hasApp(1000, kApp1));
EXPECT_TRUE(m.hasApp(1000, kApp2));
EXPECT_FALSE(m.hasApp(1000, "not.app"));
+
+ std::set<string> name_set = m.getAppNamesFromUid(1000u, true /* returnNormalized */);
+ EXPECT_EQ(name_set.size(), 2u);
+ EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
+ EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
+
+ name_set = m.getAppNamesFromUid(12345, true /* returnNormalized */);
+ EXPECT_TRUE(name_set.empty());
}
TEST(UidMapTest, TestAddAndRemove) {
@@ -90,12 +98,59 @@
versions.push_back(5);
m.updateMap(uids, versions, apps);
+ std::set<string> name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
+ EXPECT_EQ(name_set.size(), 2u);
+ EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
+ EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
+
+ // Update the app1 version.
m.updateApp(String16(kApp1.c_str()), 1000, 40);
EXPECT_EQ(40, m.getAppVersion(1000, kApp1));
+ name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
+ EXPECT_EQ(name_set.size(), 2u);
+ EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
+ EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
+
m.removeApp(String16(kApp1.c_str()), 1000);
EXPECT_FALSE(m.hasApp(1000, kApp1));
EXPECT_TRUE(m.hasApp(1000, kApp2));
+ name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
+ EXPECT_EQ(name_set.size(), 1u);
+ EXPECT_TRUE(name_set.find(kApp1) == name_set.end());
+ EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
+
+ // Remove app2.
+ m.removeApp(String16(kApp2.c_str()), 1000);
+ EXPECT_FALSE(m.hasApp(1000, kApp1));
+ EXPECT_FALSE(m.hasApp(1000, kApp2));
+ name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
+ EXPECT_TRUE(name_set.empty());
+}
+
+TEST(UidMapTest, TestUpdateApp) {
+ UidMap m;
+ m.updateMap({1000, 1000}, {4, 5}, {String16(kApp1.c_str()), String16(kApp2.c_str())});
+ std::set<string> name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
+ EXPECT_EQ(name_set.size(), 2u);
+ EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
+ EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
+
+ // Adds a new name for uid 1000.
+ m.updateApp(String16("NeW_aPP1_NAmE"), 1000, 40);
+ name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
+ EXPECT_EQ(name_set.size(), 3u);
+ EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
+ EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
+ EXPECT_TRUE(name_set.find("NeW_aPP1_NAmE") == name_set.end());
+ EXPECT_TRUE(name_set.find("new_app1_name") != name_set.end());
+
+ // This name is also reused by another uid 2000.
+ m.updateApp(String16("NeW_aPP1_NAmE"), 2000, 1);
+ name_set = m.getAppNamesFromUid(2000, true /* returnNormalized */);
+ EXPECT_EQ(name_set.size(), 1u);
+ EXPECT_TRUE(name_set.find("NeW_aPP1_NAmE") == name_set.end());
+ EXPECT_TRUE(name_set.find("new_app1_name") != name_set.end());
}
TEST(UidMapTest, TestClearingOutput) {
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index 9fed4f8..7658044 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -163,9 +163,9 @@
EXPECT_EQ(2, configReport.alert_stats_size());
bool alert1first = !configReport.alert_stats(0).name().compare("alert1");
EXPECT_EQ("alert1", configReport.alert_stats(alert1first ? 0 : 1).name());
- EXPECT_EQ(2, configReport.alert_stats(alert1first ? 0 : 1).declared_times());
+ EXPECT_EQ(2, configReport.alert_stats(alert1first ? 0 : 1).alerted_times());
EXPECT_EQ("alert2", configReport.alert_stats(alert1first ? 1 : 0).name());
- EXPECT_EQ(1, configReport.alert_stats(alert1first ? 1 : 0).declared_times());
+ EXPECT_EQ(1, configReport.alert_stats(alert1first ? 1 : 0).alerted_times());
EXPECT_EQ(1, configReport.condition_stats_size());
EXPECT_EQ("condition1", configReport.condition_stats(0).name());
@@ -201,7 +201,7 @@
EXPECT_EQ(1, configReport2.alert_stats_size());
EXPECT_EQ("alert99", configReport2.alert_stats(0).name());
- EXPECT_EQ(1, configReport2.alert_stats(0).declared_times());
+ EXPECT_EQ(1, configReport2.alert_stats(0).alerted_times());
}
TEST(StatsdStatsTest, TestAtomLog) {
@@ -214,7 +214,7 @@
stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 2);
stats.noteAtomLogged(android::util::DROPBOX_ERROR_CHANGED, now + 3);
// pulled event, should ignore
- stats.noteAtomLogged(android::util::WIFI_BYTES_TRANSFERRED, now + 4);
+ stats.noteAtomLogged(android::util::WIFI_BYTES_TRANSFER, now + 4);
vector<uint8_t> output;
stats.dumpStats(&output, false);
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index d973ba1..51eabd5 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -54,8 +54,8 @@
bucketStartTimeNs);
// 2 events in bucket 1.
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// Flushes at event #2.
countProducer.flushIfNeededLocked(bucketStartTimeNs + 2);
@@ -74,7 +74,7 @@
// 1 matched event happens in bucket 2.
LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 2);
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3, false);
+ 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) !=
@@ -111,12 +111,12 @@
CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
countProducer.onConditionChanged(true, bucketStartTimeNs);
- countProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
+ countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2);
// Upon this match event, the matched event1 is flushed.
- countProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
+ countProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
@@ -166,11 +166,11 @@
CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
bucketStartTimeNs);
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
+ 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) !=
@@ -217,29 +217,29 @@
LogEvent event7(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 3 + NS_PER_SEC);
// Two events in bucket #0.
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2, false);
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL);
// One event in bucket #2. No alarm as bucket #0 is trashed out.
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3, false);
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL);
// Two events in bucket #3.
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4, false);
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5, false);
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6, false);
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5);
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6);
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->getLastAlarmTimestampNs(), (long long)event5.GetTimestampNs());
- countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7, false);
+ countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7);
EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event7.GetTimestampNs());
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 3158c27..58a4ac6 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -56,8 +56,8 @@
kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */,
3 /* stop_all index */, false /*nesting*/, wizard, {}, bucketStartTimeNs);
- durationProducer.onMatchedLogEvent(1 /* start index*/, event1, false /* scheduledPull */);
- durationProducer.onMatchedLogEvent(2 /* stop index*/, event2, false /* scheduledPull */);
+ durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+ 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) !=
@@ -94,14 +94,14 @@
EXPECT_FALSE(durationProducer.mCondition);
EXPECT_FALSE(durationProducer.isConditionSliced());
- durationProducer.onMatchedLogEvent(1 /* start index*/, event1, false /* scheduledPull */);
- durationProducer.onMatchedLogEvent(2 /* stop index*/, event2, false /* scheduledPull */);
+ durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+ durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
- durationProducer.onMatchedLogEvent(1 /* start index*/, event3, false /* scheduledPull */);
+ durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
- durationProducer.onMatchedLogEvent(2 /* stop index*/, event4, false /* scheduledPull */);
+ 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) !=
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index f3302fd..e4fc67f 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -50,8 +50,8 @@
EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
bucketStartTimeNs);
- eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
- eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
+ eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
+ eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
// TODO: get the report and check the content after the ProtoOutputStream change is done.
// eventProducer.onDumpReport();
@@ -74,11 +74,11 @@
EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs);
- eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
+ eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
eventProducer.onConditionChanged(false /*condition*/, bucketStartTimeNs + 2);
- eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
+ eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
// TODO: get the report and check the content after the ProtoOutputStream change is done.
// eventProducer.onDumpReport();
@@ -115,8 +115,8 @@
EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
- eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
- eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
+ eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
+ eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
// TODO: get the report and check the content after the ProtoOutputStream change is done.
// eventProducer.onDumpReport();
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 59475d2..68b7dcb 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -26,6 +26,7 @@
using std::set;
using std::unordered_map;
using std::vector;
+using std::make_shared;
#ifdef __ANDROID__
@@ -34,120 +35,142 @@
namespace statsd {
const ConfigKey kConfigKey(0, "test");
+const int tagId = 1;
+const string metricName = "test_metric";
+const int64_t bucketStartTimeNs = 10000000000;
+const int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
-TEST(GaugeMetricProducerTest, TestWithCondition) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
-
+TEST(GaugeMetricProducerTest, TestNoCondition) {
GaugeMetric metric;
- metric.set_name("1");
+ metric.set_name(metricName);
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
- metric.set_gauge_field(2);
+ metric.mutable_gauge_fields()->add_field_num(2);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- GaugeMetricProducer gaugeProducer(metric, 1 /*has condition*/, wizard, -1, bucketStartTimeNs);
+ // TODO: pending refactor of StatsPullerManager
+ // For now we still need this so that it doesn't do real pulling.
+ shared_ptr<MockStatsPullerManager> pullerManager =
+ make_shared<StrictMock<MockStatsPullerManager>>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- vector<std::shared_ptr<LogEvent>> allData;
- std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1);
- event1->write(1);
- event1->write(13);
- event1->init();
- allData.push_back(event1);
+ GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ tagId, tagId, bucketStartTimeNs, pullerManager);
- std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 10);
- event2->write(1);
- event2->write(15);
- event2->init();
- allData.push_back(event2);
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event->write(tagId);
+ event->write(11);
+ event->init();
+ allData.push_back(event);
gaugeProducer.onDataPulled(allData);
- gaugeProducer.flushIfNeededLocked(event2->GetTimestampNs() + 1);
- EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(11, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
- gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 11);
- gaugeProducer.onConditionChanged(false, bucketStartTimeNs + 21);
- gaugeProducer.onConditionChanged(true, bucketStartTimeNs + bucketSizeNs + 11);
- std::shared_ptr<LogEvent> event3 =
- std::make_shared<LogEvent>(1, bucketStartTimeNs + 2 * bucketSizeNs + 10);
- event3->write(1);
- event3->write(25);
- event3->init();
- allData.push_back(event3);
+ allData.clear();
+ std::shared_ptr<LogEvent> event2 =
+ std::make_shared<LogEvent>(tagId, bucket3StartTimeNs + 10);
+ event2->write(tagId);
+ event2->write(25);
+ event2->init();
+ allData.push_back(event2);
gaugeProducer.onDataPulled(allData);
- gaugeProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 10);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(25L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+ EXPECT_EQ(25, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
// One dimension.
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(25L, gaugeProducer.mPastBuckets.begin()->second.front().mGauge);
- EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.front().mBucketNum);
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- gaugeProducer.mPastBuckets.begin()->second.front().mBucketStartNs);
+ EXPECT_EQ(11L, gaugeProducer.mPastBuckets.begin()->second.back().mEvent->kv[0].value_int());
+ EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
+
+ gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs);
+ EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
+ // One dimension.
+ EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+ EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(25L, gaugeProducer.mPastBuckets.begin()->second.back().mEvent->kv[0].value_int());
+ EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
}
-TEST(GaugeMetricProducerTest, TestNoCondition) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
-
+TEST(GaugeMetricProducerTest, TestWithCondition) {
GaugeMetric metric;
- metric.set_name("1");
+ metric.set_name(metricName);
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
- metric.set_gauge_field(2);
+ metric.mutable_gauge_fields()->add_field_num(2);
+ metric.set_condition("SCREEN_ON");
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- GaugeMetricProducer gaugeProducer(metric, -1 /*no condition*/, wizard, -1, bucketStartTimeNs);
+ 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(100);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
- vector<std::shared_ptr<LogEvent>> allData;
- std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1);
- event1->write(1);
- event1->write(13);
- event1->init();
- allData.push_back(event1);
+ GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId, tagId,
+ bucketStartTimeNs, pullerManager);
- std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 10);
- event2->write(1);
- event2->write(15);
- event2->init();
- allData.push_back(event2);
-
- std::shared_ptr<LogEvent> event3 =
- std::make_shared<LogEvent>(1, bucketStartTimeNs + 2 * bucketSizeNs + 10);
- event3->write(1);
- event3->write(25);
- event3->init();
- allData.push_back(event3);
-
- gaugeProducer.onDataPulled(allData);
- // Has one slice
+ gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(25L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+ EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+ EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event->write(1);
+ event->write(110);
+ event->init();
+ allData.push_back(event);
+ gaugeProducer.onDataPulled(allData);
+
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+ EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+ EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()->second.back().mEvent->kv[0].value_int());
+
+ gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10);
+ gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10);
+ EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(13L, gaugeProducer.mPastBuckets.begin()->second.front().mGauge);
- EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.begin()->second.front().mBucketNum);
- EXPECT_EQ(25L, gaugeProducer.mPastBuckets.begin()->second.back().mGauge);
- EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- gaugeProducer.mPastBuckets.begin()->second.back().mBucketStartNs);
+ EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()->second.back().mEvent->kv[0].value_int());
+ EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
}
TEST(GaugeMetricProducerTest, TestAnomalyDetection) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
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());
+
GaugeMetric metric;
- metric.set_name("1");
+ metric.set_name(metricName);
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
- metric.set_gauge_field(2);
- GaugeMetricProducer gaugeProducer(metric, -1 /*no condition*/, wizard, -1, bucketStartTimeNs);
+ metric.mutable_gauge_fields()->add_field_num(2);
+ GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ tagId, tagId, bucketStartTimeNs, pullerManager);
Alert alert;
alert.set_name("alert");
- alert.set_metric_name("1");
+ alert.set_metric_name(metricName);
alert.set_trigger_if_sum_gt(25);
alert.set_number_of_buckets(2);
sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey);
@@ -160,7 +183,7 @@
gaugeProducer.onDataPulled({event1});
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+ EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL);
std::shared_ptr<LogEvent> event2 =
@@ -171,7 +194,7 @@
gaugeProducer.onDataPulled({event2});
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+ EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event2->GetTimestampNs());
std::shared_ptr<LogEvent> event3 =
@@ -182,7 +205,7 @@
gaugeProducer.onDataPulled({event3});
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(24L, gaugeProducer.mCurrentSlicedBucket->begin()->second);
+ EXPECT_EQ(24L, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event3->GetTimestampNs());
// The event4 does not have the gauge field. Thus the current bucket value is 0.
@@ -191,7 +214,8 @@
event4->write(1);
event4->init();
gaugeProducer.onDataPulled({event4});
- EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(0, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event3->GetTimestampNs());
}
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index d320697..6f117d3 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -35,23 +35,23 @@
namespace statsd {
const ConfigKey kConfigKey(0, "test");
+const int tagId = 1;
+const string metricName = "test_metric";
+const int64_t bucketStartTimeNs = 10000000000;
+const int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
+
/*
* Tests pulled atoms with no conditions
*/
TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
-
- int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
- int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
-
ValueMetric metric;
- metric.set_name("1");
+ metric.set_name(metricName);
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
metric.set_value_field(2);
- int tagId = 1;
-
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
// TODO: pending refactor of StatsPullerManager
// For now we still need this so that it doesn't do real pulling.
@@ -65,8 +65,8 @@
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
- event->write(1);
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event->write(tagId);
event->write(11);
event->init();
allData.push_back(event);
@@ -75,76 +75,60 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // has one raw pair
- EXPECT_EQ(1UL, curInterval.raw.size());
- // value is 11, 11
- EXPECT_EQ(11, curInterval.raw.front().first);
- EXPECT_EQ(11, curInterval.raw.front().second);
- ValueMetricProducer::Interval nextInterval = valueProducer.mNextSlicedBucket.begin()->second;
- // has one raw pair
- EXPECT_EQ(1UL, nextInterval.raw.size());
- // value is 11, 0
- EXPECT_EQ(11, nextInterval.raw.front().first);
- EXPECT_EQ(0, nextInterval.raw.front().second);
- EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ // startUpdated:true tainted:0 sum:0 start:11
+ EXPECT_EQ(true, curInterval.startUpdated);
+ EXPECT_EQ(0, curInterval.tainted);
+ EXPECT_EQ(0, curInterval.sum);
+ EXPECT_EQ(11, curInterval.start);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second.back().mValue);
allData.clear();
- event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(1);
- event->write(22);
+ event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
+ event->write(tagId);
+ event->write(23);
event->init();
allData.push_back(event);
valueProducer.onDataPulled(allData);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // has one raw pair
- EXPECT_EQ(1UL, curInterval.raw.size());
- // value is 22, 0
- EXPECT_EQ(22, curInterval.raw.front().first);
- EXPECT_EQ(0, curInterval.raw.front().second);
- EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+ // tartUpdated:false tainted:0 sum:12
+ EXPECT_EQ(true, curInterval.startUpdated);
+ EXPECT_EQ(0, curInterval.tainted);
+ EXPECT_EQ(0, curInterval.sum);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(11, valueProducer.mPastBuckets.begin()->second.back().mValue);
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
allData.clear();
- event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
- event->write(1);
- event->write(33);
+ event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
+ event->write(tagId);
+ event->write(36);
event->init();
allData.push_back(event);
valueProducer.onDataPulled(allData);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- EXPECT_EQ(1UL, curInterval.raw.size());
- // value is 33, 0
- EXPECT_EQ(33, curInterval.raw.front().first);
- EXPECT_EQ(0, curInterval.raw.front().second);
- EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+ // startUpdated:false tainted:0 sum:12
+ EXPECT_EQ(true, curInterval.startUpdated);
+ EXPECT_EQ(0, curInterval.tainted);
+ EXPECT_EQ(0, curInterval.sum);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(11, valueProducer.mPastBuckets.begin()->second.back().mValue);
+ EXPECT_EQ(3UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second.back().mValue);
}
/*
* Test pulled event with non sliced condition.
*/
TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
-
- int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
- int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
-
ValueMetric metric;
- metric.set_name("1");
+ metric.set_name(metricName);
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
metric.set_value_field(2);
metric.set_condition("SCREEN_ON");
- int tagId = 1;
-
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
@@ -153,28 +137,18 @@
EXPECT_CALL(*pullerManager, Pull(tagId, _))
.WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
-
- int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
- int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event->write(1);
+ event->write(tagId);
event->write(100);
event->init();
data->push_back(event);
return true;
}))
.WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
-
- int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
- int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
- event->write(1);
+ event->write(tagId);
event->write(120);
event->init();
data->push_back(event);
@@ -184,17 +158,16 @@
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
pullerManager);
- valueProducer.onConditionChanged(true, bucketStartTimeNs + 10);
+ valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // has one raw pair
- EXPECT_EQ(1UL, curInterval.raw.size());
- // value is 100, 0
- EXPECT_EQ(100, curInterval.raw.front().first);
- EXPECT_EQ(0, curInterval.raw.front().second);
- EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+ // startUpdated:false tainted:0 sum:0 start:100
+ EXPECT_EQ(100, curInterval.start);
+ EXPECT_EQ(true, curInterval.startUpdated);
+ EXPECT_EQ(0, curInterval.tainted);
+ EXPECT_EQ(0, curInterval.sum);
EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
vector<shared_ptr<LogEvent>> allData;
@@ -209,11 +182,8 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // has one raw pair
- EXPECT_EQ(1UL, curInterval.raw.size());
- // value is 110, 0
- EXPECT_EQ(110, curInterval.raw.front().first);
- EXPECT_EQ(0, curInterval.raw.front().second);
+ // startUpdated:false tainted:0 sum:0 start:110
+ EXPECT_EQ(110, curInterval.start);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValue);
@@ -223,27 +193,17 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // has one raw pair
- EXPECT_EQ(1UL, curInterval.raw.size());
- // value is 110, 120
- EXPECT_EQ(110, curInterval.raw.front().first);
- EXPECT_EQ(120, curInterval.raw.front().second);
+ // startUpdated:false tainted:0 sum:0 start:110
+ EXPECT_EQ(10, curInterval.sum);
+ EXPECT_EQ(false, curInterval.startUpdated);
}
TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
-
- int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
- int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
-
ValueMetric metric;
- metric.set_name("1");
+ metric.set_name(metricName);
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
metric.set_value_field(2);
- int tagId = 1;
-
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
shared_ptr<MockStatsPullerManager> pullerManager =
make_shared<StrictMock<MockStatsPullerManager>>();
@@ -255,32 +215,22 @@
event1->write(1);
event1->write(10);
event1->init();
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
event2->write(1);
event2->write(20);
event2->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1, false);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // has one raw pair
- EXPECT_EQ(1UL, curInterval.raw.size());
- // value is 10, 0
- EXPECT_EQ(10, curInterval.raw.front().first);
- EXPECT_EQ(0, curInterval.raw.front().second);
- EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+ EXPECT_EQ(10, curInterval.sum);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2, false);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- // has one raw pair
- EXPECT_EQ(2UL, curInterval.raw.size());
- // value is 10, 20
- EXPECT_EQ(10, curInterval.raw.front().first);
- EXPECT_EQ(20, curInterval.raw.back().first);
- EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+ EXPECT_EQ(30, curInterval.sum);
valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
@@ -288,6 +238,79 @@
EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValue);
}
+TEST(ValueMetricProducerTest, TestAnomalyDetection) {
+ Alert alert;
+ alert.set_name("alert");
+ alert.set_metric_name(metricName);
+ alert.set_trigger_if_sum_gt(130);
+ alert.set_number_of_buckets(2);
+ alert.set_refractory_period_secs(3);
+ sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey);
+
+ ValueMetric metric;
+ metric.set_name(metricName);
+ metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+ metric.set_value_field(2);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ -1 /*not pulled*/, bucketStartTimeNs);
+ valueProducer.addAnomalyTracker(anomalyTracker);
+
+
+ shared_ptr<LogEvent> event1
+ = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1 * NS_PER_SEC);
+ event1->write(161);
+ event1->write(10); // value of interest
+ event1->init();
+ shared_ptr<LogEvent> event2
+ = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 + NS_PER_SEC);
+ event2->write(162);
+ event2->write(20); // value of interest
+ event2->init();
+ shared_ptr<LogEvent> event3
+ = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC);
+ event3->write(163);
+ event3->write(130); // value of interest
+ event3->init();
+ shared_ptr<LogEvent> event4
+ = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1 * NS_PER_SEC);
+ event4->write(35);
+ event4->write(1); // value of interest
+ event4->init();
+ shared_ptr<LogEvent> event5
+ = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC);
+ event5->write(45);
+ event5->write(150); // value of interest
+ event5->init();
+ shared_ptr<LogEvent> event6
+ = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC);
+ event6->write(25);
+ event6->write(160); // value of interest
+ event6->init();
+
+ // Two events in bucket #0.
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL); // Value sum == 30 <= 130.
+
+ // One event in bucket #2. No alarm as bucket #0 is trashed out.
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL); // Value sum == 130 <= 130.
+
+ // Three events in bucket #3.
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
+ // Anomaly at event 4 since Value sum == 131 > 130!
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event4->GetTimestampNs());
+ 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->getLastAlarmTimestampNs(), (long long)event4->GetTimestampNs());
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6);
+ // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period.
+ EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event6->GetTimestampNs());
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk
index 1bd5f30..6b0531d 100644
--- a/cmds/statsd/tools/dogfood/Android.mk
+++ b/cmds/statsd/tools/dogfood/Android.mk
@@ -16,19 +16,19 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SRC_FILES += ../../src/stats_log.proto \
- ../../src/atoms_copy.proto
-
-LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
LOCAL_PACKAGE_NAME := StatsdDogfood
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES += ../../src/stats_log.proto \
+ ../../src/atoms.proto
+LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
LOCAL_DEX_PREOPT := false
-include $(BUILD_PACKAGE)
\ No newline at end of file
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
diff --git a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml b/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
index 3997897..5d35c29 100644
--- a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
+++ b/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
@@ -111,6 +111,23 @@
android:text="@string/screen_off"/>
</LinearLayout>
+ <LinearLayout android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/custom_start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/custom_start" />
+
+ <Button
+ android:id="@+id/custom_stop"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/custom_stop" />
+ </LinearLayout>
+
<Button android:id="@+id/dump"
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 7690df6..0eab0f4 100644
--- a/cmds/statsd/tools/dogfood/res/values/strings.xml
+++ b/cmds/statsd/tools/dogfood/res/values/strings.xml
@@ -47,6 +47,9 @@
<string name="screen_on">Screen On</string>
<string name="screen_off">Screen Off</string>
+ <string name="custom_start">App hook start</string>
+ <string name="custom_stop">App hook stop</string>
+
<string name="dump">DumpReport</string>
<string name="report_header">Report details</string>
</resources>
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 c83802e..70dd634 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
@@ -140,6 +140,20 @@
}
});
+ findViewById(R.id.custom_start).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ StatsLog.logStart(8);
+ }
+ });
+
+ findViewById(R.id.custom_stop).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ StatsLog.logStop(8);
+ }
+ });
+
mReportText = (TextView) findViewById(R.id.report_text);
findViewById(R.id.dump).setOnClickListener(new View.OnClickListener() {
diff --git a/cmds/statsd/tools/loadtest/Android.mk b/cmds/statsd/tools/loadtest/Android.mk
index f3f0a7c..0a0fd66 100644
--- a/cmds/statsd/tools/loadtest/Android.mk
+++ b/cmds/statsd/tools/loadtest/Android.mk
@@ -16,20 +16,20 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SRC_FILES += ../../src/stats_log.proto \
- ../../src/atoms_copy.proto \
- ../../src/statsd_config.proto
-
-LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
LOCAL_PACKAGE_NAME := StatsdLoadtest
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES += ../../src/stats_log.proto \
+ ../../src/atoms.proto \
+ ../../src/statsd_config.proto
+LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
LOCAL_DEX_PREOPT := false
+LOCAL_PROGUARD_ENABLED := disabled
+
include $(BUILD_PACKAGE)
diff --git a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml b/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
index 1e28f67..2a254df 100644
--- a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
+++ b/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
@@ -160,13 +160,6 @@
android:layout_width="1dp"
android:layout_height="30dp"/>
- <Button
- android:id="@+id/display_output"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/display_output"
- android:textSize="30dp"/>
-
<Space
android:layout_width="1dp"
android:layout_height="30dp"/>
diff --git a/cmds/statsd/tools/loadtest/res/raw/loadtest_config b/cmds/statsd/tools/loadtest/res/raw/loadtest_config
index 7822367..fbce0e8 100755
--- a/cmds/statsd/tools/loadtest/res/raw/loadtest_config
+++ b/cmds/statsd/tools/loadtest/res/raw/loadtest_config
Binary files differ
diff --git a/cmds/statsd/tools/loadtest/res/values/strings.xml b/cmds/statsd/tools/loadtest/res/values/strings.xml
index cb38298..522337e 100644
--- a/cmds/statsd/tools/loadtest/res/values/strings.xml
+++ b/cmds/statsd/tools/loadtest/res/values/strings.xml
@@ -20,7 +20,6 @@
<string name="app_name">Statsd Loadtest</string>
<string name="bucket_label">bucket size (mins): </string>
<string name="burst_label">burst: </string>
- <string name="display_output">Show metrics data</string>
<string name="placebo">placebo</string>
<string name="period_label">logging period (secs): </string>
<string name="replication_label">metric replication: </string>
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 522dea6..0a30ff8 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
@@ -15,6 +15,7 @@
*/
package com.android.statsd.loadtest;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -43,6 +44,10 @@
import android.widget.EditText;
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 java.util.List;
/**
* Runs a load test for statsd.
@@ -191,13 +196,6 @@
}
});
- findViewById(R.id.display_output).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- fetchAndDisplayData();
- }
- });
-
mAlarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
mStatsManager = (StatsManager) getSystemService("stats");
mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
@@ -239,6 +237,48 @@
super.onDestroy();
}
+ @Nullable
+ public StatsdStatsReport getMetadata() {
+ if (!statsdRunning()) {
+ return null;
+ }
+ if (mStatsManager != null) {
+ byte[] data = mStatsManager.getMetadata();
+ if (data != null) {
+ StatsdStatsReport report = null;
+ boolean good = false;
+ try {
+ return StatsdStatsReport.parseFrom(data);
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ Log.d(TAG, "Bad StatsdStatsReport");
+ }
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ public List<ConfigMetricsReport> getData() {
+ if (!statsdRunning()) {
+ return null;
+ }
+ if (mStatsManager != null) {
+ byte[] data = mStatsManager.getData(ConfigFactory.CONFIG_NAME);
+ if (data != null) {
+ ConfigMetricsReportList reports = null;
+ try {
+ reports = ConfigMetricsReportList.parseFrom(data);
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ Log.d(TAG, "Invalid data");
+ }
+ if (reports != null) {
+ return reports.getReportsList();
+ }
+ }
+ }
+ return null;
+ }
+
private void onPerfAlarm() {
if (mPerfData != null) {
mPerfData.onAlarm(this);
@@ -285,6 +325,9 @@
// Prepare to push a sequence of atoms to logd.
mPusher = new SequencePusher(mBurst, mPlacebo);
+ // Force a data flush by requesting data.
+ getData();
+
// Create a config and push it to statsd.
if (!setConfig(mFactory.getConfig(mReplication, mBucketMins * 60 * 1000, mPlacebo))) {
return;
@@ -355,42 +398,6 @@
mPlaceboCheckBox.setEnabled(!mStarted);
}
- private void fetchAndDisplayData() {
- if (!statsdRunning()) {
- return;
- }
- if (mStatsManager != null) {
- byte[] data = mStatsManager.getData(ConfigFactory.CONFIG_NAME);
- if (data != null) {
- displayData(data);
- } else {
- mReportText.setText("Failed to pull data");
- }
- }
- }
-
- private void displayData(byte[] data) {
- com.android.os.StatsLog.ConfigMetricsReportList reports = null;
- boolean good = false;
- if (data != null) {
- try {
- reports = com.android.os.StatsLog.ConfigMetricsReportList.parseFrom(data);
- good = true;
- } catch (com.google.protobuf.InvalidProtocolBufferException e) {
- // display it in the text view.
- }
- }
- int size = data == null ? 0 : data.length;
- StringBuilder sb = new StringBuilder();
- sb.append(good ? "Proto parsing OK!" : "Proto parsing Error!");
- sb.append(" size:").append(size).append("\n");
-
- if (good && reports != null) {
- DisplayProtoUtils.displayLogReport(sb, reports);
- mReportText.setText(sb.toString());
- }
- }
-
private boolean statsdRunning() {
if (IStatsManager.Stub.asInterface(ServiceManager.getService("stats")) == null) {
Log.d(TAG, "Statsd not running");
@@ -412,11 +419,6 @@
private void clearConfigs() {
// TODO: Clear all configs instead of specific ones.
if (mStatsManager != null) {
- if (!mStatsManager.removeConfiguration("fake")) {
- Log.d(TAG, "Removed \"fake\" statsd configs.");
- } else {
- Log.d(TAG, "Failed to remove \"fake\" config. Loadtest results cannot be trusted.");
- }
if (mStarted) {
if (!mStatsManager.removeConfiguration(ConfigFactory.CONFIG_NAME)) {
Log.d(TAG, "Removed loadtest statsd configs.");
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java
index d82a0ea..d9513a1 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java
@@ -39,7 +39,6 @@
@Override
public void onAlarm(Context context) {
- Log.d(TAG, "GOT ALARM IN MEM");
runDumpsysStats(context, DUMP_FILENAME, "meminfo");
readDumpData(context, DUMP_FILENAME, new MemInfoParser(mStartTimeMillis), mSb);
}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java
index 81a84f5..555e6dd 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java
@@ -22,7 +22,7 @@
public abstract class NumericalWatcher implements TextWatcher {
- private static final String TAG = "loadtest.NumericalWatcher";
+ private static final String TAG = "loadtest.NumericalWatcher";
private final TextView mTextView;
private final int mMin;
@@ -45,9 +45,6 @@
}
int unsanitized = Integer.parseInt(s);
int newValue = sanitize(unsanitized);
-
- Log.d(TAG, "YOYO " + currentValue + " " + newValue + " " + unsanitized);
-
if (currentValue != newValue || unsanitized != newValue) {
currentValue = newValue;
editable.clear();
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
index 4665247..22ba9c5 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
@@ -51,13 +51,17 @@
private final Set<PerfDataRecorder> mRecorders;
- public PerfData(Context context, boolean placebo, int replication, long bucketMins,
- long periodSecs, int burst) {
+ public PerfData(LoadtestActivity loadtestActivity, boolean placebo, int replication,
+ long bucketMins, long periodSecs, int burst) {
super(placebo, replication, bucketMins, periodSecs, burst);
mRecorders = new HashSet();
mRecorders.add(new BatteryDataRecorder(placebo, replication, bucketMins, periodSecs, burst));
mRecorders.add(new MemoryDataRecorder(placebo, replication, bucketMins, periodSecs, burst));
- mAlarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ mRecorders.add(new StatsdStatsRecorder(loadtestActivity, placebo, replication, bucketMins,
+ periodSecs, burst));
+ mRecorders.add(new ValidationRecorder(loadtestActivity, placebo, replication, bucketMins,
+ periodSecs, burst));
+ mAlarmMgr = (AlarmManager) loadtestActivity.getSystemService(Context.ALARM_SERVICE);
}
public void onDestroy() {
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java
index 15a8e5c..5b5ba37 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java
@@ -35,12 +35,12 @@
public abstract class PerfDataRecorder {
private static final String TAG = "loadtest.PerfDataRecorder";
- protected final String mFileSuffix;
+ protected final String mTimeAsString;
protected final String mColumnSuffix;
protected PerfDataRecorder(boolean placebo, int replication, long bucketMins, long periodSecs,
int burst) {
- mFileSuffix = new SimpleDateFormat("YYYY_MM_dd_HH_mm_ss").format(new Date());
+ mTimeAsString = new SimpleDateFormat("YYYY_MM_dd_HH_mm_ss").format(new Date());
mColumnSuffix = getColumnSuffix(placebo, replication, bucketMins, periodSecs, burst);
}
@@ -103,7 +103,7 @@
/** Writes CSV data to a file. */
protected void writeData(Context context, String filePrefix, String columnPrefix,
StringBuilder sb) {
- File dataFile = new File(getStorageDir(), filePrefix + mFileSuffix + ".csv");
+ File dataFile = new File(getStorageDir(), filePrefix + mTimeAsString + ".csv");
FileWriter writer = null;
try {
@@ -131,7 +131,7 @@
private File getStorageDir() {
File file = new File(Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_DOCUMENTS), "loadtest");
+ Environment.DIRECTORY_DOCUMENTS), "loadtest/" + mTimeAsString);
if (!file.mkdirs()) {
Log.e(TAG, "Directory not created");
}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java
new file mode 100644
index 0000000..4ef5dc2
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java
@@ -0,0 +1,61 @@
+/*
+ * 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.statsd.loadtest;
+
+import android.content.Context;
+import android.util.Log;
+import com.android.os.StatsLog.StatsdStatsReport;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class StatsdStatsRecorder extends PerfDataRecorder {
+ private static final String TAG = "loadtest.StatsdStatsRecorder";
+
+ private final LoadtestActivity mLoadtestActivity;
+
+ public StatsdStatsRecorder(LoadtestActivity loadtestActivity, boolean placebo, int replication,
+ long bucketMins, long periodSecs, int burst) {
+ super(placebo, replication, bucketMins, periodSecs, burst);
+ mLoadtestActivity = loadtestActivity;
+ }
+
+ @Override
+ public void startRecording(Context context) {
+ // Nothing to do.
+ }
+
+ @Override
+ public void onAlarm(Context context) {
+ // Nothing to do.
+ }
+
+ @Override
+ public void stopRecording(Context context) {
+ StatsdStatsReport metadata = mLoadtestActivity.getMetadata();
+ if (metadata != null) {
+ int numConfigs = metadata.getConfigStatsCount();
+ StringBuilder sb = new StringBuilder();
+ StatsdStatsReport.ConfigStats configStats = metadata.getConfigStats(numConfigs - 1);
+ sb.append("metric_count,")
+ .append(configStats.getMetricCount() + "\n")
+ .append("condition_count,")
+ .append(configStats.getConditionCount() + "\n")
+ .append("matcher_count,")
+ .append(configStats.getMatcherCount() + "\n");
+ writeData(context, "statsdstats_", "", sb);
+ }
+ }
+}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java
new file mode 100644
index 0000000..4b614aa
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java
@@ -0,0 +1,95 @@
+/*
+ * 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.statsd.loadtest;
+
+import android.content.Context;
+import android.util.Log;
+import com.android.os.StatsLog.ConfigMetricsReport;
+import com.android.os.StatsLog.EventMetricData;
+import com.android.os.StatsLog.StatsLogReport;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Checks the correctness of the stats.
+ */
+public class ValidationRecorder extends PerfDataRecorder {
+ private static final String TAG = "loadtest.ValidationRecorder";
+
+ private final LoadtestActivity mLoadtestActivity;
+
+ public ValidationRecorder(LoadtestActivity loadtestActivity, boolean placebo, int replication,
+ long bucketMins, long periodSecs, int burst) {
+ super(placebo, replication, bucketMins, periodSecs, burst);
+ mLoadtestActivity = loadtestActivity;
+ }
+
+ @Override
+ public void startRecording(Context context) {
+ // Nothing to do.
+ }
+
+ @Override
+ public void onAlarm(Context context) {
+ validateData();
+ }
+
+ @Override
+ public void stopRecording(Context context) {
+ validateData();
+ }
+
+ private void validateData() {
+ List<ConfigMetricsReport> reports = mLoadtestActivity.getData();
+ if (reports != null) {
+ Log.d(TAG, "GOT DATA");
+ for (ConfigMetricsReport report : reports) {
+ for (StatsLogReport logReport : report.getMetricsList()) {
+ if (!logReport.hasMetricName()) {
+ Log.e(TAG, "Metric missing name.");
+ continue;
+ }
+ String metricName = logReport.getMetricName();
+ if (metricName.startsWith("EVENT_BATTERY_LEVEL_CHANGES_WHILE_SCREEN_IS_ON_")) {
+ validateEventBatteryLevelChangesWhileScreenIsOn(logReport);
+ continue;
+ }
+ if (metricName.startsWith("EVENT_BATTERY_LEVEL_CHANGES_")) {
+ validateEventBatteryLevelChanges(logReport);
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ private void validateEventBatteryLevelChanges(StatsLogReport logReport) {
+ Log.d(TAG, "Validating " + logReport.getMetricName());
+ if (logReport.hasEventMetrics()) {
+ Log.d(TAG, "Num events captured: " + logReport.getEventMetrics().getDataCount());
+ for (EventMetricData data : logReport.getEventMetrics().getDataList()) {
+ Log.d(TAG, " Event : " + data.getAtom());
+ }
+ } else {
+ Log.d(TAG, "Metric is invalid");
+ }
+ }
+
+ private void validateEventBatteryLevelChangesWhileScreenIsOn(StatsLogReport logReport) {
+ Log.d(TAG, "Validating " + logReport.getMetricName());
+ }
+}
diff --git a/cmds/uiautomator/instrumentation/Android.mk b/cmds/uiautomator/instrumentation/Android.mk
index 008bb93..ed99f3e 100644
--- a/cmds/uiautomator/instrumentation/Android.mk
+++ b/cmds/uiautomator/instrumentation/Android.mk
@@ -21,7 +21,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, testrunner-src) \
$(call all-java-files-under, ../library/core-src)
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base.stubs
LOCAL_STATIC_JAVA_LIBRARIES := junit
LOCAL_MODULE := uiautomator-instrumentation
# TODO: change this to 18 when it's available
diff --git a/cmds/uiautomator/library/Android.mk b/cmds/uiautomator/library/Android.mk
index 22cffe6..62a2865 100644
--- a/cmds/uiautomator/library/Android.mk
+++ b/cmds/uiautomator/library/Android.mk
@@ -36,7 +36,7 @@
# Generate the stub source files
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(uiautomator.core_src_files)
-LOCAL_JAVA_LIBRARIES := $(uiautomator.core_java_libraries) legacy-android-test
+LOCAL_JAVA_LIBRARIES := $(uiautomator.core_java_libraries) android.test.base
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/core-src \
$(LOCAL_PATH)/testrunner-src
diff --git a/cmds/wm/Android.mk b/cmds/wm/Android.mk
index 3f3795f..693c6e7 100644
--- a/cmds/wm/Android.mk
+++ b/cmds/wm/Android.mk
@@ -3,11 +3,6 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_MODULE := wm
-include $(BUILD_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
LOCAL_MODULE := wm
LOCAL_SRC_FILES := wm
LOCAL_MODULE_CLASS := EXECUTABLES
diff --git a/cmds/wm/src/com/android/commands/wm/Wm.java b/cmds/wm/src/com/android/commands/wm/Wm.java
deleted file mode 100644
index 8defb33..0000000
--- a/cmds/wm/src/com/android/commands/wm/Wm.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
-**
-** Copyright 2013, 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.commands.wm;
-
-import android.content.Context;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.util.AndroidException;
-import android.util.DisplayMetrics;
-import android.system.Os;
-import android.view.Display;
-import android.view.IWindowManager;
-import com.android.internal.os.BaseCommand;
-
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.DataInputStream;
-import java.io.PrintStream;
-import java.lang.Runtime;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Wm extends BaseCommand {
-
- private IWindowManager mWm;
-
- /**
- * Command-line entry point.
- *
- * @param args The command-line arguments
- */
- public static void main(String[] args) {
- (new Wm()).run(args);
- }
-
- @Override
- public void onShowUsage(PrintStream out) {
- out.println(
- "usage: wm [subcommand] [options]\n" +
- " wm size [reset|WxH|WdpxHdp]\n" +
- " wm density [reset|DENSITY]\n" +
- " wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" +
- " wm scaling [off|auto]\n" +
- " wm screen-capture [userId] [true|false]\n" +
- "\n" +
- "wm size: return or override display size.\n" +
- " width and height in pixels unless suffixed with 'dp'.\n" +
- "\n" +
- "wm density: override display density.\n" +
- "\n" +
- "wm overscan: set overscan area for display.\n" +
- "\n" +
- "wm scaling: set display scaling mode.\n" +
- "\n" +
- "wm screen-capture: enable/disable screen capture.\n" +
- "\n" +
- "wm dismiss-keyguard: dismiss the keyguard, prompting the user for auth if " +
- "necessary.\n" +
- "\n" +
- "wm surface-trace: log surface commands to stdout in a binary format.\n"
- );
- }
-
- @Override
- public void onRun() throws Exception {
- mWm = IWindowManager.Stub.asInterface(ServiceManager.checkService(
- Context.WINDOW_SERVICE));
- if (mWm == null) {
- System.err.println(NO_SYSTEM_ERROR_CODE);
- throw new AndroidException("Can't connect to window manager; is the system running?");
- }
-
- String op = nextArgRequired();
-
- if (op.equals("size")) {
- runDisplaySize();
- } else if (op.equals("density")) {
- runDisplayDensity();
- } else if (op.equals("overscan")) {
- runDisplayOverscan();
- } else if (op.equals("scaling")) {
- runDisplayScaling();
- } else if (op.equals("screen-capture")) {
- runSetScreenCapture();
- } else if (op.equals("dismiss-keyguard")) {
- runDismissKeyguard();
- } else if (op.equals("surface-trace")) {
- runSurfaceTrace();
- } else {
- showError("Error: unknown command '" + op + "'");
- return;
- }
- }
-
- private void runSurfaceTrace() throws Exception {
- ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(FileDescriptor.out);
- mWm.enableSurfaceTrace(pfd);
-
- try {
- // No one is going to wake us up, we are just waiting on SIGINT. Otherwise
- // the WM can happily continue writing to our stdout.
- synchronized (this) {
- this.wait();
- }
- } finally {
- mWm.disableSurfaceTrace();
- }
- }
-
- private void runSetScreenCapture() throws Exception {
- String userIdStr = nextArg();
- String enableStr = nextArg();
- int userId;
- boolean disable;
-
- try {
- userId = Integer.parseInt(userIdStr);
- } catch (NumberFormatException e) {
- System.err.println("Error: bad number " + e);
- return;
- }
-
- disable = !Boolean.parseBoolean(enableStr);
-
- try {
- mWm.setScreenCaptureDisabled(userId, disable);
- } catch (RemoteException e) {
- System.err.println("Error: Can't set screen capture " + e);
- }
- }
-
- private void runDisplaySize() throws Exception {
- String size = nextArg();
- int w, h;
- if (size == null) {
- Point initialSize = new Point();
- Point baseSize = new Point();
- try {
- mWm.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize);
- mWm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize);
- System.out.println("Physical size: " + initialSize.x + "x" + initialSize.y);
- if (!initialSize.equals(baseSize)) {
- System.out.println("Override size: " + baseSize.x + "x" + baseSize.y);
- }
- } catch (RemoteException e) {
- }
- return;
- } else if ("reset".equals(size)) {
- w = h = -1;
- } else {
- int div = size.indexOf('x');
- if (div <= 0 || div >= (size.length()-1)) {
- System.err.println("Error: bad size " + size);
- return;
- }
- String wstr = size.substring(0, div);
- String hstr = size.substring(div+1);
- try {
- w = parseDimension(wstr);
- h = parseDimension(hstr);
- } catch (NumberFormatException e) {
- System.err.println("Error: bad number " + e);
- return;
- }
- }
-
- try {
- if (w >= 0 && h >= 0) {
- // TODO(multidisplay): For now Configuration only applies to main screen.
- mWm.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h);
- } else {
- mWm.clearForcedDisplaySize(Display.DEFAULT_DISPLAY);
- }
- } catch (RemoteException e) {
- }
- }
-
- private void runDisplayDensity() throws Exception {
- String densityStr = nextArg();
- int density;
- if (densityStr == null) {
- try {
- int initialDensity = mWm.getInitialDisplayDensity(Display.DEFAULT_DISPLAY);
- int baseDensity = mWm.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
- System.out.println("Physical density: " + initialDensity);
- if (initialDensity != baseDensity) {
- System.out.println("Override density: " + baseDensity);
- }
- } catch (RemoteException e) {
- }
- return;
- } else if ("reset".equals(densityStr)) {
- density = -1;
- } else {
- try {
- density = Integer.parseInt(densityStr);
- } catch (NumberFormatException e) {
- System.err.println("Error: bad number " + e);
- return;
- }
- if (density < 72) {
- System.err.println("Error: density must be >= 72");
- return;
- }
- }
-
- try {
- if (density > 0) {
- // TODO(multidisplay): For now Configuration only applies to main screen.
- mWm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density,
- UserHandle.USER_CURRENT);
- } else {
- mWm.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY,
- UserHandle.USER_CURRENT);
- }
- } catch (RemoteException e) {
- }
- }
-
- private void runDisplayOverscan() throws Exception {
- String overscanStr = nextArgRequired();
- Rect rect = new Rect();
- if ("reset".equals(overscanStr)) {
- rect.set(0, 0, 0, 0);
- } else {
- final Pattern FLATTENED_PATTERN = Pattern.compile(
- "(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)");
- Matcher matcher = FLATTENED_PATTERN.matcher(overscanStr);
- if (!matcher.matches()) {
- System.err.println("Error: bad rectangle arg: " + overscanStr);
- return;
- }
- rect.left = Integer.parseInt(matcher.group(1));
- rect.top = Integer.parseInt(matcher.group(2));
- rect.right = Integer.parseInt(matcher.group(3));
- rect.bottom = Integer.parseInt(matcher.group(4));
- }
-
- try {
- mWm.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right, rect.bottom);
- } catch (RemoteException e) {
- }
- }
-
- private void runDisplayScaling() throws Exception {
- String scalingStr = nextArgRequired();
- if ("auto".equals(scalingStr)) {
- mWm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 0);
- } else if ("off".equals(scalingStr)) {
- mWm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1);
- } else {
- System.err.println("Error: scaling must be 'auto' or 'off'");
- }
- }
-
- private void runDismissKeyguard() throws Exception {
- mWm.dismissKeyguard(null /* callback */);
- }
-
- private int parseDimension(String s) throws NumberFormatException {
- if (s.endsWith("px")) {
- return Integer.parseInt(s.substring(0, s.length() - 2));
- }
- if (s.endsWith("dp")) {
- int density;
- try {
- density = mWm.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
- } catch (RemoteException e) {
- density = DisplayMetrics.DENSITY_DEFAULT;
- }
- return Integer.parseInt(s.substring(0, s.length() - 2)) * density /
- DisplayMetrics.DENSITY_DEFAULT;
- }
- return Integer.parseInt(s);
- }
-}
diff --git a/cmds/wm/wm b/cmds/wm/wm
index 16d6bd6..cb45be2 100755
--- a/cmds/wm/wm
+++ b/cmds/wm/wm
@@ -1,7 +1,2 @@
#!/system/bin/sh
-# Script to start "wm" on the device, which has a very rudimentary
-# shell.
-#
-base=/system
-export CLASSPATH=$base/framework/wm.jar
-exec app_process $base/bin com.android.commands.wm.Wm "$@"
+cmd window "$@"
diff --git a/config/compiled-classes-phone b/config/compiled-classes-phone
deleted file mode 100644
index afa802c..0000000
--- a/config/compiled-classes-phone
+++ /dev/null
@@ -1,8500 +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.
-#
-#
-#
-# Compiled-classes filter file for phones.
-#
-# Using a compiled-classes file filters non-mentioned classes from being compiled into
-# the boot.oat file(s), reducing the size of the boot image. This is a tradeoff, as classes
-# that have not been compiled must be run with the interpreter or through JIT.
-#
-# This file has been derived for mainline phone (and tablet) usage in concern with the
-# preloaded-classes file, but is not used by default. To use this file, add a copy statement
-# to your device.mk, e.g.,
-#
-# PRODUCT_COPY_FILES += \
-# frameworks/base/config/compiled-classes-phone:system/etc/compiled-classes
-#
-android.R$styleable
-android.accessibilityservice.AccessibilityServiceInfo
-android.accessibilityservice.AccessibilityServiceInfo$1
-android.accessibilityservice.IAccessibilityServiceClient
-android.accessibilityservice.IAccessibilityServiceConnection
-android.accessibilityservice.IAccessibilityServiceConnection$Stub
-android.accounts.AbstractAccountAuthenticator
-android.accounts.AbstractAccountAuthenticator$Transport
-android.accounts.Account
-android.accounts.Account$1
-android.accounts.AccountAndUser
-android.accounts.AccountAuthenticatorResponse
-android.accounts.AccountAuthenticatorResponse$1
-android.accounts.AccountManager
-android.accounts.AccountManager$1
-android.accounts.AccountManager$11
-android.accounts.AccountManager$19
-android.accounts.AccountManager$3
-android.accounts.AccountManager$4
-android.accounts.AccountManager$AmsTask
-android.accounts.AccountManager$AmsTask$1
-android.accounts.AccountManager$AmsTask$Response
-android.accounts.AccountManager$BaseFutureTask
-android.accounts.AccountManager$BaseFutureTask$1
-android.accounts.AccountManager$BaseFutureTask$Response
-android.accounts.AccountManager$Future2Task
-android.accounts.AccountManager$Future2Task$1
-android.accounts.AccountManagerCallback
-android.accounts.AccountManagerFuture
-android.accounts.AccountManagerInternal
-android.accounts.AccountManagerInternal$OnAppPermissionChangeListener
-android.accounts.AccountsException
-android.accounts.AuthenticatorDescription
-android.accounts.AuthenticatorDescription$1
-android.accounts.AuthenticatorException
-android.accounts.IAccountAuthenticator
-android.accounts.IAccountAuthenticator$Stub
-android.accounts.IAccountAuthenticator$Stub$Proxy
-android.accounts.IAccountAuthenticatorResponse
-android.accounts.IAccountAuthenticatorResponse$Stub
-android.accounts.IAccountAuthenticatorResponse$Stub$Proxy
-android.accounts.IAccountManager
-android.accounts.IAccountManager$Stub
-android.accounts.IAccountManager$Stub$Proxy
-android.accounts.IAccountManagerResponse
-android.accounts.IAccountManagerResponse$Stub
-android.accounts.IAccountManagerResponse$Stub$Proxy
-android.accounts.OnAccountsUpdateListener
-android.accounts.OperationCanceledException
-android.animation.AnimationHandler
-android.animation.AnimationHandler$1
-android.animation.AnimationHandler$AnimationFrameCallback
-android.animation.AnimationHandler$AnimationFrameCallbackProvider
-android.animation.AnimationHandler$MyFrameCallbackProvider
-android.animation.Animator
-android.animation.Animator$AnimatorConstantState
-android.animation.Animator$AnimatorListener
-android.animation.Animator$AnimatorPauseListener
-android.animation.AnimatorInflater
-android.animation.AnimatorInflater$PathDataEvaluator
-android.animation.AnimatorListenerAdapter
-android.animation.AnimatorSet
-android.animation.AnimatorSet$1
-android.animation.AnimatorSet$2
-android.animation.AnimatorSet$3
-android.animation.AnimatorSet$AnimationEvent
-android.animation.AnimatorSet$Builder
-android.animation.AnimatorSet$Node
-android.animation.AnimatorSet$SeekState
-android.animation.ArgbEvaluator
-android.animation.FloatArrayEvaluator
-android.animation.FloatEvaluator
-android.animation.FloatKeyframeSet
-android.animation.IntEvaluator
-android.animation.IntKeyframeSet
-android.animation.Keyframe
-android.animation.Keyframe$FloatKeyframe
-android.animation.Keyframe$IntKeyframe
-android.animation.Keyframe$ObjectKeyframe
-android.animation.KeyframeSet
-android.animation.Keyframes
-android.animation.Keyframes$FloatKeyframes
-android.animation.Keyframes$IntKeyframes
-android.animation.LayoutTransition
-android.animation.LayoutTransition$1
-android.animation.LayoutTransition$2
-android.animation.LayoutTransition$3
-android.animation.LayoutTransition$4
-android.animation.LayoutTransition$5
-android.animation.LayoutTransition$CleanupCallback
-android.animation.LayoutTransition$TransitionListener
-android.animation.ObjectAnimator
-android.animation.PathKeyframes
-android.animation.PathKeyframes$1
-android.animation.PathKeyframes$2
-android.animation.PathKeyframes$FloatKeyframesBase
-android.animation.PathKeyframes$IntKeyframesBase
-android.animation.PathKeyframes$SimpleKeyframes
-android.animation.PropertyValuesHolder
-android.animation.PropertyValuesHolder$1
-android.animation.PropertyValuesHolder$FloatPropertyValuesHolder
-android.animation.PropertyValuesHolder$IntPropertyValuesHolder
-android.animation.PropertyValuesHolder$PropertyValues
-android.animation.PropertyValuesHolder$PropertyValues$DataSource
-android.animation.RectEvaluator
-android.animation.RevealAnimator
-android.animation.StateListAnimator
-android.animation.StateListAnimator$1
-android.animation.StateListAnimator$StateListAnimatorConstantState
-android.animation.StateListAnimator$Tuple
-android.animation.TimeAnimator
-android.animation.TimeAnimator$TimeListener
-android.animation.TimeInterpolator
-android.animation.TypeEvaluator
-android.animation.ValueAnimator
-android.animation.ValueAnimator$AnimatorUpdateListener
-android.app.-$Lambda$9I5WEMsoBc7l4QrNqZ4wx59yuHU
-android.app.-$Lambda$9I5WEMsoBc7l4QrNqZ4wx59yuHU$1
-android.app.-$Lambda$CsyQO--8YdRe5wlajUCi-L98enA$1
-android.app.-$Lambda$CsyQO--8YdRe5wlajUCi-L98enA$2
-android.app.-$Lambda$CsyQO--8YdRe5wlajUCi-L98enA$3
-android.app.-$Lambda$FilBqgnXJrN9Mgyks1XHeAxzSTk
-android.app.-$Lambda$c44uHH2WE4sJvw5tZZB6gRzEaHI
-android.app.-$Lambda$c44uHH2WE4sJvw5tZZB6gRzEaHI$1
-android.app.-$Lambda$vZ1qb742P9hE4drBY-TrOZB_qKo
-android.app.-$Lambda$w9bG0NLfK6B6UpQKzQS6S1ayAh0
-android.app.-$Lambda$w9bG0NLfK6B6UpQKzQS6S1ayAh0$1
-android.app.ActionBar
-android.app.ActionBar$LayoutParams
-android.app.Activity
-android.app.Activity$HostCallbacks
-android.app.ActivityManager
-android.app.ActivityManager$1
-android.app.ActivityManager$AppTask
-android.app.ActivityManager$MemoryInfo
-android.app.ActivityManager$MemoryInfo$1
-android.app.ActivityManager$OnUidImportanceListener
-android.app.ActivityManager$ProcessErrorStateInfo
-android.app.ActivityManager$RecentTaskInfo
-android.app.ActivityManager$RecentTaskInfo$1
-android.app.ActivityManager$RunningAppProcessInfo
-android.app.ActivityManager$RunningAppProcessInfo$1
-android.app.ActivityManager$RunningServiceInfo
-android.app.ActivityManager$RunningServiceInfo$1
-android.app.ActivityManager$RunningTaskInfo
-android.app.ActivityManager$RunningTaskInfo$1
-android.app.ActivityManager$StackId
-android.app.ActivityManager$StackInfo
-android.app.ActivityManager$StackInfo$1
-android.app.ActivityManager$TaskDescription
-android.app.ActivityManager$TaskDescription$1
-android.app.ActivityManager$TaskSnapshot
-android.app.ActivityManager$TaskSnapshot$1
-android.app.ActivityManager$TaskThumbnail
-android.app.ActivityManager$TaskThumbnailInfo
-android.app.ActivityManager$TaskThumbnailInfo$1
-android.app.ActivityManager$UidObserver
-android.app.ActivityManagerInternal
-android.app.ActivityManagerInternal$SleepToken
-android.app.ActivityOptions
-android.app.ActivityOptions$1
-android.app.ActivityOptions$1$1
-android.app.ActivityOptions$OnAnimationFinishedListener
-android.app.ActivityOptions$OnAnimationStartedListener
-android.app.ActivityThread
-android.app.ActivityThread$1
-android.app.ActivityThread$2
-android.app.ActivityThread$ActivityClientRecord
-android.app.ActivityThread$ActivityConfigChangeData
-android.app.ActivityThread$AppBindData
-android.app.ActivityThread$ApplicationThread
-android.app.ActivityThread$BindServiceData
-android.app.ActivityThread$ContextCleanupInfo
-android.app.ActivityThread$CreateServiceData
-android.app.ActivityThread$DropBoxReporter
-android.app.ActivityThread$EventLoggingReporter
-android.app.ActivityThread$GcIdler
-android.app.ActivityThread$H
-android.app.ActivityThread$Idler
-android.app.ActivityThread$NewIntentData
-android.app.ActivityThread$Profiler
-android.app.ActivityThread$ProviderClientRecord
-android.app.ActivityThread$ProviderKey
-android.app.ActivityThread$ProviderRefCount
-android.app.ActivityThread$ReceiverData
-android.app.ActivityThread$RequestAssistContextExtras
-android.app.ActivityThread$ResultData
-android.app.ActivityThread$ServiceArgsData
-android.app.ActivityThread$StopInfo
-android.app.ActivityTransitionCoordinator
-android.app.ActivityTransitionCoordinator$ContinueTransitionListener
-android.app.ActivityTransitionCoordinator$FixedEpicenterCallback
-android.app.ActivityTransitionCoordinator$GhostViewListeners
-android.app.ActivityTransitionCoordinator$SharedElementOriginalState
-android.app.ActivityTransitionState
-android.app.AlarmManager$AlarmClockInfo
-android.app.AlarmManager$ListenerWrapper
-android.app.AlarmManager$OnAlarmListener
-android.app.AlertDialog
-android.app.AlertDialog$Builder
-android.app.AppGlobals
-android.app.AppOpsManager
-android.app.AppOpsManager$1
-android.app.AppOpsManager$OnOpChangedInternalListener
-android.app.AppOpsManager$OnOpChangedListener
-android.app.AppOpsManager$OpEntry
-android.app.AppOpsManager$OpEntry$1
-android.app.AppOpsManager$PackageOps
-android.app.AppOpsManager$PackageOps$1
-android.app.Application
-android.app.Application$ActivityLifecycleCallbacks
-android.app.ApplicationErrorReport
-android.app.ApplicationErrorReport$AnrInfo
-android.app.ApplicationErrorReport$CrashInfo
-android.app.ApplicationErrorReport$ParcelableCrashInfo
-android.app.ApplicationErrorReport$ParcelableCrashInfo$1
-android.app.ApplicationLoaders
-android.app.ApplicationPackageManager
-android.app.ApplicationPackageManager$MoveCallbackDelegate
-android.app.ApplicationPackageManager$OnPermissionsChangeListenerDelegate
-android.app.ApplicationPackageManager$ResourceName
-android.app.AutomaticZenRule
-android.app.BackStackRecord
-android.app.BackStackRecord$Op
-android.app.BroadcastOptions
-android.app.ContentProviderHolder
-android.app.ContentProviderHolder$1
-android.app.ContextImpl
-android.app.ContextImpl$ApplicationContentResolver
-android.app.DatePickerDialog$OnDateSetListener
-android.app.DexLoadReporter
-android.app.Dialog
-android.app.Dialog$ListenersHandler
-android.app.DialogFragment
-android.app.DownloadManager
-android.app.DownloadManager$CursorTranslator
-android.app.DownloadManager$Query
-android.app.EnterTransitionCoordinator
-android.app.EnterTransitionCoordinator$1
-android.app.EnterTransitionCoordinator$2
-android.app.EnterTransitionCoordinator$3
-android.app.EnterTransitionCoordinator$4
-android.app.EnterTransitionCoordinator$5
-android.app.EnterTransitionCoordinator$6
-android.app.ExitTransitionCoordinator
-android.app.ExitTransitionCoordinator$10
-android.app.ExitTransitionCoordinator$3
-android.app.ExitTransitionCoordinator$9
-android.app.Fragment
-android.app.Fragment$1
-android.app.Fragment$AnimationInfo
-android.app.FragmentContainer
-android.app.FragmentController
-android.app.FragmentHostCallback
-android.app.FragmentManager
-android.app.FragmentManager$BackStackEntry
-android.app.FragmentManager$FragmentLifecycleCallbacks
-android.app.FragmentManager$OnBackStackChangedListener
-android.app.FragmentManagerImpl
-android.app.FragmentManagerImpl$1
-android.app.FragmentManagerImpl$AnimateOnHWLayerIfNeededListener
-android.app.FragmentManagerImpl$OpGenerator
-android.app.FragmentManagerState
-android.app.FragmentManagerState$1
-android.app.FragmentState
-android.app.FragmentState$1
-android.app.FragmentTransaction
-android.app.FragmentTransition
-android.app.FragmentTransition$FragmentContainerTransition
-android.app.IActivityContainer
-android.app.IActivityContainer$Stub
-android.app.IActivityContainerCallback
-android.app.IActivityController
-android.app.IActivityManager
-android.app.IActivityManager$Stub
-android.app.IActivityManager$Stub$Proxy
-android.app.IAlarmCompleteListener
-android.app.IAlarmCompleteListener$Stub
-android.app.IAlarmCompleteListener$Stub$Proxy
-android.app.IAlarmListener
-android.app.IAlarmListener$Stub
-android.app.IAlarmListener$Stub$Proxy
-android.app.IAlarmManager
-android.app.IAlarmManager$Stub
-android.app.IAlarmManager$Stub$Proxy
-android.app.IAppTask
-android.app.IAppTask$Stub
-android.app.IAppTask$Stub$Proxy
-android.app.IApplicationThread
-android.app.IApplicationThread$Stub
-android.app.IApplicationThread$Stub$Proxy
-android.app.IInstantAppResolver
-android.app.IInstantAppResolver$Stub
-android.app.IInstantAppResolver$Stub$Proxy
-android.app.IInstrumentationWatcher
-android.app.IInstrumentationWatcher$Stub
-android.app.INotificationManager
-android.app.INotificationManager$Stub
-android.app.INotificationManager$Stub$Proxy
-android.app.IProcessObserver
-android.app.IProcessObserver$Stub
-android.app.IProcessObserver$Stub$Proxy
-android.app.ISearchManager
-android.app.ISearchManager$Stub
-android.app.ISearchManager$Stub$Proxy
-android.app.IServiceConnection
-android.app.IServiceConnection$Stub
-android.app.IServiceConnection$Stub$Proxy
-android.app.IStopUserCallback
-android.app.ITaskStackListener
-android.app.ITaskStackListener$Stub
-android.app.ITaskStackListener$Stub$Proxy
-android.app.ITransientNotification
-android.app.ITransientNotification$Stub
-android.app.ITransientNotification$Stub$Proxy
-android.app.IUiAutomationConnection
-android.app.IUiAutomationConnection$Stub
-android.app.IUiModeManager
-android.app.IUiModeManager$Stub
-android.app.IUiModeManager$Stub$Proxy
-android.app.IUidObserver
-android.app.IUidObserver$Stub
-android.app.IUidObserver$Stub$Proxy
-android.app.IUserSwitchObserver
-android.app.IUserSwitchObserver$Stub
-android.app.IUserSwitchObserver$Stub$Proxy
-android.app.IWallpaperManager
-android.app.IWallpaperManager$Stub
-android.app.IWallpaperManager$Stub$Proxy
-android.app.IWallpaperManagerCallback
-android.app.IWallpaperManagerCallback$Stub
-android.app.IWallpaperManagerCallback$Stub$Proxy
-android.app.InstantAppResolverService
-android.app.InstantAppResolverService$1
-android.app.InstantAppResolverService$InstantAppResolutionCallback
-android.app.InstantAppResolverService$ServiceHandler
-android.app.Instrumentation
-android.app.IntentReceiverLeaked
-android.app.IntentService
-android.app.IntentService$ServiceHandler
-android.app.JobSchedulerImpl
-android.app.KeyguardManager
-android.app.ListActivity
-android.app.ListFragment
-android.app.ListFragment$1
-android.app.ListFragment$2
-android.app.LoadedApk
-android.app.LoadedApk$ReceiverDispatcher
-android.app.LoadedApk$ReceiverDispatcher$Args
-android.app.LoadedApk$ReceiverDispatcher$InnerReceiver
-android.app.LoadedApk$ServiceDispatcher
-android.app.LoadedApk$ServiceDispatcher$ConnectionInfo
-android.app.LoadedApk$ServiceDispatcher$DeathMonitor
-android.app.LoadedApk$ServiceDispatcher$InnerConnection
-android.app.LoadedApk$ServiceDispatcher$RunConnection
-android.app.LoadedApk$WarningContextClassLoader
-android.app.LoaderManager
-android.app.LoaderManager$LoaderCallbacks
-android.app.LoaderManagerImpl
-android.app.LoaderManagerImpl$LoaderInfo
-android.app.NativeActivity
-android.app.Notification
-android.app.Notification$1
-android.app.Notification$Action
-android.app.Notification$Action$1
-android.app.Notification$Action$Builder
-android.app.Notification$BigPictureStyle
-android.app.Notification$BigTextStyle
-android.app.Notification$Builder
-android.app.Notification$BuilderRemoteViews
-android.app.Notification$DecoratedCustomViewStyle
-android.app.Notification$DecoratedMediaCustomViewStyle
-android.app.Notification$Extender
-android.app.Notification$InboxStyle
-android.app.Notification$MediaStyle
-android.app.Notification$MessagingStyle
-android.app.Notification$StandardTemplateParams
-android.app.Notification$Style
-android.app.Notification$TvExtender
-android.app.Notification$WearableExtender
-android.app.NotificationChannel
-android.app.NotificationChannel$1
-android.app.NotificationChannelGroup
-android.app.NotificationChannelGroup$1
-android.app.NotificationManager
-android.app.NotificationManager$Policy
-android.app.NotificationManager$Policy$1
-android.app.OnActivityPausedListener
-android.app.PackageInstallObserver
-android.app.PackageInstallObserver$1
-android.app.PendingIntent
-android.app.PendingIntent$1
-android.app.PendingIntent$CanceledException
-android.app.PendingIntent$FinishedDispatcher
-android.app.PendingIntent$OnFinished
-android.app.PendingIntent$OnMarshaledListener
-android.app.PictureInPictureParams
-android.app.PictureInPictureParams$1
-android.app.PictureInPictureParams$Builder
-android.app.ProfilerInfo
-android.app.ProgressDialog
-android.app.QueuedWork
-android.app.QueuedWork$QueuedWorkHandler
-android.app.ReceiverRestrictedContext
-android.app.RemoteAction
-android.app.RemoteAction$1
-android.app.RemoteInput
-android.app.RemoteInput$1
-android.app.ResourcesManager
-android.app.ResourcesManager$1
-android.app.ResourcesManager$ActivityResources
-android.app.ResultInfo
-android.app.ResultInfo$1
-android.app.SearchableInfo
-android.app.SearchableInfo$1
-android.app.Service
-android.app.ServiceConnectionLeaked
-android.app.ServiceStartArgs
-android.app.ServiceStartArgs$1
-android.app.SharedElementCallback
-android.app.SharedElementCallback$1
-android.app.SharedElementCallback$OnSharedElementsReadyListener
-android.app.SharedPreferencesImpl
-android.app.SharedPreferencesImpl$1
-android.app.SharedPreferencesImpl$2
-android.app.SharedPreferencesImpl$EditorImpl
-android.app.SharedPreferencesImpl$EditorImpl$1
-android.app.SharedPreferencesImpl$EditorImpl$2
-android.app.SharedPreferencesImpl$EditorImpl$3
-android.app.SharedPreferencesImpl$MemoryCommitResult
-android.app.StatusBarManager
-android.app.SynchronousUserSwitchObserver
-android.app.SystemServiceRegistry
-android.app.SystemServiceRegistry$1
-android.app.SystemServiceRegistry$10
-android.app.SystemServiceRegistry$11
-android.app.SystemServiceRegistry$12
-android.app.SystemServiceRegistry$13
-android.app.SystemServiceRegistry$14
-android.app.SystemServiceRegistry$15
-android.app.SystemServiceRegistry$16
-android.app.SystemServiceRegistry$17
-android.app.SystemServiceRegistry$18
-android.app.SystemServiceRegistry$19
-android.app.SystemServiceRegistry$2
-android.app.SystemServiceRegistry$20
-android.app.SystemServiceRegistry$21
-android.app.SystemServiceRegistry$22
-android.app.SystemServiceRegistry$23
-android.app.SystemServiceRegistry$24
-android.app.SystemServiceRegistry$25
-android.app.SystemServiceRegistry$26
-android.app.SystemServiceRegistry$27
-android.app.SystemServiceRegistry$28
-android.app.SystemServiceRegistry$29
-android.app.SystemServiceRegistry$3
-android.app.SystemServiceRegistry$30
-android.app.SystemServiceRegistry$31
-android.app.SystemServiceRegistry$32
-android.app.SystemServiceRegistry$33
-android.app.SystemServiceRegistry$34
-android.app.SystemServiceRegistry$35
-android.app.SystemServiceRegistry$36
-android.app.SystemServiceRegistry$37
-android.app.SystemServiceRegistry$38
-android.app.SystemServiceRegistry$39
-android.app.SystemServiceRegistry$4
-android.app.SystemServiceRegistry$40
-android.app.SystemServiceRegistry$41
-android.app.SystemServiceRegistry$42
-android.app.SystemServiceRegistry$43
-android.app.SystemServiceRegistry$44
-android.app.SystemServiceRegistry$45
-android.app.SystemServiceRegistry$46
-android.app.SystemServiceRegistry$47
-android.app.SystemServiceRegistry$48
-android.app.SystemServiceRegistry$49
-android.app.SystemServiceRegistry$5
-android.app.SystemServiceRegistry$50
-android.app.SystemServiceRegistry$51
-android.app.SystemServiceRegistry$52
-android.app.SystemServiceRegistry$53
-android.app.SystemServiceRegistry$54
-android.app.SystemServiceRegistry$55
-android.app.SystemServiceRegistry$56
-android.app.SystemServiceRegistry$57
-android.app.SystemServiceRegistry$58
-android.app.SystemServiceRegistry$59
-android.app.SystemServiceRegistry$6
-android.app.SystemServiceRegistry$60
-android.app.SystemServiceRegistry$61
-android.app.SystemServiceRegistry$62
-android.app.SystemServiceRegistry$63
-android.app.SystemServiceRegistry$64
-android.app.SystemServiceRegistry$65
-android.app.SystemServiceRegistry$66
-android.app.SystemServiceRegistry$67
-android.app.SystemServiceRegistry$68
-android.app.SystemServiceRegistry$69
-android.app.SystemServiceRegistry$7
-android.app.SystemServiceRegistry$70
-android.app.SystemServiceRegistry$71
-android.app.SystemServiceRegistry$72
-android.app.SystemServiceRegistry$73
-android.app.SystemServiceRegistry$74
-android.app.SystemServiceRegistry$75
-android.app.SystemServiceRegistry$76
-android.app.SystemServiceRegistry$77
-android.app.SystemServiceRegistry$78
-android.app.SystemServiceRegistry$79
-android.app.SystemServiceRegistry$8
-android.app.SystemServiceRegistry$80
-android.app.SystemServiceRegistry$81
-android.app.SystemServiceRegistry$82
-android.app.SystemServiceRegistry$9
-android.app.SystemServiceRegistry$CachedServiceFetcher
-android.app.SystemServiceRegistry$ServiceFetcher
-android.app.SystemServiceRegistry$StaticApplicationContextServiceFetcher
-android.app.SystemServiceRegistry$StaticServiceFetcher
-android.app.TaskStackListener
-android.app.TimePickerDialog$OnTimeSetListener
-android.app.UiModeManager
-android.app.UserSwitchObserver
-android.app.Vr2dDisplayProperties
-android.app.VrManager
-android.app.WaitResult
-android.app.WallpaperInfo
-android.app.WallpaperManager
-android.app.WallpaperManager$Globals
-android.app.admin.DeviceAdminInfo
-android.app.admin.DeviceAdminInfo$1
-android.app.admin.DeviceAdminInfo$PolicyInfo
-android.app.admin.DevicePolicyManager
-android.app.admin.DevicePolicyManagerInternal
-android.app.admin.DevicePolicyManagerInternal$OnCrossProfileWidgetProvidersChangeListener
-android.app.admin.IDevicePolicyManager
-android.app.admin.IDevicePolicyManager$Stub
-android.app.admin.IDevicePolicyManager$Stub$Proxy
-android.app.admin.PasswordMetrics
-android.app.admin.PasswordMetrics$1
-android.app.admin.SecurityLog
-android.app.admin.SecurityLog$SecurityEvent
-android.app.admin.SecurityLog$SecurityEvent$1
-android.app.admin.SystemUpdateInfo
-android.app.admin.SystemUpdateInfo$1
-android.app.admin.SystemUpdatePolicy
-android.app.admin.SystemUpdatePolicy$1
-android.app.assist.AssistContent
-android.app.assist.AssistStructure
-android.app.assist.AssistStructure$1
-android.app.assist.AssistStructure$AutofillOverlay
-android.app.assist.AssistStructure$ParcelTransferReader
-android.app.assist.AssistStructure$ParcelTransferWriter
-android.app.assist.AssistStructure$SendChannel
-android.app.assist.AssistStructure$ViewNode
-android.app.assist.AssistStructure$ViewNodeBuilder
-android.app.assist.AssistStructure$ViewNodeText
-android.app.assist.AssistStructure$ViewStackEntry
-android.app.assist.AssistStructure$WindowNode
-android.app.backup.BackupAgent
-android.app.backup.BackupAgentHelper
-android.app.backup.BackupDataInput
-android.app.backup.BackupDataInput$EntityHeader
-android.app.backup.BackupDataOutput
-android.app.backup.BackupHelper
-android.app.backup.BackupHelperDispatcher
-android.app.backup.BackupHelperDispatcher$Header
-android.app.backup.BackupManager
-android.app.backup.BackupTransport
-android.app.backup.BackupTransport$TransportImpl
-android.app.backup.FileBackupHelperBase
-android.app.backup.FullBackup
-android.app.backup.FullBackupDataOutput
-android.app.backup.IBackupManager
-android.app.backup.IBackupManager$Stub
-android.app.backup.IBackupManager$Stub$Proxy
-android.app.backup.IBackupManagerMonitor
-android.app.backup.IBackupObserver
-android.app.backup.IFullBackupRestoreObserver
-android.app.backup.IRestoreSession
-android.app.backup.ISelectBackupTransportCallback
-android.app.backup.RestoreDescription
-android.app.backup.RestoreSet
-android.app.job.IJobCallback
-android.app.job.IJobCallback$Stub
-android.app.job.IJobCallback$Stub$Proxy
-android.app.job.IJobScheduler
-android.app.job.IJobScheduler$Stub
-android.app.job.IJobScheduler$Stub$Proxy
-android.app.job.IJobService
-android.app.job.IJobService$Stub
-android.app.job.IJobService$Stub$Proxy
-android.app.job.JobInfo
-android.app.job.JobInfo$1
-android.app.job.JobInfo$Builder
-android.app.job.JobInfo$TriggerContentUri
-android.app.job.JobInfo$TriggerContentUri$1
-android.app.job.JobParameters
-android.app.job.JobParameters$1
-android.app.job.JobScheduler
-android.app.job.JobService
-android.app.job.JobService$1
-android.app.job.JobServiceEngine
-android.app.job.JobServiceEngine$JobHandler
-android.app.job.JobServiceEngine$JobInterface
-android.app.job.JobWorkItem
-android.app.trust.IStrongAuthTracker
-android.app.trust.IStrongAuthTracker$Stub
-android.app.trust.IStrongAuthTracker$Stub$Proxy
-android.app.trust.ITrustListener
-android.app.trust.ITrustListener$Stub
-android.app.trust.ITrustListener$Stub$Proxy
-android.app.trust.ITrustManager
-android.app.trust.ITrustManager$Stub
-android.app.trust.ITrustManager$Stub$Proxy
-android.app.trust.TrustManager
-android.app.trust.TrustManager$1
-android.app.trust.TrustManager$2
-android.app.trust.TrustManager$TrustListener
-android.app.usage.CacheQuotaHint
-android.app.usage.CacheQuotaHint$1
-android.app.usage.CacheQuotaHint$Builder
-android.app.usage.ConfigurationStats
-android.app.usage.ConfigurationStats$1
-android.app.usage.ExternalStorageStats
-android.app.usage.ICacheQuotaService
-android.app.usage.ICacheQuotaService$Stub
-android.app.usage.ICacheQuotaService$Stub$Proxy
-android.app.usage.IStorageStatsManager
-android.app.usage.IStorageStatsManager$Stub
-android.app.usage.IUsageStatsManager
-android.app.usage.IUsageStatsManager$Stub
-android.app.usage.IUsageStatsManager$Stub$Proxy
-android.app.usage.NetworkStatsManager
-android.app.usage.StorageStats
-android.app.usage.StorageStatsManager
-android.app.usage.TimeSparseArray
-android.app.usage.UsageEvents
-android.app.usage.UsageEvents$1
-android.app.usage.UsageEvents$Event
-android.app.usage.UsageStats
-android.app.usage.UsageStats$1
-android.app.usage.UsageStatsManager
-android.app.usage.UsageStatsManagerInternal
-android.app.usage.UsageStatsManagerInternal$AppIdleStateChangeListener
-android.appwidget.AppWidgetHost
-android.appwidget.AppWidgetHost$Callbacks
-android.appwidget.AppWidgetHost$UpdateHandler
-android.appwidget.AppWidgetHostView
-android.appwidget.AppWidgetHostView$1
-android.appwidget.AppWidgetHostView$ParcelableSparseArray
-android.appwidget.AppWidgetHostView$ParcelableSparseArray$1
-android.appwidget.AppWidgetManager
-android.appwidget.AppWidgetProvider
-android.appwidget.AppWidgetProviderInfo
-android.appwidget.AppWidgetProviderInfo$1
-android.appwidget.PendingHostUpdate
-android.appwidget.PendingHostUpdate$1
-android.bluetooth.BluetoothA2dp
-android.bluetooth.BluetoothA2dp$1
-android.bluetooth.BluetoothA2dp$2
-android.bluetooth.BluetoothActivityEnergyInfo
-android.bluetooth.BluetoothAdapter
-android.bluetooth.BluetoothAdapter$1
-android.bluetooth.BluetoothAdapter$BluetoothStateChangeCallback
-android.bluetooth.BluetoothCodecConfig
-android.bluetooth.BluetoothCodecConfig$1
-android.bluetooth.BluetoothCodecStatus
-android.bluetooth.BluetoothDevice
-android.bluetooth.BluetoothDevice$1
-android.bluetooth.BluetoothDevice$2
-android.bluetooth.BluetoothGatt
-android.bluetooth.BluetoothGattCallback
-android.bluetooth.BluetoothGattCharacteristic
-android.bluetooth.BluetoothGattDescriptor
-android.bluetooth.BluetoothGattService
-android.bluetooth.BluetoothHeadset
-android.bluetooth.BluetoothHeadset$1
-android.bluetooth.BluetoothHeadset$2
-android.bluetooth.BluetoothHeadset$3
-android.bluetooth.BluetoothHealthAppConfiguration
-android.bluetooth.BluetoothHidHost
-android.bluetooth.BluetoothHidHost$1
-android.bluetooth.BluetoothHidHost$2
-android.bluetooth.BluetoothInputStream
-android.bluetooth.BluetoothManager
-android.bluetooth.BluetoothMap
-android.bluetooth.BluetoothMap$1
-android.bluetooth.BluetoothMap$2
-android.bluetooth.BluetoothOutputStream
-android.bluetooth.BluetoothPan
-android.bluetooth.BluetoothPan$1
-android.bluetooth.BluetoothPan$2
-android.bluetooth.BluetoothPbap
-android.bluetooth.BluetoothPbap$1
-android.bluetooth.BluetoothPbap$2
-android.bluetooth.BluetoothPbap$ServiceListener
-android.bluetooth.BluetoothProfile
-android.bluetooth.BluetoothProfile$ServiceListener
-android.bluetooth.BluetoothServerSocket
-android.bluetooth.BluetoothSocket
-android.bluetooth.BluetoothSocket$SocketState
-android.bluetooth.BluetoothUuid
-android.bluetooth.IBluetooth
-android.bluetooth.IBluetooth$Stub
-android.bluetooth.IBluetooth$Stub$Proxy
-android.bluetooth.IBluetoothA2dp
-android.bluetooth.IBluetoothA2dp$Stub
-android.bluetooth.IBluetoothA2dp$Stub$Proxy
-android.bluetooth.IBluetoothCallback
-android.bluetooth.IBluetoothCallback$Stub
-android.bluetooth.IBluetoothCallback$Stub$Proxy
-android.bluetooth.IBluetoothGatt
-android.bluetooth.IBluetoothGatt$Stub
-android.bluetooth.IBluetoothGatt$Stub$Proxy
-android.bluetooth.IBluetoothGattCallback
-android.bluetooth.IBluetoothGattServerCallback
-android.bluetooth.IBluetoothHeadset
-android.bluetooth.IBluetoothHeadset$Stub
-android.bluetooth.IBluetoothHeadset$Stub$Proxy
-android.bluetooth.IBluetoothHeadsetPhone
-android.bluetooth.IBluetoothHeadsetPhone$Stub
-android.bluetooth.IBluetoothHeadsetPhone$Stub$Proxy
-android.bluetooth.IBluetoothHealth
-android.bluetooth.IBluetoothHealth$Stub
-android.bluetooth.IBluetoothHealthCallback
-android.bluetooth.IBluetoothHidHost
-android.bluetooth.IBluetoothHidHost$Stub
-android.bluetooth.IBluetoothHidHost$Stub$Proxy
-android.bluetooth.IBluetoothManager
-android.bluetooth.IBluetoothManager$Stub
-android.bluetooth.IBluetoothManager$Stub$Proxy
-android.bluetooth.IBluetoothManagerCallback
-android.bluetooth.IBluetoothManagerCallback$Stub
-android.bluetooth.IBluetoothManagerCallback$Stub$Proxy
-android.bluetooth.IBluetoothMap
-android.bluetooth.IBluetoothMap$Stub
-android.bluetooth.IBluetoothMap$Stub$Proxy
-android.bluetooth.IBluetoothPan
-android.bluetooth.IBluetoothPan$Stub
-android.bluetooth.IBluetoothPan$Stub$Proxy
-android.bluetooth.IBluetoothPbap
-android.bluetooth.IBluetoothPbap$Stub
-android.bluetooth.IBluetoothPbap$Stub$Proxy
-android.bluetooth.IBluetoothProfileServiceConnection
-android.bluetooth.IBluetoothProfileServiceConnection$Stub
-android.bluetooth.IBluetoothProfileServiceConnection$Stub$Proxy
-android.bluetooth.IBluetoothSap
-android.bluetooth.IBluetoothSap$Stub
-android.bluetooth.IBluetoothStateChangeCallback
-android.bluetooth.IBluetoothStateChangeCallback$Stub
-android.bluetooth.IBluetoothStateChangeCallback$Stub$Proxy
-android.bluetooth.OobData
-android.bluetooth.UidTraffic
-android.bluetooth.UidTraffic$1
-android.bluetooth.le.AdvertiseData
-android.bluetooth.le.AdvertisingSetParameters
-android.bluetooth.le.BluetoothLeAdvertiser
-android.bluetooth.le.BluetoothLeScanner
-android.bluetooth.le.BluetoothLeScanner$BleScanCallbackWrapper
-android.bluetooth.le.BluetoothLeScanner$BleScanCallbackWrapper$1
-android.bluetooth.le.BluetoothLeUtils
-android.bluetooth.le.IAdvertisingSetCallback
-android.bluetooth.le.IPeriodicAdvertisingCallback
-android.bluetooth.le.IScannerCallback
-android.bluetooth.le.IScannerCallback$Stub
-android.bluetooth.le.IScannerCallback$Stub$Proxy
-android.bluetooth.le.PeriodicAdvertisingParameters
-android.bluetooth.le.ScanCallback
-android.bluetooth.le.ScanFilter
-android.bluetooth.le.ScanFilter$1
-android.bluetooth.le.ScanFilter$Builder
-android.bluetooth.le.ScanRecord
-android.bluetooth.le.ScanResult
-android.bluetooth.le.ScanResult$1
-android.bluetooth.le.ScanSettings
-android.bluetooth.le.ScanSettings$1
-android.bluetooth.le.ScanSettings$Builder
-android.companion.AssociationRequest
-android.companion.CompanionDeviceManager
-android.companion.ICompanionDeviceManager
-android.companion.ICompanionDeviceManager$Stub
-android.companion.IFindDeviceCallback
-android.content.AbstractThreadedSyncAdapter
-android.content.AbstractThreadedSyncAdapter$ISyncAdapterImpl
-android.content.AbstractThreadedSyncAdapter$SyncThread
-android.content.ActivityNotFoundException
-android.content.AsyncQueryHandler
-android.content.AsyncQueryHandler$WorkerArgs
-android.content.AsyncQueryHandler$WorkerHandler
-android.content.AsyncTaskLoader
-android.content.AsyncTaskLoader$LoadTask
-android.content.BroadcastReceiver
-android.content.BroadcastReceiver$PendingResult
-android.content.BroadcastReceiver$PendingResult$1
-android.content.ClipData
-android.content.ClipData$1
-android.content.ClipData$Item
-android.content.ClipDescription
-android.content.ClipDescription$1
-android.content.ClipboardManager
-android.content.ClipboardManager$1
-android.content.ClipboardManager$2
-android.content.ClipboardManager$OnPrimaryClipChangedListener
-android.content.ComponentCallbacks
-android.content.ComponentCallbacks2
-android.content.ComponentName
-android.content.ComponentName$1
-android.content.ContentProvider
-android.content.ContentProvider$PipeDataWriter
-android.content.ContentProvider$Transport
-android.content.ContentProviderClient
-android.content.ContentProviderClient$CursorWrapperInner
-android.content.ContentProviderNative
-android.content.ContentProviderOperation
-android.content.ContentProviderOperation$1
-android.content.ContentProviderOperation$Builder
-android.content.ContentProviderProxy
-android.content.ContentProviderResult
-android.content.ContentProviderResult$1
-android.content.ContentQueryMap
-android.content.ContentResolver
-android.content.ContentResolver$1
-android.content.ContentResolver$CursorWrapperInner
-android.content.ContentResolver$ParcelFileDescriptorInner
-android.content.ContentUris
-android.content.ContentValues
-android.content.ContentValues$1
-android.content.Context
-android.content.ContextWrapper
-android.content.CursorLoader
-android.content.DialogInterface
-android.content.DialogInterface$OnCancelListener
-android.content.DialogInterface$OnClickListener
-android.content.DialogInterface$OnDismissListener
-android.content.DialogInterface$OnKeyListener
-android.content.DialogInterface$OnMultiChoiceClickListener
-android.content.DialogInterface$OnShowListener
-android.content.IClipboard
-android.content.IClipboard$Stub
-android.content.IClipboard$Stub$Proxy
-android.content.IContentProvider
-android.content.IContentService
-android.content.IContentService$Stub
-android.content.IContentService$Stub$Proxy
-android.content.IIntentReceiver
-android.content.IIntentReceiver$Stub
-android.content.IIntentReceiver$Stub$Proxy
-android.content.IIntentSender
-android.content.IIntentSender$Stub
-android.content.IIntentSender$Stub$Proxy
-android.content.IOnPrimaryClipChangedListener
-android.content.IOnPrimaryClipChangedListener$Stub
-android.content.IRestrictionsManager
-android.content.IRestrictionsManager$Stub
-android.content.IRestrictionsManager$Stub$Proxy
-android.content.ISyncAdapter
-android.content.ISyncAdapter$Stub
-android.content.ISyncAdapter$Stub$Proxy
-android.content.ISyncContext
-android.content.ISyncContext$Stub
-android.content.ISyncContext$Stub$Proxy
-android.content.ISyncStatusObserver
-android.content.ISyncStatusObserver$Stub
-android.content.ISyncStatusObserver$Stub$Proxy
-android.content.Intent
-android.content.Intent$1
-android.content.Intent$FilterComparison
-android.content.Intent$ShortcutIconResource
-android.content.Intent$ShortcutIconResource$1
-android.content.IntentFilter
-android.content.IntentFilter$1
-android.content.IntentFilter$AuthorityEntry
-android.content.IntentFilter$MalformedMimeTypeException
-android.content.IntentSender
-android.content.IntentSender$1
-android.content.IntentSender$SendIntentException
-android.content.Loader
-android.content.Loader$ForceLoadContentObserver
-android.content.Loader$OnLoadCanceledListener
-android.content.Loader$OnLoadCompleteListener
-android.content.OperationApplicationException
-android.content.PeriodicSync
-android.content.PeriodicSync$1
-android.content.ReceiverCallNotAllowedException
-android.content.RestrictionEntry
-android.content.RestrictionEntry$1
-android.content.RestrictionsManager
-android.content.SearchRecentSuggestionsProvider
-android.content.SearchRecentSuggestionsProvider$DatabaseHelper
-android.content.ServiceConnection
-android.content.SharedPreferences
-android.content.SharedPreferences$Editor
-android.content.SharedPreferences$OnSharedPreferenceChangeListener
-android.content.SyncAdapterType
-android.content.SyncAdapterType$1
-android.content.SyncAdaptersCache
-android.content.SyncAdaptersCache$MySerializer
-android.content.SyncContext
-android.content.SyncInfo
-android.content.SyncInfo$1
-android.content.SyncRequest
-android.content.SyncRequest$1
-android.content.SyncRequest$Builder
-android.content.SyncResult
-android.content.SyncResult$1
-android.content.SyncStats
-android.content.SyncStats$1
-android.content.SyncStatusInfo
-android.content.SyncStatusInfo$1
-android.content.SyncStatusObserver
-android.content.UndoManager
-android.content.UndoManager$UndoState
-android.content.UndoOperation
-android.content.UndoOwner
-android.content.UriMatcher
-android.content.om.IOverlayManager
-android.content.om.IOverlayManager$Stub
-android.content.om.OverlayInfo
-android.content.pm.ActivityInfo
-android.content.pm.ActivityInfo$1
-android.content.pm.ActivityInfo$WindowLayout
-android.content.pm.ApplicationInfo
-android.content.pm.ApplicationInfo$1
-android.content.pm.AuxiliaryResolveInfo
-android.content.pm.BaseParceledListSlice
-android.content.pm.BaseParceledListSlice$1
-android.content.pm.ChangedPackages
-android.content.pm.ComponentInfo
-android.content.pm.ConfigurationInfo
-android.content.pm.ConfigurationInfo$1
-android.content.pm.FallbackCategoryProvider
-android.content.pm.FeatureGroupInfo
-android.content.pm.FeatureGroupInfo$1
-android.content.pm.FeatureInfo
-android.content.pm.FeatureInfo$1
-android.content.pm.ILauncherApps
-android.content.pm.ILauncherApps$Stub
-android.content.pm.ILauncherApps$Stub$Proxy
-android.content.pm.IOnAppsChangedListener
-android.content.pm.IOnAppsChangedListener$Stub
-android.content.pm.IOnAppsChangedListener$Stub$Proxy
-android.content.pm.IOnPermissionsChangeListener
-android.content.pm.IOnPermissionsChangeListener$Stub
-android.content.pm.IOnPermissionsChangeListener$Stub$Proxy
-android.content.pm.IOtaDexopt
-android.content.pm.IOtaDexopt$Stub
-android.content.pm.IPackageDataObserver
-android.content.pm.IPackageDataObserver$Stub
-android.content.pm.IPackageDataObserver$Stub$Proxy
-android.content.pm.IPackageDeleteObserver
-android.content.pm.IPackageDeleteObserver2
-android.content.pm.IPackageInstallObserver
-android.content.pm.IPackageInstallObserver2
-android.content.pm.IPackageInstallObserver2$Stub
-android.content.pm.IPackageInstaller
-android.content.pm.IPackageInstaller$Stub
-android.content.pm.IPackageInstaller$Stub$Proxy
-android.content.pm.IPackageInstallerCallback
-android.content.pm.IPackageInstallerCallback$Stub
-android.content.pm.IPackageInstallerCallback$Stub$Proxy
-android.content.pm.IPackageInstallerSession
-android.content.pm.IPackageInstallerSession$Stub
-android.content.pm.IPackageInstallerSession$Stub$Proxy
-android.content.pm.IPackageManager
-android.content.pm.IPackageManager$Stub
-android.content.pm.IPackageManager$Stub$Proxy
-android.content.pm.IPackageMoveObserver
-android.content.pm.IPackageMoveObserver$Stub
-android.content.pm.IPackageMoveObserver$Stub$Proxy
-android.content.pm.IPackageStatsObserver
-android.content.pm.IPackageStatsObserver$Stub
-android.content.pm.IShortcutService
-android.content.pm.IShortcutService$Stub
-android.content.pm.IShortcutService$Stub$Proxy
-android.content.pm.InstantAppRequest
-android.content.pm.InstantAppResolveInfo$InstantAppDigest
-android.content.pm.InstantAppResolveInfo$InstantAppDigest$1
-android.content.pm.InstrumentationInfo
-android.content.pm.InstrumentationInfo$1
-android.content.pm.IntentFilterVerificationInfo
-android.content.pm.IntentFilterVerificationInfo$1
-android.content.pm.KeySet
-android.content.pm.LauncherActivityInfo
-android.content.pm.LauncherApps
-android.content.pm.LauncherApps$1
-android.content.pm.LauncherApps$Callback
-android.content.pm.LauncherApps$CallbackMessageHandler
-android.content.pm.LauncherApps$CallbackMessageHandler$CallbackInfo
-android.content.pm.LauncherApps$ShortcutQuery
-android.content.pm.PackageCleanItem
-android.content.pm.PackageInfo
-android.content.pm.PackageInfo$1
-android.content.pm.PackageInfoLite
-android.content.pm.PackageInfoLite$1
-android.content.pm.PackageInstaller
-android.content.pm.PackageInstaller$Session
-android.content.pm.PackageInstaller$SessionCallback
-android.content.pm.PackageInstaller$SessionCallbackDelegate
-android.content.pm.PackageInstaller$SessionInfo
-android.content.pm.PackageInstaller$SessionInfo$1
-android.content.pm.PackageInstaller$SessionParams
-android.content.pm.PackageInstaller$SessionParams$1
-android.content.pm.PackageItemInfo
-android.content.pm.PackageManager
-android.content.pm.PackageManager$MoveCallback
-android.content.pm.PackageManager$NameNotFoundException
-android.content.pm.PackageManager$OnPermissionsChangedListener
-android.content.pm.PackageManagerInternal
-android.content.pm.PackageManagerInternal$ExternalSourcesPolicy
-android.content.pm.PackageManagerInternal$PackagesProvider
-android.content.pm.PackageManagerInternal$SyncAdapterPackagesProvider
-android.content.pm.PackageParser
-android.content.pm.PackageParser$Activity
-android.content.pm.PackageParser$Activity$1
-android.content.pm.PackageParser$ActivityIntentInfo
-android.content.pm.PackageParser$ApkLite
-android.content.pm.PackageParser$Callback
-android.content.pm.PackageParser$CallbackImpl
-android.content.pm.PackageParser$Component
-android.content.pm.PackageParser$IntentInfo
-android.content.pm.PackageParser$NewPermissionInfo
-android.content.pm.PackageParser$Package
-android.content.pm.PackageParser$Package$1
-android.content.pm.PackageParser$PackageLite
-android.content.pm.PackageParser$PackageParserException
-android.content.pm.PackageParser$ParseComponentArgs
-android.content.pm.PackageParser$ParsePackageItemArgs
-android.content.pm.PackageParser$Permission
-android.content.pm.PackageParser$Permission$1
-android.content.pm.PackageParser$PermissionGroup
-android.content.pm.PackageParser$PermissionGroup$1
-android.content.pm.PackageParser$Provider
-android.content.pm.PackageParser$Provider$1
-android.content.pm.PackageParser$ProviderIntentInfo
-android.content.pm.PackageParser$Service
-android.content.pm.PackageParser$Service$1
-android.content.pm.PackageParser$ServiceIntentInfo
-android.content.pm.PackageParser$SplitNameComparator
-android.content.pm.PackageParser$SplitPermissionInfo
-android.content.pm.PackageStats
-android.content.pm.PackageUserState
-android.content.pm.ParceledListSlice
-android.content.pm.ParceledListSlice$1
-android.content.pm.PathPermission
-android.content.pm.PathPermission$1
-android.content.pm.PermissionGroupInfo
-android.content.pm.PermissionGroupInfo$1
-android.content.pm.PermissionInfo
-android.content.pm.PermissionInfo$1
-android.content.pm.ProviderInfo
-android.content.pm.ProviderInfo$1
-android.content.pm.RegisteredServicesCache
-android.content.pm.RegisteredServicesCache$1
-android.content.pm.RegisteredServicesCache$2
-android.content.pm.RegisteredServicesCache$3
-android.content.pm.RegisteredServicesCache$ServiceInfo
-android.content.pm.RegisteredServicesCache$UserServices
-android.content.pm.RegisteredServicesCacheListener
-android.content.pm.ResolveInfo
-android.content.pm.ResolveInfo$1
-android.content.pm.SELinuxUtil
-android.content.pm.ServiceInfo
-android.content.pm.ServiceInfo$1
-android.content.pm.SharedLibraryInfo
-android.content.pm.SharedLibraryInfo$1
-android.content.pm.ShortcutInfo
-android.content.pm.ShortcutInfo$1
-android.content.pm.ShortcutInfo$Builder
-android.content.pm.ShortcutManager
-android.content.pm.ShortcutServiceInternal
-android.content.pm.ShortcutServiceInternal$ShortcutChangeListener
-android.content.pm.Signature
-android.content.pm.Signature$1
-android.content.pm.StringParceledListSlice
-android.content.pm.StringParceledListSlice$1
-android.content.pm.UserInfo
-android.content.pm.UserInfo$1
-android.content.pm.VerifierDeviceIdentity
-android.content.pm.VerifierInfo
-android.content.pm.VersionedPackage
-android.content.pm.VersionedPackage$1
-android.content.pm.XmlSerializerAndParser
-android.content.pm.split.DefaultSplitAssetLoader
-android.content.pm.split.SplitAssetLoader
-android.content.pm.split.SplitDependencyLoader$IllegalDependencyException
-android.content.res.AssetFileDescriptor
-android.content.res.AssetFileDescriptor$1
-android.content.res.AssetFileDescriptor$AutoCloseInputStream
-android.content.res.AssetManager
-android.content.res.AssetManager$AssetInputStream
-android.content.res.ColorStateList
-android.content.res.ColorStateList$1
-android.content.res.ColorStateList$ColorStateListFactory
-android.content.res.CompatResources
-android.content.res.CompatibilityInfo
-android.content.res.CompatibilityInfo$1
-android.content.res.CompatibilityInfo$2
-android.content.res.ComplexColor
-android.content.res.Configuration
-android.content.res.Configuration$1
-android.content.res.ConfigurationBoundResourceCache
-android.content.res.ConstantState
-android.content.res.DrawableCache
-android.content.res.GradientColor
-android.content.res.ObbInfo
-android.content.res.ObbInfo$1
-android.content.res.ObbScanner
-android.content.res.Resources
-android.content.res.Resources$NotFoundException
-android.content.res.Resources$Theme
-android.content.res.Resources$ThemeKey
-android.content.res.ResourcesImpl
-android.content.res.ResourcesImpl$ThemeImpl
-android.content.res.ResourcesKey
-android.content.res.StringBlock
-android.content.res.StringBlock$StyleIDs
-android.content.res.ThemedResourceCache
-android.content.res.TypedArray
-android.content.res.XmlBlock
-android.content.res.XmlBlock$Parser
-android.content.res.XmlResourceParser
-android.database.AbstractCursor
-android.database.AbstractCursor$SelfContentObserver
-android.database.AbstractWindowedCursor
-android.database.BulkCursorDescriptor
-android.database.BulkCursorDescriptor$1
-android.database.BulkCursorNative
-android.database.BulkCursorProxy
-android.database.BulkCursorToCursorAdaptor
-android.database.CharArrayBuffer
-android.database.ContentObservable
-android.database.ContentObserver
-android.database.ContentObserver$NotificationRunnable
-android.database.ContentObserver$Transport
-android.database.CrossProcessCursor
-android.database.CrossProcessCursorWrapper
-android.database.Cursor
-android.database.CursorIndexOutOfBoundsException
-android.database.CursorToBulkCursorAdaptor
-android.database.CursorToBulkCursorAdaptor$ContentObserverProxy
-android.database.CursorWindow
-android.database.CursorWindow$1
-android.database.CursorWrapper
-android.database.DataSetObservable
-android.database.DataSetObserver
-android.database.DatabaseErrorHandler
-android.database.DatabaseUtils
-android.database.DatabaseUtils$InsertHelper
-android.database.DefaultDatabaseErrorHandler
-android.database.IBulkCursor
-android.database.IContentObserver
-android.database.IContentObserver$Stub
-android.database.IContentObserver$Stub$Proxy
-android.database.MatrixCursor
-android.database.MatrixCursor$RowBuilder
-android.database.MergeCursor
-android.database.MergeCursor$1
-android.database.Observable
-android.database.SQLException
-android.database.StaleDataException
-android.database.sqlite.DatabaseObjectNotClosedException
-android.database.sqlite.SQLiteAbortException
-android.database.sqlite.SQLiteCantOpenDatabaseException
-android.database.sqlite.SQLiteClosable
-android.database.sqlite.SQLiteConnection
-android.database.sqlite.SQLiteConnection$Operation
-android.database.sqlite.SQLiteConnection$OperationLog
-android.database.sqlite.SQLiteConnection$PreparedStatement
-android.database.sqlite.SQLiteConnection$PreparedStatementCache
-android.database.sqlite.SQLiteConnectionPool
-android.database.sqlite.SQLiteConnectionPool$AcquiredConnectionStatus
-android.database.sqlite.SQLiteConnectionPool$ConnectionWaiter
-android.database.sqlite.SQLiteConstraintException
-android.database.sqlite.SQLiteCursor
-android.database.sqlite.SQLiteCursorDriver
-android.database.sqlite.SQLiteCustomFunction
-android.database.sqlite.SQLiteDatabase
-android.database.sqlite.SQLiteDatabase$1
-android.database.sqlite.SQLiteDatabase$2
-android.database.sqlite.SQLiteDatabase$CursorFactory
-android.database.sqlite.SQLiteDatabase$CustomFunction
-android.database.sqlite.SQLiteDatabaseConfiguration
-android.database.sqlite.SQLiteDatabaseCorruptException
-android.database.sqlite.SQLiteDatabaseLockedException
-android.database.sqlite.SQLiteDebug
-android.database.sqlite.SQLiteDebug$PagerStats
-android.database.sqlite.SQLiteDirectCursorDriver
-android.database.sqlite.SQLiteDiskIOException
-android.database.sqlite.SQLiteDoneException
-android.database.sqlite.SQLiteException
-android.database.sqlite.SQLiteFullException
-android.database.sqlite.SQLiteGlobal
-android.database.sqlite.SQLiteOpenHelper
-android.database.sqlite.SQLiteProgram
-android.database.sqlite.SQLiteQuery
-android.database.sqlite.SQLiteQueryBuilder
-android.database.sqlite.SQLiteReadOnlyDatabaseException
-android.database.sqlite.SQLiteSession
-android.database.sqlite.SQLiteSession$Transaction
-android.database.sqlite.SQLiteStatement
-android.database.sqlite.SQLiteStatementInfo
-android.database.sqlite.SQLiteTransactionListener
-android.database.sqlite.SqliteWrapper
-android.ddm.DdmHandleAppName
-android.ddm.DdmHandleExit
-android.ddm.DdmHandleHeap
-android.ddm.DdmHandleHello
-android.ddm.DdmHandleNativeHeap
-android.ddm.DdmHandleProfiling
-android.ddm.DdmHandleThread
-android.ddm.DdmHandleViewDebug
-android.ddm.DdmRegister
-android.drm.DrmManagerClient$OnErrorListener
-android.drm.DrmManagerClient$OnEventListener
-android.drm.DrmManagerClient$OnInfoListener
-android.drm.DrmOutputStream
-android.graphics.-$Lambda$ZrP-zejiEXAqfwV-MyP5lE9mYC8
-android.graphics.-$Lambda$ZrP-zejiEXAqfwV-MyP5lE9mYC8$1
-android.graphics.-$Lambda$ZrP-zejiEXAqfwV-MyP5lE9mYC8$2
-android.graphics.-$Lambda$ZrP-zejiEXAqfwV-MyP5lE9mYC8$4
-android.graphics.-$Lambda$ZrP-zejiEXAqfwV-MyP5lE9mYC8$6
-android.graphics.-$Lambda$ZrP-zejiEXAqfwV-MyP5lE9mYC8$7
-android.graphics.-$Lambda$ZrP-zejiEXAqfwV-MyP5lE9mYC8$8
-android.graphics.BaseCanvas
-android.graphics.Bitmap
-android.graphics.Bitmap$1
-android.graphics.Bitmap$CompressFormat
-android.graphics.Bitmap$Config
-android.graphics.BitmapFactory
-android.graphics.BitmapFactory$Options
-android.graphics.BitmapRegionDecoder
-android.graphics.BitmapShader
-android.graphics.BlurMaskFilter
-android.graphics.BlurMaskFilter$Blur
-android.graphics.Camera
-android.graphics.Canvas
-android.graphics.Canvas$EdgeType
-android.graphics.Canvas$NoImagePreloadHolder
-android.graphics.CanvasProperty
-android.graphics.Color
-android.graphics.ColorFilter
-android.graphics.ColorMatrix
-android.graphics.ColorMatrixColorFilter
-android.graphics.ColorSpace
-android.graphics.ColorSpace$Adaptation
-android.graphics.ColorSpace$Lab
-android.graphics.ColorSpace$Model
-android.graphics.ColorSpace$Named
-android.graphics.ColorSpace$Rgb
-android.graphics.ColorSpace$Rgb$TransferParameters
-android.graphics.ColorSpace$Xyz
-android.graphics.ComposePathEffect
-android.graphics.ComposeShader
-android.graphics.CornerPathEffect
-android.graphics.DashPathEffect
-android.graphics.DiscretePathEffect
-android.graphics.DrawFilter
-android.graphics.EmbossMaskFilter
-android.graphics.FontFamily
-android.graphics.FontListParser
-android.graphics.GraphicBuffer
-android.graphics.GraphicBuffer$1
-android.graphics.ImageFormat
-android.graphics.Insets
-android.graphics.Interpolator
-android.graphics.Interpolator$Result
-android.graphics.LightingColorFilter
-android.graphics.LinearGradient
-android.graphics.MaskFilter
-android.graphics.Matrix
-android.graphics.Matrix$1
-android.graphics.Matrix$NoImagePreloadHolder
-android.graphics.Matrix$ScaleToFit
-android.graphics.Movie
-android.graphics.NinePatch
-android.graphics.NinePatch$InsetStruct
-android.graphics.Outline
-android.graphics.Paint
-android.graphics.Paint$Align
-android.graphics.Paint$Cap
-android.graphics.Paint$FontMetrics
-android.graphics.Paint$FontMetricsInt
-android.graphics.Paint$Join
-android.graphics.Paint$NoImagePreloadHolder
-android.graphics.Paint$Style
-android.graphics.PaintFlagsDrawFilter
-android.graphics.Path
-android.graphics.Path$Direction
-android.graphics.Path$FillType
-android.graphics.Path$Op
-android.graphics.PathDashPathEffect
-android.graphics.PathEffect
-android.graphics.PathMeasure
-android.graphics.Picture
-android.graphics.Picture$RecordingCanvas
-android.graphics.PixelFormat
-android.graphics.Point
-android.graphics.Point$1
-android.graphics.PointF
-android.graphics.PointF$1
-android.graphics.PorterDuff
-android.graphics.PorterDuff$Mode
-android.graphics.PorterDuffColorFilter
-android.graphics.PorterDuffXfermode
-android.graphics.RadialGradient
-android.graphics.Rect
-android.graphics.Rect$1
-android.graphics.Rect$UnflattenHelper
-android.graphics.RectF
-android.graphics.RectF$1
-android.graphics.Region
-android.graphics.Region$1
-android.graphics.Region$Op
-android.graphics.RegionIterator
-android.graphics.Shader
-android.graphics.Shader$TileMode
-android.graphics.SumPathEffect
-android.graphics.SurfaceTexture
-android.graphics.SurfaceTexture$1
-android.graphics.SurfaceTexture$OnFrameAvailableListener
-android.graphics.SweepGradient
-android.graphics.TableMaskFilter
-android.graphics.TemporaryBuffer
-android.graphics.Typeface
-android.graphics.Typeface$Builder
-android.graphics.Xfermode
-android.graphics.YuvImage
-android.graphics.drawable.AdaptiveIconDrawable
-android.graphics.drawable.AdaptiveIconDrawable$ChildDrawable
-android.graphics.drawable.AdaptiveIconDrawable$LayerState
-android.graphics.drawable.Animatable
-android.graphics.drawable.Animatable2
-android.graphics.drawable.Animatable2$AnimationCallback
-android.graphics.drawable.AnimatedStateListDrawable
-android.graphics.drawable.AnimatedStateListDrawable$AnimatedStateListState
-android.graphics.drawable.AnimatedStateListDrawable$AnimatedVectorDrawableTransition
-android.graphics.drawable.AnimatedStateListDrawable$AnimationDrawableTransition
-android.graphics.drawable.AnimatedStateListDrawable$FrameInterpolator
-android.graphics.drawable.AnimatedStateListDrawable$Transition
-android.graphics.drawable.AnimatedVectorDrawable
-android.graphics.drawable.AnimatedVectorDrawable$1
-android.graphics.drawable.AnimatedVectorDrawable$2
-android.graphics.drawable.AnimatedVectorDrawable$AnimatedVectorDrawableState
-android.graphics.drawable.AnimatedVectorDrawable$AnimatedVectorDrawableState$PendingAnimator
-android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimator
-android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT
-android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorUI
-android.graphics.drawable.AnimationDrawable
-android.graphics.drawable.AnimationDrawable$AnimationState
-android.graphics.drawable.BitmapDrawable
-android.graphics.drawable.BitmapDrawable$BitmapState
-android.graphics.drawable.ClipDrawable
-android.graphics.drawable.ClipDrawable$ClipState
-android.graphics.drawable.ColorDrawable
-android.graphics.drawable.ColorDrawable$ColorState
-android.graphics.drawable.Drawable
-android.graphics.drawable.Drawable$Callback
-android.graphics.drawable.Drawable$ConstantState
-android.graphics.drawable.DrawableContainer
-android.graphics.drawable.DrawableContainer$1
-android.graphics.drawable.DrawableContainer$BlockInvalidateCallback
-android.graphics.drawable.DrawableContainer$DrawableContainerState
-android.graphics.drawable.DrawableInflater
-android.graphics.drawable.DrawableWrapper
-android.graphics.drawable.DrawableWrapper$DrawableWrapperState
-android.graphics.drawable.GradientDrawable
-android.graphics.drawable.GradientDrawable$GradientState
-android.graphics.drawable.GradientDrawable$Orientation
-android.graphics.drawable.Icon
-android.graphics.drawable.Icon$1
-android.graphics.drawable.InsetDrawable
-android.graphics.drawable.InsetDrawable$InsetState
-android.graphics.drawable.InsetDrawable$InsetValue
-android.graphics.drawable.LayerDrawable
-android.graphics.drawable.LayerDrawable$ChildDrawable
-android.graphics.drawable.LayerDrawable$LayerState
-android.graphics.drawable.NinePatchDrawable
-android.graphics.drawable.NinePatchDrawable$NinePatchState
-android.graphics.drawable.PaintDrawable
-android.graphics.drawable.PictureDrawable
-android.graphics.drawable.RippleBackground
-android.graphics.drawable.RippleBackground$1
-android.graphics.drawable.RippleBackground$BackgroundProperty
-android.graphics.drawable.RippleComponent
-android.graphics.drawable.RippleComponent$RenderNodeAnimatorSet
-android.graphics.drawable.RippleDrawable
-android.graphics.drawable.RippleDrawable$RippleState
-android.graphics.drawable.RippleForeground
-android.graphics.drawable.RippleForeground$1
-android.graphics.drawable.RippleForeground$2
-android.graphics.drawable.RippleForeground$3
-android.graphics.drawable.RippleForeground$4
-android.graphics.drawable.RippleForeground$LogDecelerateInterpolator
-android.graphics.drawable.RotateDrawable
-android.graphics.drawable.RotateDrawable$RotateState
-android.graphics.drawable.ScaleDrawable
-android.graphics.drawable.ScaleDrawable$ScaleState
-android.graphics.drawable.ShapeDrawable
-android.graphics.drawable.ShapeDrawable$ShaderFactory
-android.graphics.drawable.ShapeDrawable$ShapeState
-android.graphics.drawable.StateListDrawable
-android.graphics.drawable.StateListDrawable$StateListState
-android.graphics.drawable.TransitionDrawable
-android.graphics.drawable.TransitionDrawable$TransitionState
-android.graphics.drawable.VectorDrawable
-android.graphics.drawable.VectorDrawable$VClipPath
-android.graphics.drawable.VectorDrawable$VFullPath
-android.graphics.drawable.VectorDrawable$VFullPath$1
-android.graphics.drawable.VectorDrawable$VFullPath$10
-android.graphics.drawable.VectorDrawable$VFullPath$2
-android.graphics.drawable.VectorDrawable$VFullPath$3
-android.graphics.drawable.VectorDrawable$VFullPath$4
-android.graphics.drawable.VectorDrawable$VFullPath$5
-android.graphics.drawable.VectorDrawable$VFullPath$6
-android.graphics.drawable.VectorDrawable$VFullPath$7
-android.graphics.drawable.VectorDrawable$VFullPath$8
-android.graphics.drawable.VectorDrawable$VFullPath$9
-android.graphics.drawable.VectorDrawable$VGroup
-android.graphics.drawable.VectorDrawable$VGroup$1
-android.graphics.drawable.VectorDrawable$VGroup$2
-android.graphics.drawable.VectorDrawable$VGroup$3
-android.graphics.drawable.VectorDrawable$VGroup$4
-android.graphics.drawable.VectorDrawable$VGroup$5
-android.graphics.drawable.VectorDrawable$VGroup$6
-android.graphics.drawable.VectorDrawable$VGroup$7
-android.graphics.drawable.VectorDrawable$VGroup$8
-android.graphics.drawable.VectorDrawable$VGroup$9
-android.graphics.drawable.VectorDrawable$VObject
-android.graphics.drawable.VectorDrawable$VPath
-android.graphics.drawable.VectorDrawable$VPath$1
-android.graphics.drawable.VectorDrawable$VectorDrawableState
-android.graphics.drawable.VectorDrawable$VectorDrawableState$1
-android.graphics.drawable.shapes.OvalShape
-android.graphics.drawable.shapes.RectShape
-android.graphics.drawable.shapes.RoundRectShape
-android.graphics.drawable.shapes.Shape
-android.graphics.fonts.FontVariationAxis
-android.graphics.pdf.PdfDocument
-android.graphics.pdf.PdfEditor
-android.graphics.pdf.PdfRenderer
-android.hardware.Camera
-android.hardware.Camera$CameraInfo
-android.hardware.Camera$ErrorCallback
-android.hardware.Camera$Face
-android.hardware.CameraStatus
-android.hardware.CameraStatus$1
-android.hardware.ConsumerIrManager
-android.hardware.GeomagneticField
-android.hardware.GeomagneticField$LegendreTable
-android.hardware.HardwareBuffer
-android.hardware.HardwareBuffer$1
-android.hardware.ICameraService
-android.hardware.ICameraService$Stub
-android.hardware.ICameraService$Stub$Proxy
-android.hardware.ICameraServiceListener
-android.hardware.ICameraServiceListener$Stub
-android.hardware.ICameraServiceProxy
-android.hardware.ICameraServiceProxy$Stub
-android.hardware.IConsumerIrService
-android.hardware.IConsumerIrService$Stub
-android.hardware.ISerialManager
-android.hardware.ISerialManager$Stub
-android.hardware.Sensor
-android.hardware.SensorAdditionalInfo
-android.hardware.SensorEvent
-android.hardware.SensorEventListener
-android.hardware.SensorManager
-android.hardware.SerialManager
-android.hardware.SerialPort
-android.hardware.SystemSensorManager
-android.hardware.SystemSensorManager$BaseEventQueue
-android.hardware.SystemSensorManager$SensorEventQueue
-android.hardware.SystemSensorManager$TriggerEventQueue
-android.hardware.TriggerEvent
-android.hardware.TriggerEventListener
-android.hardware.camera2.CameraAccessException
-android.hardware.camera2.CameraCaptureSession
-android.hardware.camera2.CameraCaptureSession$CaptureCallback
-android.hardware.camera2.CameraCaptureSession$StateCallback
-android.hardware.camera2.CameraCharacteristics
-android.hardware.camera2.CameraCharacteristics$1
-android.hardware.camera2.CameraCharacteristics$2
-android.hardware.camera2.CameraCharacteristics$3
-android.hardware.camera2.CameraCharacteristics$4
-android.hardware.camera2.CameraCharacteristics$5
-android.hardware.camera2.CameraCharacteristics$Key
-android.hardware.camera2.CameraDevice
-android.hardware.camera2.CameraDevice$StateCallback
-android.hardware.camera2.CameraManager
-android.hardware.camera2.CameraManager$AvailabilityCallback
-android.hardware.camera2.CameraManager$CameraManagerGlobal
-android.hardware.camera2.CameraManager$CameraManagerGlobal$1
-android.hardware.camera2.CameraManager$CameraManagerGlobal$2
-android.hardware.camera2.CameraManager$CameraManagerGlobal$3
-android.hardware.camera2.CameraManager$CameraManagerGlobal$4
-android.hardware.camera2.CameraManager$TorchCallback
-android.hardware.camera2.CameraMetadata
-android.hardware.camera2.CaptureFailure
-android.hardware.camera2.CaptureRequest
-android.hardware.camera2.CaptureRequest$1
-android.hardware.camera2.CaptureRequest$2
-android.hardware.camera2.CaptureRequest$Builder
-android.hardware.camera2.CaptureRequest$Key
-android.hardware.camera2.CaptureResult
-android.hardware.camera2.CaptureResult$1
-android.hardware.camera2.CaptureResult$2
-android.hardware.camera2.CaptureResult$3
-android.hardware.camera2.CaptureResult$Key
-android.hardware.camera2.DngCreator
-android.hardware.camera2.ICameraDeviceCallbacks
-android.hardware.camera2.ICameraDeviceCallbacks$Stub
-android.hardware.camera2.ICameraDeviceUser
-android.hardware.camera2.ICameraDeviceUser$Stub
-android.hardware.camera2.ICameraDeviceUser$Stub$Proxy
-android.hardware.camera2.TotalCaptureResult
-android.hardware.camera2.dispatch.ArgumentReplacingDispatcher
-android.hardware.camera2.dispatch.BroadcastDispatcher
-android.hardware.camera2.dispatch.Dispatchable
-android.hardware.camera2.dispatch.DuckTypingDispatcher
-android.hardware.camera2.dispatch.HandlerDispatcher
-android.hardware.camera2.dispatch.HandlerDispatcher$1
-android.hardware.camera2.dispatch.InvokeDispatcher
-android.hardware.camera2.dispatch.MethodNameInvoker
-android.hardware.camera2.impl.CallbackProxies$DeviceCaptureCallbackProxy
-android.hardware.camera2.impl.CallbackProxies$SessionStateCallbackProxy
-android.hardware.camera2.impl.CameraCaptureSessionCore
-android.hardware.camera2.impl.CameraCaptureSessionImpl
-android.hardware.camera2.impl.CameraCaptureSessionImpl$1
-android.hardware.camera2.impl.CameraCaptureSessionImpl$2
-android.hardware.camera2.impl.CameraCaptureSessionImpl$AbortDrainListener
-android.hardware.camera2.impl.CameraCaptureSessionImpl$IdleDrainListener
-android.hardware.camera2.impl.CameraCaptureSessionImpl$SequenceDrainListener
-android.hardware.camera2.impl.CameraDeviceImpl
-android.hardware.camera2.impl.CameraDeviceImpl$1
-android.hardware.camera2.impl.CameraDeviceImpl$10
-android.hardware.camera2.impl.CameraDeviceImpl$2
-android.hardware.camera2.impl.CameraDeviceImpl$3
-android.hardware.camera2.impl.CameraDeviceImpl$4
-android.hardware.camera2.impl.CameraDeviceImpl$5
-android.hardware.camera2.impl.CameraDeviceImpl$6
-android.hardware.camera2.impl.CameraDeviceImpl$7
-android.hardware.camera2.impl.CameraDeviceImpl$9
-android.hardware.camera2.impl.CameraDeviceImpl$CameraDeviceCallbacks
-android.hardware.camera2.impl.CameraDeviceImpl$CameraDeviceCallbacks$2
-android.hardware.camera2.impl.CameraDeviceImpl$CameraDeviceCallbacks$3
-android.hardware.camera2.impl.CameraDeviceImpl$CameraDeviceCallbacks$4
-android.hardware.camera2.impl.CameraDeviceImpl$CaptureCallback
-android.hardware.camera2.impl.CameraDeviceImpl$CaptureCallbackHolder
-android.hardware.camera2.impl.CameraDeviceImpl$FrameNumberTracker
-android.hardware.camera2.impl.CameraDeviceImpl$RequestLastFrameNumbersHolder
-android.hardware.camera2.impl.CameraDeviceImpl$StateCallbackKK
-android.hardware.camera2.impl.CameraMetadataNative
-android.hardware.camera2.impl.CameraMetadataNative$1
-android.hardware.camera2.impl.CameraMetadataNative$10
-android.hardware.camera2.impl.CameraMetadataNative$11
-android.hardware.camera2.impl.CameraMetadataNative$12
-android.hardware.camera2.impl.CameraMetadataNative$13
-android.hardware.camera2.impl.CameraMetadataNative$14
-android.hardware.camera2.impl.CameraMetadataNative$15
-android.hardware.camera2.impl.CameraMetadataNative$16
-android.hardware.camera2.impl.CameraMetadataNative$17
-android.hardware.camera2.impl.CameraMetadataNative$18
-android.hardware.camera2.impl.CameraMetadataNative$19
-android.hardware.camera2.impl.CameraMetadataNative$2
-android.hardware.camera2.impl.CameraMetadataNative$3
-android.hardware.camera2.impl.CameraMetadataNative$4
-android.hardware.camera2.impl.CameraMetadataNative$5
-android.hardware.camera2.impl.CameraMetadataNative$6
-android.hardware.camera2.impl.CameraMetadataNative$7
-android.hardware.camera2.impl.CameraMetadataNative$8
-android.hardware.camera2.impl.CameraMetadataNative$9
-android.hardware.camera2.impl.CameraMetadataNative$Key
-android.hardware.camera2.impl.CaptureResultExtras
-android.hardware.camera2.impl.CaptureResultExtras$1
-android.hardware.camera2.impl.GetCommand
-android.hardware.camera2.impl.ICameraDeviceUserWrapper
-android.hardware.camera2.impl.SetCommand
-android.hardware.camera2.legacy.LegacyCameraDevice
-android.hardware.camera2.legacy.LegacyExceptionUtils
-android.hardware.camera2.legacy.LegacyExceptionUtils$BufferQueueAbandonedException
-android.hardware.camera2.legacy.PerfMeasurement
-android.hardware.camera2.marshal.MarshalHelpers
-android.hardware.camera2.marshal.MarshalQueryable
-android.hardware.camera2.marshal.MarshalRegistry
-android.hardware.camera2.marshal.MarshalRegistry$MarshalToken
-android.hardware.camera2.marshal.Marshaler
-android.hardware.camera2.marshal.impl.MarshalQueryableArray
-android.hardware.camera2.marshal.impl.MarshalQueryableArray$MarshalerArray
-android.hardware.camera2.marshal.impl.MarshalQueryableBlackLevelPattern
-android.hardware.camera2.marshal.impl.MarshalQueryableBlackLevelPattern$MarshalerBlackLevelPattern
-android.hardware.camera2.marshal.impl.MarshalQueryableBoolean
-android.hardware.camera2.marshal.impl.MarshalQueryableBoolean$MarshalerBoolean
-android.hardware.camera2.marshal.impl.MarshalQueryableColorSpaceTransform
-android.hardware.camera2.marshal.impl.MarshalQueryableColorSpaceTransform$MarshalerColorSpaceTransform
-android.hardware.camera2.marshal.impl.MarshalQueryableEnum
-android.hardware.camera2.marshal.impl.MarshalQueryableHighSpeedVideoConfiguration
-android.hardware.camera2.marshal.impl.MarshalQueryableHighSpeedVideoConfiguration$MarshalerHighSpeedVideoConfiguration
-android.hardware.camera2.marshal.impl.MarshalQueryableMeteringRectangle
-android.hardware.camera2.marshal.impl.MarshalQueryableMeteringRectangle$MarshalerMeteringRectangle
-android.hardware.camera2.marshal.impl.MarshalQueryableNativeByteToInteger
-android.hardware.camera2.marshal.impl.MarshalQueryableNativeByteToInteger$MarshalerNativeByteToInteger
-android.hardware.camera2.marshal.impl.MarshalQueryablePair
-android.hardware.camera2.marshal.impl.MarshalQueryablePair$MarshalerPair
-android.hardware.camera2.marshal.impl.MarshalQueryableParcelable
-android.hardware.camera2.marshal.impl.MarshalQueryablePrimitive
-android.hardware.camera2.marshal.impl.MarshalQueryablePrimitive$MarshalerPrimitive
-android.hardware.camera2.marshal.impl.MarshalQueryableRange
-android.hardware.camera2.marshal.impl.MarshalQueryableRange$MarshalerRange
-android.hardware.camera2.marshal.impl.MarshalQueryableRect
-android.hardware.camera2.marshal.impl.MarshalQueryableRect$MarshalerRect
-android.hardware.camera2.marshal.impl.MarshalQueryableReprocessFormatsMap
-android.hardware.camera2.marshal.impl.MarshalQueryableReprocessFormatsMap$MarshalerReprocessFormatsMap
-android.hardware.camera2.marshal.impl.MarshalQueryableRggbChannelVector
-android.hardware.camera2.marshal.impl.MarshalQueryableRggbChannelVector$MarshalerRggbChannelVector
-android.hardware.camera2.marshal.impl.MarshalQueryableSize
-android.hardware.camera2.marshal.impl.MarshalQueryableSize$MarshalerSize
-android.hardware.camera2.marshal.impl.MarshalQueryableSizeF
-android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfiguration
-android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfiguration$MarshalerStreamConfiguration
-android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration
-android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration$MarshalerStreamConfigurationDuration
-android.hardware.camera2.marshal.impl.MarshalQueryableString
-android.hardware.camera2.params.BlackLevelPattern
-android.hardware.camera2.params.ColorSpaceTransform
-android.hardware.camera2.params.Face
-android.hardware.camera2.params.HighSpeedVideoConfiguration
-android.hardware.camera2.params.InputConfiguration
-android.hardware.camera2.params.LensShadingMap
-android.hardware.camera2.params.MeteringRectangle
-android.hardware.camera2.params.OutputConfiguration
-android.hardware.camera2.params.OutputConfiguration$1
-android.hardware.camera2.params.ReprocessFormatsMap
-android.hardware.camera2.params.RggbChannelVector
-android.hardware.camera2.params.StreamConfiguration
-android.hardware.camera2.params.StreamConfigurationDuration
-android.hardware.camera2.params.StreamConfigurationMap
-android.hardware.camera2.params.TonemapCurve
-android.hardware.camera2.utils.HashCodeHelpers
-android.hardware.camera2.utils.SubmitInfo
-android.hardware.camera2.utils.SubmitInfo$1
-android.hardware.camera2.utils.SurfaceUtils
-android.hardware.camera2.utils.TaskDrainer
-android.hardware.camera2.utils.TaskDrainer$1
-android.hardware.camera2.utils.TaskDrainer$DrainListener
-android.hardware.camera2.utils.TaskSingleDrainer
-android.hardware.camera2.utils.TypeReference
-android.hardware.camera2.utils.TypeReference$SpecializedBaseTypeReference
-android.hardware.camera2.utils.TypeReference$SpecializedTypeReference
-android.hardware.display.DisplayManager
-android.hardware.display.DisplayManager$DisplayListener
-android.hardware.display.DisplayManagerGlobal
-android.hardware.display.DisplayManagerGlobal$DisplayListenerDelegate
-android.hardware.display.DisplayManagerGlobal$DisplayManagerCallback
-android.hardware.display.DisplayManagerInternal
-android.hardware.display.DisplayManagerInternal$DisplayPowerCallbacks
-android.hardware.display.DisplayManagerInternal$DisplayPowerRequest
-android.hardware.display.DisplayManagerInternal$DisplayTransactionListener
-android.hardware.display.DisplayViewport
-android.hardware.display.IDisplayManager
-android.hardware.display.IDisplayManager$Stub
-android.hardware.display.IDisplayManager$Stub$Proxy
-android.hardware.display.IDisplayManagerCallback
-android.hardware.display.IDisplayManagerCallback$Stub
-android.hardware.display.IDisplayManagerCallback$Stub$Proxy
-android.hardware.display.IVirtualDisplayCallback
-android.hardware.display.WifiDisplay
-android.hardware.display.WifiDisplay$1
-android.hardware.display.WifiDisplaySessionInfo
-android.hardware.display.WifiDisplaySessionInfo$1
-android.hardware.display.WifiDisplayStatus
-android.hardware.display.WifiDisplayStatus$1
-android.hardware.fingerprint.Fingerprint
-android.hardware.fingerprint.Fingerprint$1
-android.hardware.fingerprint.FingerprintManager
-android.hardware.fingerprint.FingerprintManager$1
-android.hardware.fingerprint.FingerprintManager$2
-android.hardware.fingerprint.FingerprintManager$AuthenticationCallback
-android.hardware.fingerprint.FingerprintManager$AuthenticationResult
-android.hardware.fingerprint.FingerprintManager$LockoutResetCallback
-android.hardware.fingerprint.FingerprintManager$MyHandler
-android.hardware.fingerprint.IFingerprintClientActiveCallback
-android.hardware.fingerprint.IFingerprintService
-android.hardware.fingerprint.IFingerprintService$Stub
-android.hardware.fingerprint.IFingerprintService$Stub$Proxy
-android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback
-android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback$Stub
-android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback$Stub$Proxy
-android.hardware.fingerprint.IFingerprintServiceReceiver
-android.hardware.fingerprint.IFingerprintServiceReceiver$Stub
-android.hardware.hdmi.HdmiControlManager
-android.hardware.hdmi.HdmiPlaybackClient$DisplayStatusCallback
-android.hardware.input.IInputDevicesChangedListener
-android.hardware.input.IInputDevicesChangedListener$Stub
-android.hardware.input.IInputDevicesChangedListener$Stub$Proxy
-android.hardware.input.IInputManager
-android.hardware.input.IInputManager$Stub
-android.hardware.input.IInputManager$Stub$Proxy
-android.hardware.input.ITabletModeChangedListener
-android.hardware.input.InputDeviceIdentifier
-android.hardware.input.InputDeviceIdentifier$1
-android.hardware.input.InputManager
-android.hardware.input.InputManager$InputDeviceListener
-android.hardware.input.InputManager$InputDeviceListenerDelegate
-android.hardware.input.InputManager$InputDevicesChangedListener
-android.hardware.input.InputManager$OnTabletModeChangedListener
-android.hardware.input.InputManagerInternal
-android.hardware.input.KeyboardLayout
-android.hardware.input.KeyboardLayout$1
-android.hardware.input.TouchCalibration
-android.hardware.input.TouchCalibration$1
-android.hardware.location.ActivityRecognitionHardware
-android.hardware.location.ContextHubInfo
-android.hardware.location.ContextHubInfo$1
-android.hardware.location.ContextHubManager
-android.hardware.location.ContextHubManager$1
-android.hardware.location.ContextHubManager$Callback
-android.hardware.location.ContextHubManager$ICallback
-android.hardware.location.ContextHubMessage
-android.hardware.location.ContextHubMessage$1
-android.hardware.location.GeofenceHardware
-android.hardware.location.GeofenceHardware$GeofenceHardwareMonitorCallbackWrapper
-android.hardware.location.GeofenceHardwareCallback
-android.hardware.location.GeofenceHardwareImpl
-android.hardware.location.GeofenceHardwareImpl$1
-android.hardware.location.GeofenceHardwareImpl$2
-android.hardware.location.GeofenceHardwareImpl$3
-android.hardware.location.GeofenceHardwareImpl$Reaper
-android.hardware.location.GeofenceHardwareMonitorCallback
-android.hardware.location.GeofenceHardwareService
-android.hardware.location.GeofenceHardwareService$1
-android.hardware.location.IActivityRecognitionHardware
-android.hardware.location.IActivityRecognitionHardware$Stub
-android.hardware.location.IActivityRecognitionHardwareClient
-android.hardware.location.IActivityRecognitionHardwareClient$Stub
-android.hardware.location.IActivityRecognitionHardwareClient$Stub$Proxy
-android.hardware.location.IActivityRecognitionHardwareWatcher
-android.hardware.location.IContextHubCallback
-android.hardware.location.IContextHubCallback$Stub
-android.hardware.location.IContextHubCallback$Stub$Proxy
-android.hardware.location.IContextHubService
-android.hardware.location.IContextHubService$Stub
-android.hardware.location.IContextHubService$Stub$Proxy
-android.hardware.location.IFusedLocationHardware
-android.hardware.location.IGeofenceHardware
-android.hardware.location.IGeofenceHardware$Stub
-android.hardware.location.IGeofenceHardware$Stub$Proxy
-android.hardware.location.IGeofenceHardwareMonitorCallback
-android.hardware.location.IGeofenceHardwareMonitorCallback$Stub
-android.hardware.location.IGeofenceHardwareMonitorCallback$Stub$Proxy
-android.hardware.location.MemoryRegion
-android.hardware.location.MemoryRegion$1
-android.hardware.location.NanoApp
-android.hardware.location.NanoAppFilter
-android.hardware.location.NanoAppFilter$1
-android.hardware.location.NanoAppInstanceInfo
-android.hardware.location.NanoAppInstanceInfo$1
-android.hardware.radio.RadioManager
-android.hardware.radio.RadioManager$AmBandConfig
-android.hardware.radio.RadioManager$AmBandConfig$1
-android.hardware.radio.RadioManager$AmBandDescriptor
-android.hardware.radio.RadioManager$AmBandDescriptor$1
-android.hardware.radio.RadioManager$BandConfig
-android.hardware.radio.RadioManager$BandConfig$1
-android.hardware.radio.RadioManager$BandDescriptor
-android.hardware.radio.RadioManager$BandDescriptor$1
-android.hardware.radio.RadioManager$FmBandConfig
-android.hardware.radio.RadioManager$FmBandConfig$1
-android.hardware.radio.RadioManager$FmBandDescriptor
-android.hardware.radio.RadioManager$FmBandDescriptor$1
-android.hardware.radio.RadioManager$ModuleProperties
-android.hardware.radio.RadioManager$ModuleProperties$1
-android.hardware.radio.RadioManager$ProgramInfo
-android.hardware.radio.RadioManager$ProgramInfo$1
-android.hardware.radio.RadioMetadata
-android.hardware.radio.RadioMetadata$1
-android.hardware.radio.RadioModule
-android.hardware.radio.RadioTuner
-android.hardware.radio.V1_0.Call
-android.hardware.radio.V1_0.CardStatus
-android.hardware.radio.V1_0.CdmaSignalStrength
-android.hardware.radio.V1_0.CellIdentity
-android.hardware.radio.V1_0.CellIdentityCdma
-android.hardware.radio.V1_0.CellIdentityGsm
-android.hardware.radio.V1_0.CellIdentityLte
-android.hardware.radio.V1_0.CellIdentityTdscdma
-android.hardware.radio.V1_0.CellIdentityWcdma
-android.hardware.radio.V1_0.CellInfo
-android.hardware.radio.V1_0.CellInfoCdma
-android.hardware.radio.V1_0.CellInfoGsm
-android.hardware.radio.V1_0.CellInfoLte
-android.hardware.radio.V1_0.CellInfoTdscdma
-android.hardware.radio.V1_0.CellInfoType
-android.hardware.radio.V1_0.CellInfoWcdma
-android.hardware.radio.V1_0.DataRegStateResult
-android.hardware.radio.V1_0.EvdoSignalStrength
-android.hardware.radio.V1_0.GsmSignalStrength
-android.hardware.radio.V1_0.HardwareConfig
-android.hardware.radio.V1_0.IRadio
-android.hardware.radio.V1_0.IRadio$Proxy
-android.hardware.radio.V1_0.IRadioIndication
-android.hardware.radio.V1_0.IRadioIndication$Stub
-android.hardware.radio.V1_0.IRadioResponse
-android.hardware.radio.V1_0.IRadioResponse$Stub
-android.hardware.radio.V1_0.LceStatusInfo
-android.hardware.radio.V1_0.LteSignalStrength
-android.hardware.radio.V1_0.RadioCapability
-android.hardware.radio.V1_0.RadioResponseInfo
-android.hardware.radio.V1_0.RegState
-android.hardware.radio.V1_0.SignalStrength
-android.hardware.radio.V1_0.TdScdmaSignalStrength
-android.hardware.radio.V1_0.VoiceRegStateResult
-android.hardware.radio.V1_0.WcdmaSignalStrength
-android.hardware.radio.deprecated.V1_0.IOemHook
-android.hardware.radio.deprecated.V1_0.IOemHook$Proxy
-android.hardware.radio.deprecated.V1_0.IOemHookIndication
-android.hardware.radio.deprecated.V1_0.IOemHookIndication$Stub
-android.hardware.radio.deprecated.V1_0.IOemHookResponse
-android.hardware.radio.deprecated.V1_0.IOemHookResponse$Stub
-android.hardware.soundtrigger.IRecognitionStatusCallback
-android.hardware.soundtrigger.IRecognitionStatusCallback$Stub
-android.hardware.soundtrigger.KeyphraseEnrollmentInfo
-android.hardware.soundtrigger.KeyphraseMetadata
-android.hardware.soundtrigger.SoundTrigger
-android.hardware.soundtrigger.SoundTrigger$ConfidenceLevel
-android.hardware.soundtrigger.SoundTrigger$ConfidenceLevel$1
-android.hardware.soundtrigger.SoundTrigger$GenericRecognitionEvent
-android.hardware.soundtrigger.SoundTrigger$GenericRecognitionEvent$1
-android.hardware.soundtrigger.SoundTrigger$GenericSoundModel
-android.hardware.soundtrigger.SoundTrigger$Keyphrase
-android.hardware.soundtrigger.SoundTrigger$Keyphrase$1
-android.hardware.soundtrigger.SoundTrigger$KeyphraseRecognitionEvent
-android.hardware.soundtrigger.SoundTrigger$KeyphraseRecognitionEvent$1
-android.hardware.soundtrigger.SoundTrigger$KeyphraseRecognitionExtra
-android.hardware.soundtrigger.SoundTrigger$KeyphraseRecognitionExtra$1
-android.hardware.soundtrigger.SoundTrigger$KeyphraseSoundModel
-android.hardware.soundtrigger.SoundTrigger$KeyphraseSoundModel$1
-android.hardware.soundtrigger.SoundTrigger$ModuleProperties
-android.hardware.soundtrigger.SoundTrigger$ModuleProperties$1
-android.hardware.soundtrigger.SoundTrigger$RecognitionConfig
-android.hardware.soundtrigger.SoundTrigger$RecognitionConfig$1
-android.hardware.soundtrigger.SoundTrigger$RecognitionEvent
-android.hardware.soundtrigger.SoundTrigger$RecognitionEvent$1
-android.hardware.soundtrigger.SoundTrigger$SoundModel
-android.hardware.soundtrigger.SoundTrigger$SoundModelEvent
-android.hardware.soundtrigger.SoundTrigger$SoundModelEvent$1
-android.hardware.soundtrigger.SoundTrigger$StatusListener
-android.hardware.soundtrigger.SoundTriggerModule
-android.hardware.usb.IUsbManager
-android.hardware.usb.IUsbManager$Stub
-android.hardware.usb.IUsbManager$Stub$Proxy
-android.hardware.usb.UsbAccessory
-android.hardware.usb.UsbDevice
-android.hardware.usb.UsbDeviceConnection
-android.hardware.usb.UsbManager
-android.hardware.usb.UsbPort
-android.hardware.usb.UsbPort$1
-android.hardware.usb.UsbPortStatus
-android.hardware.usb.UsbPortStatus$1
-android.hardware.usb.UsbRequest
-android.hidl.base.V1_0.DebugInfo
-android.hidl.base.V1_0.IBase
-android.hidl.manager.V1_0.IServiceManager
-android.hidl.manager.V1_0.IServiceManager$Proxy
-android.hidl.manager.V1_0.IServiceNotification
-android.hidl.manager.V1_0.IServiceNotification$Stub
-android.icu.impl.BMPSet
-android.icu.impl.CacheBase
-android.icu.impl.CacheValue
-android.icu.impl.CacheValue$NullValue
-android.icu.impl.CacheValue$SoftValue
-android.icu.impl.CacheValue$Strength
-android.icu.impl.CalendarUtil
-android.icu.impl.CalendarUtil$CalendarPreferences
-android.icu.impl.CaseMapImpl
-android.icu.impl.CaseMapImpl$GreekUpper
-android.icu.impl.CaseMapImpl$StringContextIterator
-android.icu.impl.CharTrie
-android.icu.impl.CharacterIteration
-android.icu.impl.ClassLoaderUtil
-android.icu.impl.CurrencyData
-android.icu.impl.CurrencyData$CurrencyDisplayInfo
-android.icu.impl.CurrencyData$CurrencyDisplayInfoProvider
-android.icu.impl.CurrencyData$CurrencySpacingInfo
-android.icu.impl.CurrencyData$CurrencySpacingInfo$SpacingPattern
-android.icu.impl.CurrencyData$CurrencySpacingInfo$SpacingType
-android.icu.impl.DateNumberFormat
-android.icu.impl.DontCareFieldPosition
-android.icu.impl.Grego
-android.icu.impl.ICUBinary
-android.icu.impl.ICUBinary$Authenticate
-android.icu.impl.ICUBinary$DatPackageReader
-android.icu.impl.ICUBinary$DatPackageReader$IsAcceptable
-android.icu.impl.ICUBinary$DataFile
-android.icu.impl.ICUBinary$PackageDataFile
-android.icu.impl.ICUCache
-android.icu.impl.ICUConfig
-android.icu.impl.ICUCurrencyDisplayInfoProvider
-android.icu.impl.ICUCurrencyDisplayInfoProvider$ICUCurrencyDisplayInfo
-android.icu.impl.ICUCurrencyDisplayInfoProvider$ICUCurrencyDisplayInfo$SpacingInfoSink
-android.icu.impl.ICUCurrencyMetaInfo
-android.icu.impl.ICUCurrencyMetaInfo$Collector
-android.icu.impl.ICUCurrencyMetaInfo$CurrencyCollector
-android.icu.impl.ICUCurrencyMetaInfo$UniqueList
-android.icu.impl.ICUData
-android.icu.impl.ICUDebug
-android.icu.impl.ICULangDataTables
-android.icu.impl.ICULocaleService
-android.icu.impl.ICULocaleService$ICUResourceBundleFactory
-android.icu.impl.ICULocaleService$LocaleKey
-android.icu.impl.ICULocaleService$LocaleKeyFactory
-android.icu.impl.ICUNotifier
-android.icu.impl.ICURWLock
-android.icu.impl.ICURegionDataTables
-android.icu.impl.ICUResourceBundle
-android.icu.impl.ICUResourceBundle$1
-android.icu.impl.ICUResourceBundle$2
-android.icu.impl.ICUResourceBundle$3
-android.icu.impl.ICUResourceBundle$3$1
-android.icu.impl.ICUResourceBundle$4
-android.icu.impl.ICUResourceBundle$AvailEntry
-android.icu.impl.ICUResourceBundle$Loader
-android.icu.impl.ICUResourceBundle$OpenType
-android.icu.impl.ICUResourceBundle$WholeBundle
-android.icu.impl.ICUResourceBundleImpl
-android.icu.impl.ICUResourceBundleImpl$ResourceArray
-android.icu.impl.ICUResourceBundleImpl$ResourceBinary
-android.icu.impl.ICUResourceBundleImpl$ResourceContainer
-android.icu.impl.ICUResourceBundleImpl$ResourceInt
-android.icu.impl.ICUResourceBundleImpl$ResourceIntVector
-android.icu.impl.ICUResourceBundleImpl$ResourceString
-android.icu.impl.ICUResourceBundleImpl$ResourceTable
-android.icu.impl.ICUResourceBundleReader
-android.icu.impl.ICUResourceBundleReader$Array
-android.icu.impl.ICUResourceBundleReader$Array16
-android.icu.impl.ICUResourceBundleReader$Array32
-android.icu.impl.ICUResourceBundleReader$Container
-android.icu.impl.ICUResourceBundleReader$IsAcceptable
-android.icu.impl.ICUResourceBundleReader$ReaderCache
-android.icu.impl.ICUResourceBundleReader$ReaderCacheKey
-android.icu.impl.ICUResourceBundleReader$ReaderValue
-android.icu.impl.ICUResourceBundleReader$ResourceCache
-android.icu.impl.ICUResourceBundleReader$ResourceCache$Level
-android.icu.impl.ICUResourceBundleReader$Table
-android.icu.impl.ICUResourceBundleReader$Table16
-android.icu.impl.ICUResourceBundleReader$Table1632
-android.icu.impl.ICUResourceTableAccess
-android.icu.impl.ICUService
-android.icu.impl.ICUService$CacheEntry
-android.icu.impl.ICUService$Factory
-android.icu.impl.ICUService$Key
-android.icu.impl.IDNA2003
-android.icu.impl.JavaTimeZone
-android.icu.impl.LocaleDisplayNamesImpl
-android.icu.impl.LocaleDisplayNamesImpl$Cache
-android.icu.impl.LocaleDisplayNamesImpl$CapitalizationContextUsage
-android.icu.impl.LocaleDisplayNamesImpl$DataTable
-android.icu.impl.LocaleDisplayNamesImpl$DataTables
-android.icu.impl.LocaleDisplayNamesImpl$ICUDataTable
-android.icu.impl.LocaleDisplayNamesImpl$ICUDataTables
-android.icu.impl.LocaleDisplayNamesImpl$LangDataTables
-android.icu.impl.LocaleDisplayNamesImpl$RegionDataTables
-android.icu.impl.LocaleIDParser
-android.icu.impl.LocaleIDs
-android.icu.impl.Norm2AllModes
-android.icu.impl.Norm2AllModes$1
-android.icu.impl.Norm2AllModes$ComposeNormalizer2
-android.icu.impl.Norm2AllModes$DecomposeNormalizer2
-android.icu.impl.Norm2AllModes$FCDNormalizer2
-android.icu.impl.Norm2AllModes$NFCSingleton
-android.icu.impl.Norm2AllModes$NFKCSingleton
-android.icu.impl.Norm2AllModes$NoopNormalizer2
-android.icu.impl.Norm2AllModes$Norm2AllModesSingleton
-android.icu.impl.Norm2AllModes$Normalizer2WithImpl
-android.icu.impl.Normalizer2Impl
-android.icu.impl.Normalizer2Impl$1
-android.icu.impl.Normalizer2Impl$IsAcceptable
-android.icu.impl.Normalizer2Impl$ReorderingBuffer
-android.icu.impl.OlsonTimeZone
-android.icu.impl.Pair
-android.icu.impl.PatternProps
-android.icu.impl.PatternTokenizer
-android.icu.impl.PluralRulesLoader
-android.icu.impl.ReplaceableUCharacterIterator
-android.icu.impl.RuleCharacterIterator
-android.icu.impl.SimpleCache
-android.icu.impl.SimpleFormatterImpl
-android.icu.impl.SoftCache
-android.icu.impl.StandardPlural
-android.icu.impl.StringPrepDataReader
-android.icu.impl.Trie
-android.icu.impl.Trie$DataManipulate
-android.icu.impl.Trie$DefaultGetFoldingOffset
-android.icu.impl.Trie2
-android.icu.impl.Trie2$1
-android.icu.impl.Trie2$Range
-android.icu.impl.Trie2$Trie2Iterator
-android.icu.impl.Trie2$UTrie2Header
-android.icu.impl.Trie2$ValueMapper
-android.icu.impl.Trie2$ValueWidth
-android.icu.impl.Trie2_16
-android.icu.impl.Trie2_32
-android.icu.impl.UBiDiProps
-android.icu.impl.UBiDiProps$IsAcceptable
-android.icu.impl.UCaseProps
-android.icu.impl.UCaseProps$ContextIterator
-android.icu.impl.UCaseProps$IsAcceptable
-android.icu.impl.UCharacterProperty
-android.icu.impl.UCharacterProperty$1
-android.icu.impl.UCharacterProperty$10
-android.icu.impl.UCharacterProperty$11
-android.icu.impl.UCharacterProperty$12
-android.icu.impl.UCharacterProperty$13
-android.icu.impl.UCharacterProperty$14
-android.icu.impl.UCharacterProperty$15
-android.icu.impl.UCharacterProperty$16
-android.icu.impl.UCharacterProperty$17
-android.icu.impl.UCharacterProperty$18
-android.icu.impl.UCharacterProperty$19
-android.icu.impl.UCharacterProperty$2
-android.icu.impl.UCharacterProperty$20
-android.icu.impl.UCharacterProperty$21
-android.icu.impl.UCharacterProperty$22
-android.icu.impl.UCharacterProperty$23
-android.icu.impl.UCharacterProperty$3
-android.icu.impl.UCharacterProperty$4
-android.icu.impl.UCharacterProperty$5
-android.icu.impl.UCharacterProperty$6
-android.icu.impl.UCharacterProperty$7
-android.icu.impl.UCharacterProperty$8
-android.icu.impl.UCharacterProperty$9
-android.icu.impl.UCharacterProperty$BiDiIntProperty
-android.icu.impl.UCharacterProperty$BinaryProperty
-android.icu.impl.UCharacterProperty$CaseBinaryProperty
-android.icu.impl.UCharacterProperty$CombiningClassIntProperty
-android.icu.impl.UCharacterProperty$IntProperty
-android.icu.impl.UCharacterProperty$IsAcceptable
-android.icu.impl.UCharacterProperty$NormInertBinaryProperty
-android.icu.impl.UCharacterProperty$NormQuickCheckIntProperty
-android.icu.impl.UPropertyAliases
-android.icu.impl.UPropertyAliases$IsAcceptable
-android.icu.impl.URLHandler$URLVisitor
-android.icu.impl.UResource$Array
-android.icu.impl.UResource$Key
-android.icu.impl.UResource$Sink
-android.icu.impl.UResource$Table
-android.icu.impl.UResource$Value
-android.icu.impl.USerializedSet
-android.icu.impl.Utility
-android.icu.impl.ZoneMeta
-android.icu.impl.ZoneMeta$CustomTimeZoneCache
-android.icu.impl.ZoneMeta$SystemTimeZoneCache
-android.icu.impl.coll.Collation
-android.icu.impl.coll.CollationCompare
-android.icu.impl.coll.CollationData
-android.icu.impl.coll.CollationDataReader
-android.icu.impl.coll.CollationDataReader$IsAcceptable
-android.icu.impl.coll.CollationFCD
-android.icu.impl.coll.CollationFastLatin
-android.icu.impl.coll.CollationIterator
-android.icu.impl.coll.CollationIterator$CEBuffer
-android.icu.impl.coll.CollationKeys
-android.icu.impl.coll.CollationKeys$LevelCallback
-android.icu.impl.coll.CollationKeys$SortKeyByteSink
-android.icu.impl.coll.CollationLoader
-android.icu.impl.coll.CollationRoot
-android.icu.impl.coll.CollationSettings
-android.icu.impl.coll.CollationTailoring
-android.icu.impl.coll.ContractionsAndExpansions
-android.icu.impl.coll.FCDUTF16CollationIterator
-android.icu.impl.coll.SharedObject
-android.icu.impl.coll.SharedObject$Reference
-android.icu.impl.coll.UTF16CollationIterator
-android.icu.impl.locale.AsciiUtil
-android.icu.impl.locale.BaseLocale
-android.icu.impl.locale.BaseLocale$Cache
-android.icu.impl.locale.BaseLocale$Key
-android.icu.impl.locale.LocaleObjectCache
-android.icu.impl.locale.LocaleObjectCache$CacheEntry
-android.icu.impl.locale.LocaleSyntaxException
-android.icu.lang.UCharacter
-android.icu.lang.UCharacterEnums$ECharacterCategory
-android.icu.lang.UCharacterEnums$ECharacterDirection
-android.icu.lang.UScript
-android.icu.lang.UScript$ScriptUsage
-android.icu.math.BigDecimal
-android.icu.math.MathContext
-android.icu.text.AlphabeticIndex
-android.icu.text.AlphabeticIndex$1
-android.icu.text.AlphabeticIndex$Bucket
-android.icu.text.AlphabeticIndex$Bucket$LabelType
-android.icu.text.AlphabeticIndex$BucketList
-android.icu.text.AlphabeticIndex$ImmutableIndex
-android.icu.text.Bidi
-android.icu.text.Bidi$ImpTabPair
-android.icu.text.BreakIterator
-android.icu.text.BreakIterator$BreakIteratorCache
-android.icu.text.BreakIterator$BreakIteratorServiceShim
-android.icu.text.BreakIteratorFactory
-android.icu.text.BreakIteratorFactory$BFService
-android.icu.text.BreakIteratorFactory$BFService$1RBBreakIteratorFactory
-android.icu.text.CaseMap
-android.icu.text.CaseMap$Upper
-android.icu.text.CollationKey
-android.icu.text.Collator
-android.icu.text.Collator$ServiceShim
-android.icu.text.CollatorServiceShim
-android.icu.text.CollatorServiceShim$CService
-android.icu.text.CollatorServiceShim$CService$1CollatorFactory
-android.icu.text.CurrencyDisplayNames
-android.icu.text.CurrencyMetaInfo
-android.icu.text.CurrencyMetaInfo$CurrencyDigits
-android.icu.text.CurrencyMetaInfo$CurrencyFilter
-android.icu.text.CurrencyPluralInfo
-android.icu.text.DateFormat
-android.icu.text.DateFormat$BooleanAttribute
-android.icu.text.DateFormat$Field
-android.icu.text.DateFormatSymbols
-android.icu.text.DateFormatSymbols$1
-android.icu.text.DateFormatSymbols$CalendarDataSink
-android.icu.text.DateFormatSymbols$CalendarDataSink$AliasType
-android.icu.text.DateFormatSymbols$CapitalizationContextUsage
-android.icu.text.DateIntervalFormat
-android.icu.text.DateIntervalFormat$BestMatchInfo
-android.icu.text.DateIntervalFormat$SkeletonAndItsBestMatch
-android.icu.text.DateIntervalInfo
-android.icu.text.DateIntervalInfo$DateIntervalSink
-android.icu.text.DateIntervalInfo$PatternInfo
-android.icu.text.DateTimePatternGenerator
-android.icu.text.DateTimePatternGenerator$AppendItemFormatsSink
-android.icu.text.DateTimePatternGenerator$AppendItemNamesSink
-android.icu.text.DateTimePatternGenerator$AvailableFormatsSink
-android.icu.text.DateTimePatternGenerator$DTPGflags
-android.icu.text.DateTimePatternGenerator$DateTimeMatcher
-android.icu.text.DateTimePatternGenerator$DayPeriodAllowedHoursSink
-android.icu.text.DateTimePatternGenerator$DistanceInfo
-android.icu.text.DateTimePatternGenerator$FormatParser
-android.icu.text.DateTimePatternGenerator$PatternInfo
-android.icu.text.DateTimePatternGenerator$PatternWithMatcher
-android.icu.text.DateTimePatternGenerator$PatternWithSkeletonFlag
-android.icu.text.DateTimePatternGenerator$SkeletonFields
-android.icu.text.DateTimePatternGenerator$VariableField
-android.icu.text.DecimalFormat
-android.icu.text.DecimalFormat$Unit
-android.icu.text.DecimalFormatSymbols
-android.icu.text.DecimalFormatSymbols$1
-android.icu.text.DecimalFormatSymbols$CacheData
-android.icu.text.DecimalFormatSymbols$DecFmtDataSink
-android.icu.text.DigitList
-android.icu.text.DisplayContext
-android.icu.text.DisplayContext$Type
-android.icu.text.Edits
-android.icu.text.IDNA
-android.icu.text.LanguageBreakEngine
-android.icu.text.ListFormatter$Style
-android.icu.text.LocaleDisplayNames
-android.icu.text.LocaleDisplayNames$DialectHandling
-android.icu.text.MeasureFormat
-android.icu.text.MeasureFormat$FormatWidth
-android.icu.text.MeasureFormat$ImmutableNumberFormat
-android.icu.text.MeasureFormat$MeasureFormatData
-android.icu.text.MeasureFormat$UnitDataSink
-android.icu.text.Normalizer
-android.icu.text.Normalizer$FCDMode
-android.icu.text.Normalizer$Mode
-android.icu.text.Normalizer$ModeImpl
-android.icu.text.Normalizer$NFCMode
-android.icu.text.Normalizer$NFCModeImpl
-android.icu.text.Normalizer$NFDMode
-android.icu.text.Normalizer$NFKCMode
-android.icu.text.Normalizer$NFKDMode
-android.icu.text.Normalizer$NFKDModeImpl
-android.icu.text.Normalizer$NONEMode
-android.icu.text.Normalizer$QuickCheckResult
-android.icu.text.Normalizer2
-android.icu.text.NumberFormat
-android.icu.text.NumberFormat$Field
-android.icu.text.NumberFormat$NumberFormatShim
-android.icu.text.NumberFormatServiceShim
-android.icu.text.NumberFormatServiceShim$NFService
-android.icu.text.NumberFormatServiceShim$NFService$1RBNumberFormatFactory
-android.icu.text.NumberingSystem
-android.icu.text.NumberingSystem$1
-android.icu.text.NumberingSystem$2
-android.icu.text.NumberingSystem$LocaleLookupData
-android.icu.text.PluralRanges
-android.icu.text.PluralRanges$Matrix
-android.icu.text.PluralRules
-android.icu.text.PluralRules$1
-android.icu.text.PluralRules$AndConstraint
-android.icu.text.PluralRules$BinaryConstraint
-android.icu.text.PluralRules$Constraint
-android.icu.text.PluralRules$Factory
-android.icu.text.PluralRules$FixedDecimal
-android.icu.text.PluralRules$FixedDecimalRange
-android.icu.text.PluralRules$FixedDecimalSamples
-android.icu.text.PluralRules$Operand
-android.icu.text.PluralRules$PluralType
-android.icu.text.PluralRules$RangeConstraint
-android.icu.text.PluralRules$Rule
-android.icu.text.PluralRules$RuleList
-android.icu.text.PluralRules$SampleType
-android.icu.text.PluralRules$SimpleTokenizer
-android.icu.text.QuantityFormatter
-android.icu.text.RBBIDataWrapper
-android.icu.text.RBBIDataWrapper$IsAcceptable
-android.icu.text.RBBIDataWrapper$RBBIDataHeader
-android.icu.text.RBBIDataWrapper$TrieFoldingFunc
-android.icu.text.RawCollationKey
-android.icu.text.RelativeDateTimeFormatter
-android.icu.text.RelativeDateTimeFormatter$AbsoluteUnit
-android.icu.text.RelativeDateTimeFormatter$Cache
-android.icu.text.RelativeDateTimeFormatter$Cache$1
-android.icu.text.RelativeDateTimeFormatter$Direction
-android.icu.text.RelativeDateTimeFormatter$Loader
-android.icu.text.RelativeDateTimeFormatter$RelDateTimeDataSink
-android.icu.text.RelativeDateTimeFormatter$RelDateTimeDataSink$DateTimeUnit
-android.icu.text.RelativeDateTimeFormatter$RelativeDateTimeFormatterData
-android.icu.text.RelativeDateTimeFormatter$RelativeUnit
-android.icu.text.RelativeDateTimeFormatter$Style
-android.icu.text.Replaceable
-android.icu.text.ReplaceableString
-android.icu.text.RuleBasedBreakIterator
-android.icu.text.RuleBasedBreakIterator$LookAheadResults
-android.icu.text.RuleBasedCollator
-android.icu.text.RuleBasedCollator$CollationBuffer
-android.icu.text.RuleBasedCollator$CollationKeyByteSink
-android.icu.text.RuleBasedCollator$FCDUTF16NFDIterator
-android.icu.text.RuleBasedCollator$NFDIterator
-android.icu.text.RuleBasedCollator$UTF16NFDIterator
-android.icu.text.SimpleDateFormat
-android.icu.text.SimpleDateFormat$PatternItem
-android.icu.text.StringPrep
-android.icu.text.StringPrepParseException
-android.icu.text.TimeZoneNames$NameType
-android.icu.text.UCharacterIterator
-android.icu.text.UFieldPosition
-android.icu.text.UFormat
-android.icu.text.UForwardCharacterIterator
-android.icu.text.UTF16
-android.icu.text.UTF16$StringComparator
-android.icu.text.UnhandledBreakEngine
-android.icu.text.UnicodeFilter
-android.icu.text.UnicodeMatcher
-android.icu.text.UnicodeSet
-android.icu.text.UnicodeSet$Filter
-android.icu.text.UnicodeSet$GeneralCategoryMaskFilter
-android.icu.text.UnicodeSet$IntPropertyFilter
-android.icu.text.UnicodeSet$UnicodeSetIterator2
-android.icu.util.BasicTimeZone
-android.icu.util.ByteArrayWrapper
-android.icu.util.BytesTrie
-android.icu.util.BytesTrie$Result
-android.icu.util.Calendar
-android.icu.util.Calendar$CalType
-android.icu.util.Calendar$FormatConfiguration
-android.icu.util.Calendar$PatternData
-android.icu.util.Calendar$WeekData
-android.icu.util.Calendar$WeekDataCache
-android.icu.util.CharsTrie
-android.icu.util.CharsTrie$Entry
-android.icu.util.CharsTrie$Iterator
-android.icu.util.Currency
-android.icu.util.Currency$1
-android.icu.util.Currency$CurrencyUsage
-android.icu.util.Currency$EquivalenceRelation
-android.icu.util.CurrencyAmount
-android.icu.util.Freezable
-android.icu.util.GregorianCalendar
-android.icu.util.ICUException
-android.icu.util.ICUUncheckedIOException
-android.icu.util.LocaleData
-android.icu.util.Measure
-android.icu.util.MeasureUnit
-android.icu.util.MeasureUnit$1
-android.icu.util.MeasureUnit$2
-android.icu.util.MeasureUnit$3
-android.icu.util.MeasureUnit$Factory
-android.icu.util.Output
-android.icu.util.SimpleTimeZone
-android.icu.util.TimeUnit
-android.icu.util.TimeZone
-android.icu.util.TimeZone$ConstantZone
-android.icu.util.ULocale
-android.icu.util.ULocale$1
-android.icu.util.ULocale$2
-android.icu.util.ULocale$Category
-android.icu.util.ULocale$JDKLocaleHelper
-android.icu.util.ULocale$Type
-android.icu.util.UResourceBundle
-android.icu.util.UResourceBundle$RootType
-android.icu.util.UResourceBundleIterator
-android.icu.util.UResourceTypeMismatchException
-android.icu.util.VersionInfo
-android.inputmethodservice.AbstractInputMethodService
-android.inputmethodservice.AbstractInputMethodService$AbstractInputMethodImpl
-android.inputmethodservice.AbstractInputMethodService$AbstractInputMethodSessionImpl
-android.inputmethodservice.IInputMethodSessionWrapper
-android.inputmethodservice.IInputMethodSessionWrapper$ImeInputEventReceiver
-android.inputmethodservice.IInputMethodWrapper
-android.inputmethodservice.IInputMethodWrapper$InputMethodSessionCallbackWrapper
-android.inputmethodservice.InputMethodService
-android.inputmethodservice.InputMethodService$1
-android.inputmethodservice.InputMethodService$2
-android.inputmethodservice.InputMethodService$InputMethodImpl
-android.inputmethodservice.InputMethodService$InputMethodSessionImpl
-android.inputmethodservice.InputMethodService$Insets
-android.inputmethodservice.InputMethodService$SettingsObserver
-android.inputmethodservice.SoftInputWindow
-android.location.Address
-android.location.Address$1
-android.location.BatchedLocationCallbackTransport
-android.location.BatchedLocationCallbackTransport$CallbackTransport
-android.location.Country
-android.location.Country$1
-android.location.CountryDetector
-android.location.CountryDetector$ListenerTransport
-android.location.CountryDetector$ListenerTransport$1
-android.location.CountryListener
-android.location.Criteria
-android.location.Criteria$1
-android.location.Geocoder
-android.location.GeocoderParams
-android.location.GeocoderParams$1
-android.location.Geofence
-android.location.GnssMeasurementCallbackTransport
-android.location.GnssMeasurementCallbackTransport$ListenerTransport
-android.location.GnssNavigationMessageCallbackTransport
-android.location.GnssNavigationMessageCallbackTransport$ListenerTransport
-android.location.GnssStatus
-android.location.GnssStatus$Callback
-android.location.GpsSatellite
-android.location.GpsStatus
-android.location.GpsStatus$1
-android.location.GpsStatus$Listener
-android.location.GpsStatus$SatelliteIterator
-android.location.IBatchedLocationCallback
-android.location.IBatchedLocationCallback$Stub
-android.location.ICountryDetector
-android.location.ICountryDetector$Stub
-android.location.ICountryDetector$Stub$Proxy
-android.location.ICountryListener
-android.location.ICountryListener$Stub
-android.location.ICountryListener$Stub$Proxy
-android.location.IFusedProvider
-android.location.IFusedProvider$Stub
-android.location.IGeocodeProvider
-android.location.IGeocodeProvider$Stub
-android.location.IGeocodeProvider$Stub$Proxy
-android.location.IGeofenceProvider
-android.location.IGeofenceProvider$Stub
-android.location.IGeofenceProvider$Stub$Proxy
-android.location.IGnssMeasurementsListener
-android.location.IGnssMeasurementsListener$Stub
-android.location.IGnssNavigationMessageListener
-android.location.IGnssNavigationMessageListener$Stub
-android.location.IGnssStatusListener
-android.location.IGnssStatusListener$Stub
-android.location.IGnssStatusListener$Stub$Proxy
-android.location.IGnssStatusProvider
-android.location.IGnssStatusProvider$Stub
-android.location.IGpsGeofenceHardware
-android.location.IGpsGeofenceHardware$Stub
-android.location.ILocationListener
-android.location.ILocationListener$Stub
-android.location.ILocationListener$Stub$Proxy
-android.location.ILocationManager
-android.location.ILocationManager$Stub
-android.location.ILocationManager$Stub$Proxy
-android.location.INetInitiatedListener
-android.location.INetInitiatedListener$Stub
-android.location.LocalListenerHelper
-android.location.Location
-android.location.Location$1
-android.location.Location$2
-android.location.Location$BearingDistanceCache
-android.location.LocationListener
-android.location.LocationManager
-android.location.LocationManager$GnssStatusListenerTransport
-android.location.LocationManager$GnssStatusListenerTransport$1
-android.location.LocationManager$GnssStatusListenerTransport$GnssHandler
-android.location.LocationManager$ListenerTransport
-android.location.LocationManager$ListenerTransport$1
-android.location.LocationManager$ListenerTransport$2
-android.location.LocationProvider
-android.location.LocationRequest
-android.location.LocationRequest$1
-android.media.AudioAttributes
-android.media.AudioAttributes$1
-android.media.AudioAttributes$Builder
-android.media.AudioDeviceCallback
-android.media.AudioDeviceInfo
-android.media.AudioDevicePort
-android.media.AudioDevicePortConfig
-android.media.AudioFocusInfo
-android.media.AudioFocusInfo$1
-android.media.AudioFocusRequest
-android.media.AudioFocusRequest$Builder
-android.media.AudioFormat
-android.media.AudioFormat$1
-android.media.AudioFormat$Builder
-android.media.AudioGain
-android.media.AudioGainConfig
-android.media.AudioHandle
-android.media.AudioManager
-android.media.AudioManager$1
-android.media.AudioManager$2
-android.media.AudioManager$3
-android.media.AudioManager$FocusRequestInfo
-android.media.AudioManager$NativeEventHandlerDelegate
-android.media.AudioManager$NativeEventHandlerDelegate$1
-android.media.AudioManager$OnAmPortUpdateListener
-android.media.AudioManager$OnAudioFocusChangeListener
-android.media.AudioManager$OnAudioPortUpdateListener
-android.media.AudioManager$ServiceEventHandlerDelegate
-android.media.AudioManager$ServiceEventHandlerDelegate$1
-android.media.AudioManagerInternal
-android.media.AudioManagerInternal$RingerModeDelegate
-android.media.AudioMixPort
-android.media.AudioMixPortConfig
-android.media.AudioPatch
-android.media.AudioPlaybackConfiguration
-android.media.AudioPlaybackConfiguration$1
-android.media.AudioPlaybackConfiguration$IPlayerShell
-android.media.AudioPlaybackConfiguration$PlayerDeathMonitor
-android.media.AudioPort
-android.media.AudioPortConfig
-android.media.AudioPortEventHandler
-android.media.AudioPortEventHandler$1
-android.media.AudioRecord
-android.media.AudioRoutesInfo
-android.media.AudioRoutesInfo$1
-android.media.AudioRouting
-android.media.AudioSystem
-android.media.AudioSystem$AudioRecordingCallback
-android.media.AudioSystem$DynamicPolicyCallback
-android.media.AudioSystem$ErrorCallback
-android.media.AudioTimestamp
-android.media.AudioTrack
-android.media.BufferingParams
-android.media.BufferingParams$1
-android.media.CamcorderProfile
-android.media.CameraProfile
-android.media.DecoderCapabilities
-android.media.DeniedByServerException
-android.media.EncoderCapabilities
-android.media.ExifInterface
-android.media.ExifInterface$ByteOrderedDataInputStream
-android.media.ExifInterface$ExifAttribute
-android.media.ExifInterface$ExifTag
-android.media.IAudioFocusDispatcher
-android.media.IAudioFocusDispatcher$Stub
-android.media.IAudioFocusDispatcher$Stub$Proxy
-android.media.IAudioRoutesObserver
-android.media.IAudioRoutesObserver$Stub
-android.media.IAudioRoutesObserver$Stub$Proxy
-android.media.IAudioService
-android.media.IAudioService$Stub
-android.media.IAudioService$Stub$Proxy
-android.media.IMediaHTTPConnection
-android.media.IMediaHTTPConnection$Stub
-android.media.IMediaHTTPService
-android.media.IMediaHTTPService$Stub
-android.media.IMediaResourceMonitor
-android.media.IMediaResourceMonitor$Stub
-android.media.IMediaRouterClient
-android.media.IMediaRouterClient$Stub
-android.media.IMediaRouterClient$Stub$Proxy
-android.media.IMediaRouterService
-android.media.IMediaRouterService$Stub
-android.media.IMediaRouterService$Stub$Proxy
-android.media.IPlaybackConfigDispatcher
-android.media.IPlaybackConfigDispatcher$Stub
-android.media.IPlayer
-android.media.IPlayer$Stub
-android.media.IPlayer$Stub$Proxy
-android.media.IRecordingConfigDispatcher
-android.media.IRecordingConfigDispatcher$Stub
-android.media.IRemoteVolumeController
-android.media.IRemoteVolumeController$Stub
-android.media.IRemoteVolumeController$Stub$Proxy
-android.media.IRemoteVolumeObserver
-android.media.IRemoteVolumeObserver$Stub
-android.media.IRingtonePlayer
-android.media.IRingtonePlayer$Stub
-android.media.IRingtonePlayer$Stub$Proxy
-android.media.IVolumeController
-android.media.IVolumeController$Stub
-android.media.IVolumeController$Stub$Proxy
-android.media.Image
-android.media.Image$Plane
-android.media.ImageReader
-android.media.ImageReader$ListenerHandler
-android.media.ImageReader$OnImageAvailableListener
-android.media.ImageReader$SurfaceImage
-android.media.ImageReader$SurfaceImage$SurfacePlane
-android.media.ImageUtils
-android.media.ImageWriter
-android.media.ImageWriter$ListenerHandler
-android.media.ImageWriter$OnImageReleasedListener
-android.media.ImageWriter$WriterSurfaceImage
-android.media.ImageWriter$WriterSurfaceImage$SurfacePlane
-android.media.JetPlayer
-android.media.MediaCodec
-android.media.MediaCodec$BufferInfo
-android.media.MediaCodec$BufferMap
-android.media.MediaCodec$BufferMap$CodecBuffer
-android.media.MediaCodec$CodecException
-android.media.MediaCodec$CryptoException
-android.media.MediaCodec$CryptoInfo
-android.media.MediaCodec$CryptoInfo$Pattern
-android.media.MediaCodec$EventHandler
-android.media.MediaCodec$PersistentSurface
-android.media.MediaCodecInfo
-android.media.MediaCodecInfo$AudioCapabilities
-android.media.MediaCodecInfo$CodecCapabilities
-android.media.MediaCodecInfo$CodecProfileLevel
-android.media.MediaCodecInfo$EncoderCapabilities
-android.media.MediaCodecInfo$Feature
-android.media.MediaCodecInfo$VideoCapabilities
-android.media.MediaCodecList
-android.media.MediaCrypto
-android.media.MediaCryptoException
-android.media.MediaDescrambler
-android.media.MediaDescription
-android.media.MediaDescription$1
-android.media.MediaDescription$Builder
-android.media.MediaDrm
-android.media.MediaDrm$Certificate
-android.media.MediaDrm$EventHandler
-android.media.MediaDrm$KeyRequest
-android.media.MediaDrm$MediaDrmStateException
-android.media.MediaDrm$OnEventListener
-android.media.MediaDrm$ProvisionRequest
-android.media.MediaDrmException
-android.media.MediaExtractor
-android.media.MediaFile
-android.media.MediaFile$MediaFileType
-android.media.MediaFormat
-android.media.MediaHTTPConnection
-android.media.MediaHTTPService
-android.media.MediaMetadata
-android.media.MediaMetadata$1
-android.media.MediaMetadata$Builder
-android.media.MediaMetadataRetriever
-android.media.MediaMuxer
-android.media.MediaPlayer
-android.media.MediaPlayer$1
-android.media.MediaPlayer$2
-android.media.MediaPlayer$4
-android.media.MediaPlayer$4$1
-android.media.MediaPlayer$EventHandler
-android.media.MediaPlayer$OnCompletionListener
-android.media.MediaPlayer$OnErrorListener
-android.media.MediaPlayer$OnInfoListener
-android.media.MediaPlayer$OnPreparedListener
-android.media.MediaPlayer$OnSeekCompleteListener
-android.media.MediaPlayer$OnSubtitleDataListener
-android.media.MediaPlayer$TimeProvider
-android.media.MediaPlayer$TimeProvider$EventHandler
-android.media.MediaPlayer$TrackInfo
-android.media.MediaPlayer$TrackInfo$1
-android.media.MediaRecorder
-android.media.MediaRecorder$EventHandler
-android.media.MediaRecorder$OnErrorListener
-android.media.MediaRouter
-android.media.MediaRouter$Callback
-android.media.MediaRouter$CallbackInfo
-android.media.MediaRouter$RouteCategory
-android.media.MediaRouter$RouteGroup
-android.media.MediaRouter$RouteInfo
-android.media.MediaRouter$RouteInfo$1
-android.media.MediaRouter$SimpleCallback
-android.media.MediaRouter$Static
-android.media.MediaRouter$Static$1
-android.media.MediaRouter$Static$Client
-android.media.MediaRouter$Static$Client$1
-android.media.MediaRouter$VolumeCallback
-android.media.MediaRouter$VolumeChangeReceiver
-android.media.MediaRouter$WifiDisplayStatusChangedReceiver
-android.media.MediaRouterClientState
-android.media.MediaRouterClientState$1
-android.media.MediaRouterClientState$RouteInfo
-android.media.MediaRouterClientState$RouteInfo$1
-android.media.MediaScanner
-android.media.MediaScannerConnection
-android.media.MediaScannerConnection$OnScanCompletedListener
-android.media.MediaSync
-android.media.MediaTimeProvider
-android.media.MediaTimeProvider$OnMediaTimeListener
-android.media.MiniThumbFile
-android.media.NotProvisionedException
-android.media.PlaybackParams
-android.media.PlaybackParams$1
-android.media.PlayerBase
-android.media.PlayerBase$IAppOpsCallbackWrapper
-android.media.PlayerBase$IPlayerWrapper
-android.media.PlayerBase$PlayerIdCard
-android.media.PlayerBase$PlayerIdCard$1
-android.media.PlayerProxy
-android.media.Rating
-android.media.Rating$1
-android.media.RemoteDisplay
-android.media.ResampleInputStream
-android.media.SoundPool
-android.media.SoundPool$Builder
-android.media.SoundPool$EventHandler
-android.media.SoundPool$OnLoadCompleteListener
-android.media.SubtitleController
-android.media.SubtitleController$1
-android.media.SubtitleController$2
-android.media.SubtitleController$Anchor
-android.media.SubtitleController$Listener
-android.media.SubtitleTrack
-android.media.SyncParams
-android.media.ThumbnailUtils
-android.media.ThumbnailUtils$SizedThumbnailBitmap
-android.media.ToneGenerator
-android.media.UnsupportedSchemeException
-android.media.Utils
-android.media.Utils$1
-android.media.Utils$2
-android.media.VolumeAutomation
-android.media.VolumePolicy
-android.media.VolumePolicy$1
-android.media.VolumeShaper
-android.media.VolumeShaper$Configuration
-android.media.VolumeShaper$Configuration$1
-android.media.VolumeShaper$Configuration$Builder
-android.media.VolumeShaper$Operation
-android.media.VolumeShaper$Operation$1
-android.media.VolumeShaper$Operation$Builder
-android.media.VolumeShaper$State
-android.media.VolumeShaper$State$1
-android.media.audiofx.AudioEffect
-android.media.audiofx.AudioEffect$Descriptor
-android.media.audiofx.LoudnessEnhancer
-android.media.audiofx.Virtualizer
-android.media.audiofx.Visualizer
-android.media.audiopolicy.AudioMix
-android.media.audiopolicy.AudioMixingRule
-android.media.audiopolicy.AudioMixingRule$AudioMixMatchCriterion
-android.media.audiopolicy.AudioPolicyConfig
-android.media.audiopolicy.IAudioPolicyCallback
-android.media.audiopolicy.IAudioPolicyCallback$Stub
-android.media.browse.MediaBrowser
-android.media.browse.MediaBrowser$1
-android.media.browse.MediaBrowser$2
-android.media.browse.MediaBrowser$6
-android.media.browse.MediaBrowser$ConnectionCallback
-android.media.browse.MediaBrowser$MediaServiceConnection
-android.media.browse.MediaBrowser$MediaServiceConnection$1
-android.media.browse.MediaBrowser$ServiceCallbacks
-android.media.midi.IMidiDeviceListener
-android.media.midi.IMidiDeviceOpenCallback
-android.media.midi.IMidiDeviceServer
-android.media.midi.IMidiManager
-android.media.midi.IMidiManager$Stub
-android.media.midi.MidiDeviceInfo
-android.media.midi.MidiDeviceStatus
-android.media.midi.MidiManager
-android.media.projection.IMediaProjection
-android.media.projection.IMediaProjectionManager
-android.media.projection.IMediaProjectionManager$Stub
-android.media.projection.IMediaProjectionManager$Stub$Proxy
-android.media.projection.IMediaProjectionWatcherCallback
-android.media.projection.IMediaProjectionWatcherCallback$Stub
-android.media.projection.IMediaProjectionWatcherCallback$Stub$Proxy
-android.media.projection.MediaProjectionInfo
-android.media.projection.MediaProjectionManager
-android.media.projection.MediaProjectionManager$Callback
-android.media.projection.MediaProjectionManager$CallbackDelegate
-android.media.session.IActiveSessionsListener
-android.media.session.IActiveSessionsListener$Stub
-android.media.session.IActiveSessionsListener$Stub$Proxy
-android.media.session.ICallback
-android.media.session.ICallback$Stub
-android.media.session.ICallback$Stub$Proxy
-android.media.session.IOnMediaKeyListener
-android.media.session.IOnVolumeKeyLongPressListener
-android.media.session.ISession
-android.media.session.ISession$Stub
-android.media.session.ISession$Stub$Proxy
-android.media.session.ISessionCallback
-android.media.session.ISessionCallback$Stub
-android.media.session.ISessionCallback$Stub$Proxy
-android.media.session.ISessionController
-android.media.session.ISessionController$Stub
-android.media.session.ISessionController$Stub$Proxy
-android.media.session.ISessionControllerCallback
-android.media.session.ISessionControllerCallback$Stub
-android.media.session.ISessionControllerCallback$Stub$Proxy
-android.media.session.ISessionManager
-android.media.session.ISessionManager$Stub
-android.media.session.ISessionManager$Stub$Proxy
-android.media.session.MediaController
-android.media.session.MediaController$Callback
-android.media.session.MediaController$CallbackStub
-android.media.session.MediaController$MessageHandler
-android.media.session.MediaController$PlaybackInfo
-android.media.session.MediaController$TransportControls
-android.media.session.MediaSession
-android.media.session.MediaSession$Callback
-android.media.session.MediaSession$CallbackMessageHandler
-android.media.session.MediaSession$CallbackStub
-android.media.session.MediaSession$QueueItem
-android.media.session.MediaSession$QueueItem$1
-android.media.session.MediaSession$Token
-android.media.session.MediaSession$Token$1
-android.media.session.MediaSessionManager
-android.media.session.MediaSessionManager$Callback
-android.media.session.MediaSessionManager$CallbackImpl
-android.media.session.MediaSessionManager$CallbackImpl$3
-android.media.session.MediaSessionManager$CallbackImpl$4
-android.media.session.MediaSessionManager$OnActiveSessionsChangedListener
-android.media.session.MediaSessionManager$SessionsChangedWrapper
-android.media.session.MediaSessionManager$SessionsChangedWrapper$1
-android.media.session.MediaSessionManager$SessionsChangedWrapper$1$1
-android.media.session.ParcelableVolumeInfo
-android.media.session.ParcelableVolumeInfo$1
-android.media.session.PlaybackState
-android.media.session.PlaybackState$1
-android.media.session.PlaybackState$Builder
-android.media.session.PlaybackState$CustomAction
-android.media.session.PlaybackState$CustomAction$1
-android.media.session.PlaybackState$CustomAction$Builder
-android.media.soundtrigger.SoundTriggerManager
-android.media.tv.TvInputHardwareInfo$Builder
-android.media.tv.TvInputManager
-android.media.tv.TvStreamConfig
-android.media.tv.TvStreamConfig$Builder
-android.metrics.LogMaker
-android.mtp.MtpDatabase
-android.mtp.MtpDevice
-android.mtp.MtpDeviceInfo
-android.mtp.MtpEvent
-android.mtp.MtpObjectInfo
-android.mtp.MtpPropertyGroup
-android.mtp.MtpPropertyList
-android.mtp.MtpServer
-android.mtp.MtpStorage
-android.mtp.MtpStorageInfo
-android.net.ConnectivityManager
-android.net.ConnectivityManager$CallbackHandler
-android.net.ConnectivityManager$NetworkCallback
-android.net.ConnectivityManager$OnNetworkActiveListener
-android.net.ConnectivityManager$PacketKeepaliveCallback
-android.net.ConnectivityMetricsEvent
-android.net.ConnectivityMetricsEvent$1
-android.net.ConnectivityThread
-android.net.ConnectivityThread$Singleton
-android.net.Credentials
-android.net.DataUsageRequest
-android.net.DhcpInfo
-android.net.DhcpInfo$1
-android.net.DhcpResults
-android.net.DhcpResults$1
-android.net.EthernetManager
-android.net.EthernetManager$1
-android.net.EthernetManager$2
-android.net.EventLogTags
-android.net.IConnectivityManager
-android.net.IConnectivityManager$Stub
-android.net.IConnectivityManager$Stub$Proxy
-android.net.IEthernetManager
-android.net.IEthernetManager$Stub
-android.net.IEthernetServiceListener
-android.net.IEthernetServiceListener$Stub
-android.net.IIpConnectivityMetrics
-android.net.IIpConnectivityMetrics$Stub
-android.net.IIpSecService
-android.net.IIpSecService$Stub
-android.net.INetd
-android.net.INetd$Stub
-android.net.INetd$Stub$Proxy
-android.net.INetdEventCallback
-android.net.INetworkManagementEventObserver
-android.net.INetworkManagementEventObserver$Stub
-android.net.INetworkPolicyListener
-android.net.INetworkPolicyListener$Stub
-android.net.INetworkPolicyListener$Stub$Proxy
-android.net.INetworkPolicyManager
-android.net.INetworkPolicyManager$Stub
-android.net.INetworkPolicyManager$Stub$Proxy
-android.net.INetworkRecommendationProvider
-android.net.INetworkRecommendationProvider$Stub
-android.net.INetworkRecommendationProvider$Stub$Proxy
-android.net.INetworkScoreCache
-android.net.INetworkScoreCache$Stub
-android.net.INetworkScoreCache$Stub$Proxy
-android.net.INetworkScoreService
-android.net.INetworkScoreService$Stub
-android.net.INetworkScoreService$Stub$Proxy
-android.net.INetworkStatsService
-android.net.INetworkStatsService$Stub
-android.net.INetworkStatsService$Stub$Proxy
-android.net.INetworkStatsSession
-android.net.InterfaceConfiguration
-android.net.InterfaceConfiguration$1
-android.net.IpConfiguration
-android.net.IpConfiguration$1
-android.net.IpConfiguration$IpAssignment
-android.net.IpConfiguration$ProxySettings
-android.net.IpPrefix
-android.net.IpPrefix$1
-android.net.IpSecConfig
-android.net.IpSecManager
-android.net.LinkAddress
-android.net.LinkAddress$1
-android.net.LinkProperties
-android.net.LinkProperties$1
-android.net.LinkProperties$CompareResult
-android.net.LinkProperties$ProvisioningChange
-android.net.LocalServerSocket
-android.net.LocalSocket
-android.net.LocalSocketAddress
-android.net.LocalSocketAddress$Namespace
-android.net.LocalSocketImpl
-android.net.LocalSocketImpl$SocketInputStream
-android.net.LocalSocketImpl$SocketOutputStream
-android.net.MatchAllNetworkSpecifier
-android.net.MatchAllNetworkSpecifier$1
-android.net.Network
-android.net.Network$1
-android.net.Network$2
-android.net.Network$NetworkBoundSocketFactory
-android.net.NetworkAgent
-android.net.NetworkCapabilities
-android.net.NetworkCapabilities$1
-android.net.NetworkConfig
-android.net.NetworkFactory
-android.net.NetworkFactory$NetworkRequestInfo
-android.net.NetworkIdentity
-android.net.NetworkInfo
-android.net.NetworkInfo$1
-android.net.NetworkInfo$DetailedState
-android.net.NetworkInfo$State
-android.net.NetworkKey
-android.net.NetworkKey$1
-android.net.NetworkMisc
-android.net.NetworkMisc$1
-android.net.NetworkPolicy
-android.net.NetworkPolicy$1
-android.net.NetworkPolicyManager
-android.net.NetworkQuotaInfo
-android.net.NetworkRecommendationProvider
-android.net.NetworkRecommendationProvider$ServiceWrapper
-android.net.NetworkRecommendationProvider$ServiceWrapper$1
-android.net.NetworkRequest
-android.net.NetworkRequest$1
-android.net.NetworkRequest$Builder
-android.net.NetworkRequest$Type
-android.net.NetworkScoreManager
-android.net.NetworkScorerAppData
-android.net.NetworkScorerAppData$1
-android.net.NetworkSpecifier
-android.net.NetworkState
-android.net.NetworkState$1
-android.net.NetworkStats
-android.net.NetworkStats$1
-android.net.NetworkStats$Entry
-android.net.NetworkStats$NonMonotonicObserver
-android.net.NetworkStatsHistory
-android.net.NetworkStatsHistory$1
-android.net.NetworkStatsHistory$DataStreamUtils
-android.net.NetworkStatsHistory$Entry
-android.net.NetworkTemplate
-android.net.NetworkTemplate$1
-android.net.NetworkUtils
-android.net.ParseException
-android.net.Proxy
-android.net.ProxyInfo
-android.net.ProxyInfo$1
-android.net.RouteInfo
-android.net.RouteInfo$1
-android.net.RssiCurve
-android.net.RssiCurve$1
-android.net.SSLCertificateSocketFactory
-android.net.SSLCertificateSocketFactory$1
-android.net.SSLSessionCache
-android.net.ScoredNetwork
-android.net.ScoredNetwork$1
-android.net.SntpClient
-android.net.StaticIpConfiguration
-android.net.StaticIpConfiguration$1
-android.net.StringNetworkSpecifier
-android.net.StringNetworkSpecifier$1
-android.net.TrafficStats
-android.net.UidRange
-android.net.Uri
-android.net.Uri$1
-android.net.Uri$AbstractHierarchicalUri
-android.net.Uri$AbstractPart
-android.net.Uri$Builder
-android.net.Uri$HierarchicalUri
-android.net.Uri$OpaqueUri
-android.net.Uri$Part
-android.net.Uri$Part$EmptyPart
-android.net.Uri$PathPart
-android.net.Uri$PathSegments
-android.net.Uri$PathSegmentsBuilder
-android.net.Uri$StringUri
-android.net.WebAddress
-android.net.WifiKey
-android.net.WifiKey$1
-android.net.http.AndroidHttpClient
-android.net.http.AndroidHttpClient$1
-android.net.http.AndroidHttpClient$2
-android.net.http.AndroidHttpClient$CurlLogger
-android.net.http.AndroidHttpClient$LoggingConfiguration
-android.net.http.HttpResponseCache
-android.net.http.SslCertificate
-android.net.http.X509TrustManagerExtensions
-android.net.metrics.ApfProgramEvent
-android.net.metrics.ApfProgramEvent$1
-android.net.metrics.ApfStats
-android.net.metrics.ApfStats$1
-android.net.metrics.ConnectStats
-android.net.metrics.DefaultNetworkEvent
-android.net.metrics.DefaultNetworkEvent$1
-android.net.metrics.DhcpClientEvent
-android.net.metrics.DhcpClientEvent$1
-android.net.metrics.DnsEvent
-android.net.metrics.IpConnectivityLog
-android.net.metrics.IpManagerEvent
-android.net.metrics.IpManagerEvent$1
-android.net.metrics.NetworkEvent
-android.net.metrics.NetworkEvent$1
-android.net.metrics.RaEvent
-android.net.metrics.RaEvent$1
-android.net.metrics.RaEvent$Builder
-android.net.metrics.ValidationProbeEvent
-android.net.metrics.ValidationProbeEvent$1
-android.net.metrics.ValidationProbeEvent$Decoder
-android.net.nsd.INsdManager
-android.net.nsd.INsdManager$Stub
-android.net.nsd.NsdManager
-android.net.sip.ISipService
-android.net.sip.ISipService$Stub
-android.net.sip.SipManager
-android.net.wifi.IRttManager
-android.net.wifi.IRttManager$Stub
-android.net.wifi.IWifiManager
-android.net.wifi.IWifiManager$Stub
-android.net.wifi.IWifiManager$Stub$Proxy
-android.net.wifi.IWifiScanner
-android.net.wifi.IWifiScanner$Stub
-android.net.wifi.IWifiScanner$Stub$Proxy
-android.net.wifi.ParcelUtil
-android.net.wifi.RttManager
-android.net.wifi.RttManager$RttCapabilities
-android.net.wifi.RttManager$RttListener
-android.net.wifi.RttManager$RttResult
-android.net.wifi.ScanResult
-android.net.wifi.ScanResult$1
-android.net.wifi.ScanResult$InformationElement
-android.net.wifi.ScanSettings
-android.net.wifi.SupplicantState
-android.net.wifi.SupplicantState$1
-android.net.wifi.WifiActivityEnergyInfo
-android.net.wifi.WifiActivityEnergyInfo$1
-android.net.wifi.WifiConfiguration
-android.net.wifi.WifiConfiguration$1
-android.net.wifi.WifiConfiguration$KeyMgmt
-android.net.wifi.WifiConfiguration$NetworkSelectionStatus
-android.net.wifi.WifiConfiguration$Visibility
-android.net.wifi.WifiConnectionStatistics
-android.net.wifi.WifiConnectionStatistics$1
-android.net.wifi.WifiEnterpriseConfig
-android.net.wifi.WifiEnterpriseConfig$1
-android.net.wifi.WifiInfo
-android.net.wifi.WifiInfo$1
-android.net.wifi.WifiLinkLayerStats
-android.net.wifi.WifiLinkLayerStats$1
-android.net.wifi.WifiManager
-android.net.wifi.WifiManager$ActionListener
-android.net.wifi.WifiManager$MulticastLock
-android.net.wifi.WifiManager$WifiLock
-android.net.wifi.WifiNetworkScoreCache
-android.net.wifi.WifiNetworkScoreCache$CacheListener
-android.net.wifi.WifiNetworkScoreCache$CacheListener$1
-android.net.wifi.WifiScanner
-android.net.wifi.WifiScanner$ActionListener
-android.net.wifi.WifiScanner$ChannelSpec
-android.net.wifi.WifiScanner$ParcelableScanData
-android.net.wifi.WifiScanner$ParcelableScanData$1
-android.net.wifi.WifiScanner$ParcelableScanResults
-android.net.wifi.WifiScanner$ParcelableScanResults$1
-android.net.wifi.WifiScanner$PnoScanListener
-android.net.wifi.WifiScanner$ScanData
-android.net.wifi.WifiScanner$ScanData$1
-android.net.wifi.WifiScanner$ScanListener
-android.net.wifi.WifiScanner$ScanSettings
-android.net.wifi.WifiScanner$ScanSettings$1
-android.net.wifi.WifiScanner$ScanSettings$HiddenNetwork
-android.net.wifi.WifiScanner$ServiceHandler
-android.net.wifi.WifiSsid
-android.net.wifi.WifiSsid$1
-android.net.wifi.WpsInfo
-android.net.wifi.WpsInfo$1
-android.net.wifi.aware.WifiAwareManager
-android.net.wifi.hotspot2.PasspointConfiguration
-android.net.wifi.p2p.IWifiP2pManager
-android.net.wifi.p2p.IWifiP2pManager$Stub
-android.net.wifi.p2p.WifiP2pConfig
-android.net.wifi.p2p.WifiP2pConfig$1
-android.net.wifi.p2p.WifiP2pDevice
-android.net.wifi.p2p.WifiP2pDevice$1
-android.net.wifi.p2p.WifiP2pDeviceList
-android.net.wifi.p2p.WifiP2pDeviceList$1
-android.net.wifi.p2p.WifiP2pGroup
-android.net.wifi.p2p.WifiP2pGroup$1
-android.net.wifi.p2p.WifiP2pGroupList
-android.net.wifi.p2p.WifiP2pGroupList$1
-android.net.wifi.p2p.WifiP2pGroupList$2
-android.net.wifi.p2p.WifiP2pGroupList$GroupDeleteListener
-android.net.wifi.p2p.WifiP2pInfo
-android.net.wifi.p2p.WifiP2pInfo$1
-android.net.wifi.p2p.WifiP2pManager
-android.net.wifi.p2p.WifiP2pWfdInfo
-android.net.wifi.p2p.WifiP2pWfdInfo$1
-android.nfc.BeamShareData
-android.nfc.FormatException
-android.nfc.IAppCallback
-android.nfc.IAppCallback$Stub
-android.nfc.IAppCallback$Stub$Proxy
-android.nfc.INfcAdapter
-android.nfc.INfcAdapter$Stub
-android.nfc.INfcAdapter$Stub$Proxy
-android.nfc.INfcAdapterExtras
-android.nfc.INfcCardEmulation
-android.nfc.INfcCardEmulation$Stub
-android.nfc.INfcCardEmulation$Stub$Proxy
-android.nfc.INfcFCardEmulation
-android.nfc.INfcFCardEmulation$Stub
-android.nfc.INfcFCardEmulation$Stub$Proxy
-android.nfc.INfcTag
-android.nfc.INfcTag$Stub
-android.nfc.INfcTag$Stub$Proxy
-android.nfc.INfcUnlockHandler
-android.nfc.ITagRemovedCallback
-android.nfc.NdefMessage
-android.nfc.NfcActivityManager
-android.nfc.NfcActivityManager$NfcActivityState
-android.nfc.NfcActivityManager$NfcApplicationState
-android.nfc.NfcAdapter
-android.nfc.NfcAdapter$1
-android.nfc.NfcAdapter$CreateBeamUrisCallback
-android.nfc.NfcAdapter$CreateNdefMessageCallback
-android.nfc.NfcAdapter$OnNdefPushCompleteCallback
-android.nfc.NfcEvent
-android.nfc.NfcManager
-android.nfc.Tag
-android.nfc.TechListParcel
-android.nfc.TransceiveResult
-android.nfc.cardemulation.AidGroup
-android.nfc.cardemulation.AidGroup$1
-android.nfc.cardemulation.ApduServiceInfo
-android.nfc.cardemulation.ApduServiceInfo$1
-android.nfc.cardemulation.CardEmulation
-android.nfc.cardemulation.HostApduService
-android.nfc.cardemulation.HostApduService$MsgHandler
-android.opengl.EGL14
-android.opengl.EGLConfig
-android.opengl.EGLContext
-android.opengl.EGLDisplay
-android.opengl.EGLExt
-android.opengl.EGLObjectHandle
-android.opengl.EGLSurface
-android.opengl.ETC1
-android.opengl.GLES10
-android.opengl.GLES10Ext
-android.opengl.GLES11
-android.opengl.GLES11Ext
-android.opengl.GLES20
-android.opengl.GLES30
-android.opengl.GLES31
-android.opengl.GLES31Ext
-android.opengl.GLES32
-android.opengl.GLUtils
-android.opengl.Matrix
-android.opengl.Visibility
-android.os.-$Lambda$-dncxFEc2F2bgG2fsIoC6FC6WNE
-android.os.-$Lambda$-dncxFEc2F2bgG2fsIoC6FC6WNE$1
-android.os.-$Lambda$6x30vPJhBKUfNY8tswxuZo3DCe0
-android.os.AsyncResult
-android.os.AsyncTask
-android.os.AsyncTask$1
-android.os.AsyncTask$2
-android.os.AsyncTask$3
-android.os.AsyncTask$AsyncTaskResult
-android.os.AsyncTask$InternalHandler
-android.os.AsyncTask$SerialExecutor
-android.os.AsyncTask$SerialExecutor$1
-android.os.AsyncTask$Status
-android.os.AsyncTask$WorkerRunnable
-android.os.BadParcelableException
-android.os.BaseBundle
-android.os.BaseBundle$NoImagePreloadHolder
-android.os.BatteryManager
-android.os.BatteryManagerInternal
-android.os.BatteryProperties
-android.os.BatteryProperties$1
-android.os.BatteryStats
-android.os.BatteryStats$BitDescription
-android.os.BatteryStats$ControllerActivityCounter
-android.os.BatteryStats$Counter
-android.os.BatteryStats$DailyItem
-android.os.BatteryStats$HistoryEventTracker
-android.os.BatteryStats$HistoryItem
-android.os.BatteryStats$HistoryStepDetails
-android.os.BatteryStats$HistoryTag
-android.os.BatteryStats$IntToString
-android.os.BatteryStats$LevelStepTracker
-android.os.BatteryStats$LongCounter
-android.os.BatteryStats$LongCounterArray
-android.os.BatteryStats$PackageChange
-android.os.BatteryStats$Timer
-android.os.BatteryStats$Uid
-android.os.BatteryStats$Uid$Pid
-android.os.BatteryStats$Uid$Pkg
-android.os.BatteryStats$Uid$Pkg$Serv
-android.os.BatteryStats$Uid$Sensor
-android.os.BatteryStats$Uid$Wakelock
-android.os.Binder
-android.os.BinderProxy
-android.os.Build
-android.os.Build$VERSION
-android.os.Bundle
-android.os.Bundle$1
-android.os.CancellationSignal
-android.os.CancellationSignal$OnCancelListener
-android.os.CancellationSignal$Transport
-android.os.CommonTimeConfig$OnServerDiedListener
-android.os.ConditionVariable
-android.os.CountDownTimer
-android.os.CpuUsageInfo
-android.os.CpuUsageInfo$1
-android.os.DeadObjectException
-android.os.DeadSystemException
-android.os.Debug
-android.os.Debug$MemoryInfo
-android.os.Debug$MemoryInfo$1
-android.os.DropBoxManager
-android.os.DropBoxManager$Entry
-android.os.DropBoxManager$Entry$1
-android.os.Environment
-android.os.Environment$UserEnvironment
-android.os.FactoryTest
-android.os.FileBridge
-android.os.FileBridge$FileBridgeOutputStream
-android.os.FileObserver$ObserverThread
-android.os.FileUtils
-android.os.GraphicsEnvironment
-android.os.Handler
-android.os.Handler$BlockingRunnable
-android.os.Handler$Callback
-android.os.Handler$MessengerImpl
-android.os.HandlerThread
-android.os.HardwarePropertiesManager
-android.os.HwBinder
-android.os.HwBlob
-android.os.HwParcel
-android.os.HwRemoteBinder
-android.os.IBatteryPropertiesListener
-android.os.IBatteryPropertiesListener$Stub
-android.os.IBatteryPropertiesRegistrar
-android.os.IBatteryPropertiesRegistrar$Stub
-android.os.IBatteryPropertiesRegistrar$Stub$Proxy
-android.os.IBinder
-android.os.IBinder$DeathRecipient
-android.os.ICancellationSignal
-android.os.ICancellationSignal$Stub
-android.os.ICancellationSignal$Stub$Proxy
-android.os.IDeviceIdentifiersPolicyService
-android.os.IDeviceIdentifiersPolicyService$Stub
-android.os.IDeviceIdleController
-android.os.IDeviceIdleController$Stub
-android.os.IDeviceIdleController$Stub$Proxy
-android.os.IHardwarePropertiesManager
-android.os.IHardwarePropertiesManager$Stub
-android.os.IHardwarePropertiesManager$Stub$Proxy
-android.os.IHwBinder
-android.os.IHwBinder$DeathRecipient
-android.os.IHwInterface
-android.os.IIncidentManager
-android.os.IIncidentManager$Stub
-android.os.IInstalld
-android.os.IInstalld$Stub
-android.os.IInstalld$Stub$Proxy
-android.os.IInterface
-android.os.IMaintenanceActivityListener
-android.os.IMessenger
-android.os.IMessenger$Stub
-android.os.IMessenger$Stub$Proxy
-android.os.INetworkActivityListener
-android.os.INetworkManagementService
-android.os.INetworkManagementService$Stub
-android.os.INetworkManagementService$Stub$Proxy
-android.os.IPermissionController
-android.os.IPermissionController$Stub
-android.os.IPowerManager
-android.os.IPowerManager$Stub
-android.os.IPowerManager$Stub$Proxy
-android.os.IProcessInfoService
-android.os.IProcessInfoService$Stub
-android.os.IProgressListener
-android.os.IRecoverySystem
-android.os.IRecoverySystem$Stub
-android.os.IRecoverySystemProgressListener
-android.os.IRemoteCallback
-android.os.IRemoteCallback$Stub
-android.os.IRemoteCallback$Stub$Proxy
-android.os.ISchedulingPolicyService
-android.os.ISchedulingPolicyService$Stub
-android.os.IServiceManager
-android.os.IUpdateLock
-android.os.IUpdateLock$Stub
-android.os.IUserManager
-android.os.IUserManager$Stub
-android.os.IUserManager$Stub$Proxy
-android.os.IVibratorService
-android.os.IVibratorService$Stub
-android.os.IVibratorService$Stub$Proxy
-android.os.IncidentManager
-android.os.LocaleList
-android.os.LocaleList$1
-android.os.Looper
-android.os.MemoryFile
-android.os.Message
-android.os.Message$1
-android.os.MessageQueue
-android.os.MessageQueue$FileDescriptorRecord
-android.os.MessageQueue$IdleHandler
-android.os.MessageQueue$OnFileDescriptorEventListener
-android.os.Messenger
-android.os.Messenger$1
-android.os.OperationCanceledException
-android.os.Parcel
-android.os.Parcel$1
-android.os.Parcel$2
-android.os.Parcel$ReadWriteHelper
-android.os.ParcelFileDescriptor
-android.os.ParcelFileDescriptor$1
-android.os.ParcelFileDescriptor$2
-android.os.ParcelFileDescriptor$AutoCloseInputStream
-android.os.ParcelFileDescriptor$AutoCloseOutputStream
-android.os.ParcelFileDescriptor$OnCloseListener
-android.os.ParcelFileDescriptor$Status
-android.os.ParcelFormatException
-android.os.ParcelUuid
-android.os.ParcelUuid$1
-android.os.Parcelable
-android.os.Parcelable$ClassLoaderCreator
-android.os.Parcelable$Creator
-android.os.ParcelableException
-android.os.ParcelableParcel
-android.os.ParcelableParcel$1
-android.os.PatternMatcher
-android.os.PatternMatcher$1
-android.os.PersistableBundle
-android.os.PersistableBundle$1
-android.os.PersistableBundle$MyReadMapCallback
-android.os.PooledStringReader
-android.os.PooledStringWriter
-android.os.PowerManager
-android.os.PowerManager$WakeLock
-android.os.PowerManager$WakeLock$1
-android.os.PowerManagerInternal
-android.os.PowerManagerInternal$LowPowerModeListener
-android.os.PowerSaveState
-android.os.PowerSaveState$1
-android.os.PowerSaveState$Builder
-android.os.Process
-android.os.Process$ProcessStartResult
-android.os.RecoverySystem
-android.os.RecoverySystem$ProgressListener
-android.os.Registrant
-android.os.RegistrantList
-android.os.RemoteCallback
-android.os.RemoteCallback$1
-android.os.RemoteCallback$2
-android.os.RemoteCallback$OnResultListener
-android.os.RemoteCallbackList
-android.os.RemoteCallbackList$Callback
-android.os.RemoteException
-android.os.ResultReceiver
-android.os.ResultReceiver$1
-android.os.ResultReceiver$MyResultReceiver
-android.os.ResultReceiver$MyRunnable
-android.os.SELinux
-android.os.Seccomp
-android.os.ServiceManager
-android.os.ServiceManager$ServiceNotFoundException
-android.os.ServiceManagerNative
-android.os.ServiceManagerProxy
-android.os.ServiceSpecificException
-android.os.ShellCallback
-android.os.StatFs
-android.os.StrictMode
-android.os.StrictMode$1
-android.os.StrictMode$2
-android.os.StrictMode$3
-android.os.StrictMode$4
-android.os.StrictMode$5
-android.os.StrictMode$6
-android.os.StrictMode$7
-android.os.StrictMode$8
-android.os.StrictMode$9
-android.os.StrictMode$AndroidBlockGuardPolicy
-android.os.StrictMode$AndroidBlockGuardPolicy$1
-android.os.StrictMode$AndroidCloseGuardReporter
-android.os.StrictMode$InstanceTracker
-android.os.StrictMode$Span
-android.os.StrictMode$ThreadPolicy
-android.os.StrictMode$ThreadPolicy$Builder
-android.os.StrictMode$ThreadSpanState
-android.os.StrictMode$ViolationInfo
-android.os.StrictMode$ViolationInfo$1
-android.os.StrictMode$VmPolicy
-android.os.StrictMode$VmPolicy$Builder
-android.os.SynchronousResultReceiver
-android.os.SynchronousResultReceiver$Result
-android.os.SystemClock
-android.os.SystemProperties
-android.os.SystemService
-android.os.SystemService$1
-android.os.SystemService$State
-android.os.SystemVibrator
-android.os.TokenWatcher
-android.os.TokenWatcher$1
-android.os.Trace
-android.os.Trace$1
-android.os.TransactionTooLargeException
-android.os.UEventObserver
-android.os.UEventObserver$UEvent
-android.os.UEventObserver$UEventThread
-android.os.UpdateLock
-android.os.UserHandle
-android.os.UserHandle$1
-android.os.UserManager
-android.os.UserManager$EnforcingUser
-android.os.UserManager$EnforcingUser$1
-android.os.UserManagerInternal
-android.os.UserManagerInternal$UserRestrictionsListener
-android.os.VibrationEffect
-android.os.VibrationEffect$1
-android.os.VibrationEffect$OneShot
-android.os.VibrationEffect$Prebaked
-android.os.VibrationEffect$Prebaked$1
-android.os.VibrationEffect$Waveform
-android.os.VibrationEffect$Waveform$1
-android.os.Vibrator
-android.os.VintfObject
-android.os.VintfRuntimeInfo
-android.os.WorkSource
-android.os.WorkSource$1
-android.os.ZygoteProcess
-android.os.ZygoteProcess$ZygoteState
-android.os.ZygoteStartFailedEx
-android.os.health.HealthStatsParceler
-android.os.health.SystemHealthManager
-android.os.storage.DiskInfo
-android.os.storage.DiskInfo$1
-android.os.storage.IObbActionListener
-android.os.storage.IObbActionListener$Stub
-android.os.storage.IStorageEventListener
-android.os.storage.IStorageEventListener$Stub
-android.os.storage.IStorageEventListener$Stub$Proxy
-android.os.storage.IStorageManager
-android.os.storage.IStorageManager$Stub
-android.os.storage.IStorageManager$Stub$Proxy
-android.os.storage.IStorageShutdownObserver
-android.os.storage.StorageEventListener
-android.os.storage.StorageManager
-android.os.storage.StorageManager$ObbActionListener
-android.os.storage.StorageManager$StorageEventListenerDelegate
-android.os.storage.StorageManagerInternal
-android.os.storage.StorageManagerInternal$ExternalStorageMountPolicy
-android.os.storage.StorageVolume
-android.os.storage.StorageVolume$1
-android.os.storage.VolumeInfo
-android.os.storage.VolumeInfo$1
-android.os.storage.VolumeInfo$2
-android.os.storage.VolumeRecord
-android.os.storage.VolumeRecord$1
-android.preference.GenericInflater$Parent
-android.preference.Preference
-android.preference.Preference$BaseSavedState
-android.preference.Preference$BaseSavedState$1
-android.preference.Preference$OnPreferenceChangeListener
-android.preference.PreferenceActivity
-android.preference.PreferenceFragment
-android.preference.PreferenceFragment$OnPreferenceStartFragmentCallback
-android.preference.PreferenceGroup
-android.preference.PreferenceManager
-android.preference.PreferenceManager$OnPreferenceTreeClickListener
-android.preference.PreferenceScreen
-android.print.IPrintDocumentAdapter
-android.print.IPrintJobStateChangeListener
-android.print.IPrintManager
-android.print.IPrintManager$Stub
-android.print.IPrintServicesChangeListener
-android.print.IPrintSpooler
-android.print.IPrintSpooler$Stub
-android.print.IPrintSpooler$Stub$Proxy
-android.print.IPrintSpoolerCallbacks
-android.print.IPrintSpoolerCallbacks$Stub
-android.print.IPrintSpoolerClient
-android.print.IPrintSpoolerClient$Stub
-android.print.IPrinterDiscoveryObserver
-android.print.PageRange
-android.print.PrintAttributes
-android.print.PrintDocumentAdapter
-android.print.PrintDocumentAdapter$LayoutResultCallback
-android.print.PrintDocumentAdapter$WriteResultCallback
-android.print.PrintJobId
-android.print.PrintJobInfo
-android.print.PrintManager
-android.print.PrinterId
-android.printservice.IPrintServiceClient
-android.printservice.IPrintServiceClient$Stub
-android.printservice.PrintServiceInfo
-android.printservice.PrintServiceInfo$1
-android.printservice.recommendation.IRecommendationsChangeListener
-android.provider.-$Lambda$87WmhkvObehVg0OMBzwa_MTVV8g
-android.provider.-$Lambda$a7Jyr6j_Mb70hHJ2ssL1AAhKh4c
-android.provider.BaseColumns
-android.provider.BlockedNumberContract
-android.provider.BlockedNumberContract$BlockedNumbers
-android.provider.CalendarContract
-android.provider.CalendarContract$Attendees
-android.provider.CalendarContract$AttendeesColumns
-android.provider.CalendarContract$CalendarAlerts
-android.provider.CalendarContract$CalendarAlertsColumns
-android.provider.CalendarContract$CalendarCache
-android.provider.CalendarContract$CalendarCacheColumns
-android.provider.CalendarContract$CalendarColumns
-android.provider.CalendarContract$CalendarSyncColumns
-android.provider.CalendarContract$Calendars
-android.provider.CalendarContract$Colors
-android.provider.CalendarContract$ColorsColumns
-android.provider.CalendarContract$Events
-android.provider.CalendarContract$EventsColumns
-android.provider.CalendarContract$ExtendedProperties
-android.provider.CalendarContract$ExtendedPropertiesColumns
-android.provider.CalendarContract$Instances
-android.provider.CalendarContract$Reminders
-android.provider.CalendarContract$RemindersColumns
-android.provider.CalendarContract$SyncColumns
-android.provider.CallLog
-android.provider.CallLog$Calls
-android.provider.ContactsContract
-android.provider.ContactsContract$BaseSyncColumns
-android.provider.ContactsContract$CommonDataKinds$BaseTypes
-android.provider.ContactsContract$CommonDataKinds$Callable
-android.provider.ContactsContract$CommonDataKinds$CommonColumns
-android.provider.ContactsContract$CommonDataKinds$Email
-android.provider.ContactsContract$CommonDataKinds$Event
-android.provider.ContactsContract$CommonDataKinds$Im
-android.provider.ContactsContract$CommonDataKinds$Phone
-android.provider.ContactsContract$CommonDataKinds$Relation
-android.provider.ContactsContract$CommonDataKinds$StructuredPostal
-android.provider.ContactsContract$ContactCounts
-android.provider.ContactsContract$ContactNameColumns
-android.provider.ContactsContract$ContactOptionsColumns
-android.provider.ContactsContract$ContactStatusColumns
-android.provider.ContactsContract$Contacts
-android.provider.ContactsContract$ContactsColumns
-android.provider.ContactsContract$Data
-android.provider.ContactsContract$DataColumns
-android.provider.ContactsContract$DataColumnsWithJoins
-android.provider.ContactsContract$DataUsageFeedback
-android.provider.ContactsContract$DataUsageStatColumns
-android.provider.ContactsContract$DeletedContacts
-android.provider.ContactsContract$DeletedContactsColumns
-android.provider.ContactsContract$Directory
-android.provider.ContactsContract$DisplayPhoto
-android.provider.ContactsContract$Groups
-android.provider.ContactsContract$GroupsColumns
-android.provider.ContactsContract$MetadataSync
-android.provider.ContactsContract$MetadataSyncColumns
-android.provider.ContactsContract$PhoneLookup
-android.provider.ContactsContract$PhoneLookupColumns
-android.provider.ContactsContract$Profile
-android.provider.ContactsContract$ProviderStatus
-android.provider.ContactsContract$RawContacts
-android.provider.ContactsContract$RawContactsColumns
-android.provider.ContactsContract$RawContactsEntity
-android.provider.ContactsContract$StatusColumns
-android.provider.ContactsContract$SyncColumns
-android.provider.DocumentsContract
-android.provider.DocumentsContract$Path
-android.provider.DocumentsProvider
-android.provider.Downloads
-android.provider.Downloads$Impl
-android.provider.FontsContract
-android.provider.FontsContract$1
-android.provider.MediaStore$Audio
-android.provider.MediaStore$Audio$AlbumColumns
-android.provider.MediaStore$Audio$Albums
-android.provider.MediaStore$Audio$AudioColumns
-android.provider.MediaStore$Audio$Media
-android.provider.MediaStore$Audio$Playlists
-android.provider.MediaStore$Audio$PlaylistsColumns
-android.provider.MediaStore$Files
-android.provider.MediaStore$Images$ImageColumns
-android.provider.MediaStore$Images$Media
-android.provider.MediaStore$Images$Thumbnails
-android.provider.MediaStore$MediaColumns
-android.provider.MediaStore$Video$Media
-android.provider.MediaStore$Video$VideoColumns
-android.provider.OpenableColumns
-android.provider.SearchIndexableData
-android.provider.SearchIndexableResource
-android.provider.SearchIndexablesProvider
-android.provider.SearchRecentSuggestions
-android.provider.Settings
-android.provider.Settings$ContentProviderHolder
-android.provider.Settings$GenerationTracker
-android.provider.Settings$Global
-android.provider.Settings$NameValueCache
-android.provider.Settings$NameValueTable
-android.provider.Settings$Secure
-android.provider.Settings$SettingNotFoundException
-android.provider.Settings$System
-android.provider.Settings$System$1
-android.provider.Settings$System$2
-android.provider.Settings$System$3
-android.provider.Settings$System$4
-android.provider.Settings$System$5
-android.provider.Settings$System$6
-android.provider.Settings$System$7
-android.provider.Settings$System$8
-android.provider.Settings$System$9
-android.provider.Settings$System$DiscreteValueValidator
-android.provider.Settings$System$InclusiveFloatRangeValidator
-android.provider.Settings$System$InclusiveIntegerRangeValidator
-android.provider.Settings$System$Validator
-android.provider.SyncStateContract$Columns
-android.provider.Telephony$BaseMmsColumns
-android.provider.Telephony$Carriers
-android.provider.Telephony$Mms
-android.provider.Telephony$MmsSms
-android.provider.Telephony$ServiceStateTable
-android.provider.Telephony$Sms
-android.provider.Telephony$TextBasedSmsColumns
-android.provider.Telephony$Threads
-android.provider.Telephony$ThreadsColumns
-android.provider.UserDictionary$Words
-android.provider.VoicemailContract$Status
-android.provider.VoicemailContract$Voicemails
-android.renderscript.Allocation
-android.renderscript.BaseObj
-android.renderscript.Matrix4f
-android.renderscript.RenderScriptCacheDir
-android.security.GateKeeper
-android.security.IKeyChainService
-android.security.IKeyChainService$Stub
-android.security.IKeyChainService$Stub$Proxy
-android.security.IKeystoreService
-android.security.IKeystoreService$Stub
-android.security.IKeystoreService$Stub$Proxy
-android.security.KeyChain
-android.security.KeyChain$1
-android.security.KeyChain$KeyChainConnection
-android.security.KeyStore
-android.security.NetworkSecurityPolicy
-android.security.keymaster.IKeyAttestationApplicationIdProvider
-android.security.keymaster.IKeyAttestationApplicationIdProvider$Stub
-android.security.keymaster.KeyAttestationApplicationId
-android.security.keymaster.KeymasterArguments
-android.security.keymaster.KeymasterArguments$1
-android.security.keystore.AndroidKeyStoreBCWorkaroundProvider
-android.security.keystore.AndroidKeyStoreKeyGeneratorSpi
-android.security.keystore.AndroidKeyStoreKeyGeneratorSpi$AES
-android.security.keystore.AndroidKeyStoreProvider
-android.security.keystore.AndroidKeyStoreSpi
-android.security.keystore.ArrayUtils
-android.security.keystore.KeyGenParameterSpec
-android.security.keystore.KeyGenParameterSpec$Builder
-android.security.keystore.KeyInfo
-android.security.keystore.KeyPermanentlyInvalidatedException
-android.security.keystore.KeyProperties
-android.security.keystore.KeyProperties$BlockMode
-android.security.keystore.KeyProperties$EncryptionPadding
-android.security.keystore.KeyProperties$Purpose
-android.security.keystore.KeymasterUtils
-android.security.keystore.UserNotAuthenticatedException
-android.security.keystore.Utils
-android.security.net.config.ApplicationConfig
-android.security.net.config.CertificateSource
-android.security.net.config.CertificatesEntryRef
-android.security.net.config.ConfigNetworkSecurityPolicy
-android.security.net.config.ConfigSource
-android.security.net.config.DirectoryCertificateSource
-android.security.net.config.DirectoryCertificateSource$1
-android.security.net.config.DirectoryCertificateSource$3
-android.security.net.config.DirectoryCertificateSource$CertSelector
-android.security.net.config.KeyStoreCertificateSource
-android.security.net.config.KeyStoreConfigSource
-android.security.net.config.ManifestConfigSource
-android.security.net.config.ManifestConfigSource$DefaultConfigSource
-android.security.net.config.NetworkSecurityConfig
-android.security.net.config.NetworkSecurityConfig$1
-android.security.net.config.NetworkSecurityConfig$Builder
-android.security.net.config.NetworkSecurityConfigProvider
-android.security.net.config.NetworkSecurityTrustManager
-android.security.net.config.PinSet
-android.security.net.config.RootTrustManager
-android.security.net.config.RootTrustManagerFactorySpi
-android.security.net.config.SystemCertificateSource
-android.security.net.config.TrustedCertificateStoreAdapter
-android.security.net.config.UserCertificateSource
-android.security.net.config.XmlConfigSource
-android.security.net.config.XmlConfigSource$ParserException
-android.service.autofill.-$Lambda$svbjmB3NFhHnuZrn67G14PFSJlY
-android.service.autofill.AutofillService
-android.service.autofill.AutofillService$1
-android.service.autofill.AutofillServiceInfo
-android.service.autofill.FillCallback
-android.service.autofill.FillContext
-android.service.autofill.FillContext$1
-android.service.autofill.FillEventHistory
-android.service.autofill.FillRequest
-android.service.autofill.FillRequest$1
-android.service.autofill.FillResponse
-android.service.autofill.IAutoFillService
-android.service.autofill.IAutoFillService$Stub
-android.service.autofill.IAutoFillService$Stub$Proxy
-android.service.autofill.IFillCallback
-android.service.autofill.IFillCallback$Stub
-android.service.autofill.IFillCallback$Stub$Proxy
-android.service.autofill.SaveCallback
-android.service.autofill.SaveRequest
-android.service.carrier.CarrierIdentifier
-android.service.carrier.CarrierIdentifier$1
-android.service.carrier.ICarrierService
-android.service.carrier.ICarrierService$Stub
-android.service.carrier.ICarrierService$Stub$Proxy
-android.service.dreams.DreamManagerInternal
-android.service.dreams.IDreamManager
-android.service.dreams.IDreamManager$Stub
-android.service.dreams.IDreamManager$Stub$Proxy
-android.service.gatekeeper.IGateKeeperService
-android.service.gatekeeper.IGateKeeperService$Stub
-android.service.gatekeeper.IGateKeeperService$Stub$Proxy
-android.service.media.IMediaBrowserService
-android.service.media.IMediaBrowserService$Stub
-android.service.media.IMediaBrowserService$Stub$Proxy
-android.service.media.IMediaBrowserServiceCallbacks
-android.service.media.IMediaBrowserServiceCallbacks$Stub
-android.service.notification.Adjustment
-android.service.notification.Condition
-android.service.notification.Condition$1
-android.service.notification.ConditionProviderService
-android.service.notification.ConditionProviderService$H
-android.service.notification.ConditionProviderService$Provider
-android.service.notification.IConditionListener
-android.service.notification.IConditionListener$Stub
-android.service.notification.IConditionProvider
-android.service.notification.IConditionProvider$Stub
-android.service.notification.INotificationListener
-android.service.notification.INotificationListener$Stub
-android.service.notification.INotificationListener$Stub$Proxy
-android.service.notification.IStatusBarNotificationHolder
-android.service.notification.IStatusBarNotificationHolder$Stub
-android.service.notification.IStatusBarNotificationHolder$Stub$Proxy
-android.service.notification.NotificationListenerService
-android.service.notification.NotificationListenerService$MyHandler
-android.service.notification.NotificationListenerService$NotificationListenerWrapper
-android.service.notification.NotificationListenerService$Ranking
-android.service.notification.NotificationListenerService$RankingMap
-android.service.notification.NotificationListenerService$RankingMap$1
-android.service.notification.NotificationRankingUpdate
-android.service.notification.NotificationRankingUpdate$1
-android.service.notification.StatusBarNotification
-android.service.notification.StatusBarNotification$1
-android.service.notification.ZenModeConfig
-android.service.notification.ZenModeConfig$1
-android.service.notification.ZenModeConfig$Diff
-android.service.notification.ZenModeConfig$EventInfo
-android.service.notification.ZenModeConfig$ScheduleInfo
-android.service.notification.ZenModeConfig$ZenRule
-android.service.notification.ZenModeConfig$ZenRule$1
-android.service.oemlock.IOemLockService
-android.service.oemlock.IOemLockService$Stub
-android.service.oemlock.OemLockManager
-android.service.persistentdata.IPersistentDataBlockService
-android.service.persistentdata.IPersistentDataBlockService$Stub
-android.service.persistentdata.IPersistentDataBlockService$Stub$Proxy
-android.service.persistentdata.PersistentDataBlockManager
-android.service.quicksettings.IQSService
-android.service.quicksettings.IQSService$Stub
-android.service.quicksettings.Tile
-android.service.textservice.SpellCheckerService
-android.service.textservice.SpellCheckerService$InternalISpellCheckerSession
-android.service.textservice.SpellCheckerService$Session
-android.service.textservice.SpellCheckerService$SpellCheckerServiceBinder
-android.service.voice.AlwaysOnHotwordDetector
-android.service.voice.AlwaysOnHotwordDetector$Callback
-android.service.voice.AlwaysOnHotwordDetector$EventPayload
-android.service.voice.AlwaysOnHotwordDetector$MyHandler
-android.service.voice.AlwaysOnHotwordDetector$RefreshAvailabiltyTask
-android.service.voice.AlwaysOnHotwordDetector$SoundTriggerListener
-android.service.voice.IVoiceInteractionService
-android.service.voice.IVoiceInteractionService$Stub
-android.service.voice.IVoiceInteractionService$Stub$Proxy
-android.service.voice.IVoiceInteractionSession
-android.service.voice.VoiceInteractionManagerInternal
-android.service.voice.VoiceInteractionService
-android.service.voice.VoiceInteractionService$1
-android.service.voice.VoiceInteractionService$MyHandler
-android.service.voice.VoiceInteractionServiceInfo
-android.service.vr.IPersistentVrStateCallbacks
-android.service.vr.IPersistentVrStateCallbacks$Stub
-android.service.vr.IVrManager
-android.service.vr.IVrManager$Stub
-android.service.vr.IVrManager$Stub$Proxy
-android.service.vr.IVrStateCallbacks
-android.service.vr.IVrStateCallbacks$Stub
-android.service.vr.IVrStateCallbacks$Stub$Proxy
-android.service.wallpaper.IWallpaperConnection
-android.service.wallpaper.IWallpaperConnection$Stub
-android.service.wallpaper.IWallpaperConnection$Stub$Proxy
-android.service.wallpaper.IWallpaperEngine
-android.service.wallpaper.IWallpaperEngine$Stub
-android.service.wallpaper.IWallpaperEngine$Stub$Proxy
-android.service.wallpaper.IWallpaperService
-android.service.wallpaper.IWallpaperService$Stub
-android.service.wallpaper.IWallpaperService$Stub$Proxy
-android.service.wallpaper.WallpaperService
-android.service.wallpaper.WallpaperService$Engine
-android.service.wallpaper.WallpaperService$Engine$1
-android.service.wallpaper.WallpaperService$Engine$2
-android.service.wallpaper.WallpaperService$Engine$3
-android.service.wallpaper.WallpaperService$Engine$WallpaperInputEventReceiver
-android.service.wallpaper.WallpaperService$IWallpaperEngineWrapper
-android.service.wallpaper.WallpaperService$IWallpaperServiceWrapper
-android.service.wallpaper.WallpaperService$WallpaperCommand
-android.speech.SpeechRecognizer
-android.speech.tts.AbstractEventLogger
-android.speech.tts.AbstractSynthesisCallback
-android.speech.tts.AudioPlaybackHandler
-android.speech.tts.AudioPlaybackHandler$MessageLoop
-android.speech.tts.EventLogger
-android.speech.tts.FileSynthesisCallback
-android.speech.tts.ITextToSpeechCallback
-android.speech.tts.ITextToSpeechCallback$Stub
-android.speech.tts.ITextToSpeechCallback$Stub$Proxy
-android.speech.tts.ITextToSpeechService
-android.speech.tts.ITextToSpeechService$Stub
-android.speech.tts.ITextToSpeechService$Stub$Proxy
-android.speech.tts.SynthesisCallback
-android.speech.tts.SynthesisRequest
-android.speech.tts.TextToSpeech
-android.speech.tts.TextToSpeech$10
-android.speech.tts.TextToSpeech$16
-android.speech.tts.TextToSpeech$17
-android.speech.tts.TextToSpeech$7
-android.speech.tts.TextToSpeech$Action
-android.speech.tts.TextToSpeech$Connection
-android.speech.tts.TextToSpeech$Connection$1
-android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
-android.speech.tts.TextToSpeech$EngineInfo
-android.speech.tts.TextToSpeech$OnInitListener
-android.speech.tts.TextToSpeechService
-android.speech.tts.TextToSpeechService$1
-android.speech.tts.TextToSpeechService$CallbackMap
-android.speech.tts.TextToSpeechService$SpeechItem
-android.speech.tts.TextToSpeechService$SynthHandler
-android.speech.tts.TextToSpeechService$SynthHandler$1
-android.speech.tts.TextToSpeechService$SynthHandler$2
-android.speech.tts.TextToSpeechService$SynthThread
-android.speech.tts.TextToSpeechService$SynthesisSpeechItem
-android.speech.tts.TextToSpeechService$SynthesisToFileOutputStreamSpeechItem
-android.speech.tts.TextToSpeechService$UtteranceProgressDispatcher
-android.speech.tts.TextToSpeechService$UtteranceSpeechItem
-android.speech.tts.TextToSpeechService$UtteranceSpeechItemWithParams
-android.speech.tts.TtsEngines
-android.speech.tts.TtsEngines$EngineInfoComparator
-android.speech.tts.UtteranceProgressListener
-android.system.ErrnoException
-android.system.GaiException
-android.system.NetlinkSocketAddress
-android.system.Os
-android.system.OsConstants
-android.system.PacketSocketAddress
-android.system.StructAddrinfo
-android.system.StructFlock
-android.system.StructGroupReq
-android.system.StructIcmpHdr
-android.system.StructIfaddrs
-android.system.StructLinger
-android.system.StructPasswd
-android.system.StructPollfd
-android.system.StructStat
-android.system.StructStatVfs
-android.system.StructTimeval
-android.system.StructUcred
-android.system.StructUtsname
-android.system.UnixSocketAddress
-android.telecom.-$Lambda$afyb_ODGzn3xMew6fjs8ANSIdVo
-android.telecom.CallAudioState
-android.telecom.CallAudioState$1
-android.telecom.Conference
-android.telecom.Conference$Listener
-android.telecom.Conferenceable
-android.telecom.Connection
-android.telecom.Connection$1
-android.telecom.Connection$2
-android.telecom.Connection$FailureSignalingConnection
-android.telecom.Connection$Listener
-android.telecom.ConnectionRequest
-android.telecom.ConnectionRequest$1
-android.telecom.ConnectionRequest$Builder
-android.telecom.ConnectionService
-android.telecom.ConnectionService$1
-android.telecom.ConnectionService$2
-android.telecom.ConnectionService$2$1
-android.telecom.ConnectionService$3
-android.telecom.ConnectionService$4
-android.telecom.ConnectionService$5
-android.telecom.ConnectionService$5$1
-android.telecom.ConnectionServiceAdapter
-android.telecom.DefaultDialerManager
-android.telecom.DisconnectCause
-android.telecom.DisconnectCause$1
-android.telecom.Log
-android.telecom.Log$1
-android.telecom.Logging.-$Lambda$OwO3BlCgqcOx28O1BaOAPVPor24
-android.telecom.Logging.-$Lambda$OwO3BlCgqcOx28O1BaOAPVPor24$1
-android.telecom.Logging.-$Lambda$OwO3BlCgqcOx28O1BaOAPVPor24$2
-android.telecom.Logging.EventManager
-android.telecom.Logging.EventManager$Event
-android.telecom.Logging.EventManager$EventListener
-android.telecom.Logging.EventManager$EventRecord
-android.telecom.Logging.EventManager$Loggable
-android.telecom.Logging.EventManager$TimedEventPair
-android.telecom.Logging.Runnable
-android.telecom.Logging.Runnable$1
-android.telecom.Logging.Session
-android.telecom.Logging.Session$Info
-android.telecom.Logging.Session$Info$1
-android.telecom.Logging.SessionManager
-android.telecom.Logging.SessionManager$ICurrentThreadId
-android.telecom.Logging.SessionManager$ISessionCleanupTimeoutMs
-android.telecom.Logging.SessionManager$ISessionIdQueryHandler
-android.telecom.Logging.SessionManager$ISessionListener
-android.telecom.ParcelableCall
-android.telecom.ParcelableCall$1
-android.telecom.ParcelableConference
-android.telecom.ParcelableConnection
-android.telecom.ParcelableConnection$1
-android.telecom.PhoneAccount
-android.telecom.PhoneAccount$1
-android.telecom.PhoneAccount$Builder
-android.telecom.PhoneAccountHandle
-android.telecom.PhoneAccountHandle$1
-android.telecom.RemoteConnectionManager
-android.telecom.StatusHints
-android.telecom.TelecomAnalytics
-android.telecom.TelecomManager
-android.telecom.VideoProfile
-android.telecom.VideoProfile$1
-android.telephony.CarrierConfigManager
-android.telephony.CellIdentityWcdma
-android.telephony.CellIdentityWcdma$1
-android.telephony.CellInfo
-android.telephony.CellInfo$1
-android.telephony.CellInfoCdma
-android.telephony.CellInfoGsm
-android.telephony.CellInfoLte
-android.telephony.CellInfoWcdma
-android.telephony.CellInfoWcdma$1
-android.telephony.CellLocation
-android.telephony.CellSignalStrength
-android.telephony.CellSignalStrengthWcdma
-android.telephony.CellSignalStrengthWcdma$1
-android.telephony.ClientRequestStats
-android.telephony.ClientRequestStats$1
-android.telephony.DisconnectCause
-android.telephony.IccOpenLogicalChannelResponse
-android.telephony.ModemActivityInfo
-android.telephony.PhoneNumberFormattingTextWatcher
-android.telephony.PhoneNumberUtils
-android.telephony.PhoneStateListener
-android.telephony.PhoneStateListener$1
-android.telephony.PhoneStateListener$IPhoneStateListenerStub
-android.telephony.PreciseCallState
-android.telephony.PreciseCallState$1
-android.telephony.PreciseDataConnectionState
-android.telephony.PreciseDataConnectionState$1
-android.telephony.RadioAccessFamily
-android.telephony.Rlog
-android.telephony.ServiceState
-android.telephony.ServiceState$1
-android.telephony.SignalStrength
-android.telephony.SignalStrength$1
-android.telephony.SmsManager
-android.telephony.SmsMessage
-android.telephony.SubscriptionInfo
-android.telephony.SubscriptionInfo$1
-android.telephony.SubscriptionManager
-android.telephony.SubscriptionManager$OnSubscriptionsChangedListener
-android.telephony.SubscriptionManager$OnSubscriptionsChangedListener$1
-android.telephony.SubscriptionManager$OnSubscriptionsChangedListener$2
-android.telephony.TelephonyHistogram
-android.telephony.TelephonyHistogram$1
-android.telephony.TelephonyManager
-android.telephony.TelephonyManager$MultiSimVariants
-android.telephony.VisualVoicemailSmsFilterSettings
-android.telephony.VoLteServiceState
-android.telephony.VoLteServiceState$1
-android.telephony.gsm.GsmCellLocation
-android.telephony.ims.stub.ImsConfigImplBase
-android.telephony.ims.stub.ImsEcbmImplBase
-android.telephony.ims.stub.ImsUtImplBase
-android.telephony.ims.stub.ImsUtListenerImplBase
-android.text.AndroidBidi
-android.text.AndroidCharacter
-android.text.Annotation
-android.text.BidiFormatter
-android.text.BidiFormatter$DirectionalityEstimator
-android.text.BoringLayout
-android.text.BoringLayout$Metrics
-android.text.CharSequenceCharacterIterator
-android.text.ClipboardManager
-android.text.DynamicLayout
-android.text.DynamicLayout$ChangeWatcher
-android.text.Editable
-android.text.Editable$Factory
-android.text.FontConfig
-android.text.FontConfig$Alias
-android.text.FontConfig$Family
-android.text.FontConfig$Font
-android.text.GetChars
-android.text.GraphicsOperations
-android.text.Html
-android.text.Html$HtmlParser
-android.text.Html$TagHandler
-android.text.HtmlToSpannedConverter
-android.text.HtmlToSpannedConverter$Bold
-android.text.HtmlToSpannedConverter$Href
-android.text.Hyphenator
-android.text.Hyphenator$HyphenationData
-android.text.InputFilter
-android.text.InputFilter$LengthFilter
-android.text.InputType
-android.text.Layout
-android.text.Layout$Alignment
-android.text.Layout$Directions
-android.text.Layout$Ellipsizer
-android.text.Layout$SpannedEllipsizer
-android.text.MeasuredText
-android.text.NoCopySpan
-android.text.NoCopySpan$Concrete
-android.text.PackedIntVector
-android.text.PackedObjectVector
-android.text.ParcelableSpan
-android.text.Selection
-android.text.Selection$END
-android.text.Selection$PositionIterator
-android.text.Selection$START
-android.text.SpanSet
-android.text.SpanWatcher
-android.text.Spannable
-android.text.Spannable$Factory
-android.text.SpannableString
-android.text.SpannableStringBuilder
-android.text.SpannableStringInternal
-android.text.Spanned
-android.text.SpannedString
-android.text.StaticLayout
-android.text.StaticLayout$Builder
-android.text.StaticLayout$LineBreaks
-android.text.TextDirectionHeuristic
-android.text.TextDirectionHeuristics
-android.text.TextDirectionHeuristics$AnyStrong
-android.text.TextDirectionHeuristics$FirstStrong
-android.text.TextDirectionHeuristics$TextDirectionAlgorithm
-android.text.TextDirectionHeuristics$TextDirectionHeuristicImpl
-android.text.TextDirectionHeuristics$TextDirectionHeuristicInternal
-android.text.TextDirectionHeuristics$TextDirectionHeuristicLocale
-android.text.TextLine
-android.text.TextPaint
-android.text.TextUtils
-android.text.TextUtils$1
-android.text.TextUtils$EllipsizeCallback
-android.text.TextUtils$SimpleStringSplitter
-android.text.TextUtils$StringSplitter
-android.text.TextUtils$TruncateAt
-android.text.TextWatcher
-android.text.format.DateFormat
-android.text.format.DateUtils
-android.text.format.Formatter
-android.text.format.Formatter$BytesResult
-android.text.format.Time
-android.text.format.Time$TimeCalculator
-android.text.format.TimeFormatter
-android.text.method.AllCapsTransformationMethod
-android.text.method.ArrowKeyMovementMethod
-android.text.method.BaseKeyListener
-android.text.method.BaseMovementMethod
-android.text.method.DialerKeyListener
-android.text.method.KeyListener
-android.text.method.LinkMovementMethod
-android.text.method.MetaKeyKeyListener
-android.text.method.MovementMethod
-android.text.method.NumberKeyListener
-android.text.method.PasswordTransformationMethod
-android.text.method.QwertyKeyListener
-android.text.method.QwertyKeyListener$Replaced
-android.text.method.ReplacementTransformationMethod
-android.text.method.ReplacementTransformationMethod$ReplacementCharSequence
-android.text.method.ReplacementTransformationMethod$SpannedReplacementCharSequence
-android.text.method.ScrollingMovementMethod
-android.text.method.SingleLineTransformationMethod
-android.text.method.TextKeyListener
-android.text.method.TextKeyListener$Capitalize
-android.text.method.TextKeyListener$SettingsObserver
-android.text.method.Touch
-android.text.method.Touch$DragState
-android.text.method.TransformationMethod
-android.text.method.TransformationMethod2
-android.text.method.WordIterator
-android.text.style.AlignmentSpan
-android.text.style.BackgroundColorSpan
-android.text.style.CharacterStyle
-android.text.style.CharacterStyle$Passthrough
-android.text.style.ClickableSpan
-android.text.style.DynamicDrawableSpan
-android.text.style.EasyEditSpan
-android.text.style.ForegroundColorSpan
-android.text.style.ImageSpan
-android.text.style.LeadingMarginSpan
-android.text.style.LeadingMarginSpan$LeadingMarginSpan2
-android.text.style.LeadingMarginSpan$Standard
-android.text.style.LineBackgroundSpan
-android.text.style.LineHeightSpan
-android.text.style.LineHeightSpan$WithDensity
-android.text.style.MetricAffectingSpan
-android.text.style.MetricAffectingSpan$Passthrough
-android.text.style.ParagraphStyle
-android.text.style.RelativeSizeSpan
-android.text.style.ReplacementSpan
-android.text.style.SpellCheckSpan
-android.text.style.StrikethroughSpan
-android.text.style.StyleSpan
-android.text.style.SuggestionSpan
-android.text.style.TabStopSpan
-android.text.style.TextAppearanceSpan
-android.text.style.TtsSpan
-android.text.style.TtsSpan$Builder
-android.text.style.TtsSpan$SemioticClassBuilder
-android.text.style.TtsSpan$TelephoneBuilder
-android.text.style.TtsSpan$VerbatimBuilder
-android.text.style.TypefaceSpan
-android.text.style.URLSpan
-android.text.style.UnderlineSpan
-android.text.style.UpdateAppearance
-android.text.style.UpdateLayout
-android.text.style.WrapTogetherSpan
-android.text.util.Linkify
-android.text.util.Linkify$1
-android.text.util.Linkify$2
-android.text.util.Linkify$3
-android.text.util.Linkify$4
-android.text.util.Linkify$MatchFilter
-android.text.util.Linkify$TransformFilter
-android.text.util.Rfc822Token
-android.text.util.Rfc822Tokenizer
-android.transition.ArcMotion
-android.transition.AutoTransition
-android.transition.ChangeBounds
-android.transition.ChangeBounds$1
-android.transition.ChangeBounds$2
-android.transition.ChangeBounds$3
-android.transition.ChangeBounds$4
-android.transition.ChangeBounds$5
-android.transition.ChangeBounds$6
-android.transition.ChangeBounds$9
-android.transition.ChangeClipBounds
-android.transition.ChangeImageTransform
-android.transition.ChangeImageTransform$1
-android.transition.ChangeImageTransform$2
-android.transition.ChangeTransform
-android.transition.ChangeTransform$1
-android.transition.ChangeTransform$2
-android.transition.ChangeTransform$3
-android.transition.ChangeTransform$GhostListener
-android.transition.ChangeTransform$PathAnimatorMatrix
-android.transition.ChangeTransform$Transforms
-android.transition.CircularPropagation
-android.transition.Explode
-android.transition.Fade
-android.transition.Fade$1
-android.transition.Fade$FadeAnimatorListener
-android.transition.PathMotion
-android.transition.Scene
-android.transition.SidePropagation
-android.transition.Slide
-android.transition.Slide$1
-android.transition.Slide$2
-android.transition.Slide$3
-android.transition.Slide$4
-android.transition.Slide$5
-android.transition.Slide$6
-android.transition.Slide$CalculateSlide
-android.transition.Slide$CalculateSlideHorizontal
-android.transition.Slide$CalculateSlideVertical
-android.transition.Transition
-android.transition.Transition$1
-android.transition.Transition$2
-android.transition.Transition$3
-android.transition.Transition$AnimationInfo
-android.transition.Transition$ArrayListManager
-android.transition.Transition$EpicenterCallback
-android.transition.Transition$TransitionListener
-android.transition.TransitionInflater
-android.transition.TransitionListenerAdapter
-android.transition.TransitionManager
-android.transition.TransitionManager$MultiListener
-android.transition.TransitionManager$MultiListener$1
-android.transition.TransitionPropagation
-android.transition.TransitionSet
-android.transition.TransitionSet$TransitionSetListener
-android.transition.TransitionUtils
-android.transition.TransitionValues
-android.transition.TransitionValuesMaps
-android.transition.Visibility
-android.transition.Visibility$1
-android.transition.Visibility$VisibilityInfo
-android.transition.VisibilityPropagation
-android.util.AndroidException
-android.util.AndroidRuntimeException
-android.util.ArrayMap
-android.util.ArrayMap$1
-android.util.ArraySet
-android.util.ArraySet$1
-android.util.AtomicFile
-android.util.AttributeSet
-android.util.Base64
-android.util.Base64$Coder
-android.util.Base64$Decoder
-android.util.Base64$Encoder
-android.util.TimingsTraceLog
-android.util.ByteStringUtils
-android.util.ContainerHelpers
-android.util.DebugUtils
-android.util.DisplayMetrics
-android.util.EventLog
-android.util.EventLog$Event
-android.util.FastImmutableArraySet
-android.util.FastImmutableArraySet$FastIterator
-android.util.FloatProperty
-android.util.IconDrawableFactory
-android.util.IntArray
-android.util.IntProperty
-android.util.JsonReader
-android.util.JsonScope
-android.util.JsonToken
-android.util.JsonWriter
-android.util.KeyValueListParser
-android.util.LauncherIcons
-android.util.LauncherIcons$ShadowDrawable
-android.util.LauncherIcons$ShadowDrawable$MyConstantState
-android.util.LocalLog
-android.util.LocalLog$ReadOnlyLocalLog
-android.util.Log
-android.util.Log$1
-android.util.Log$ImmediateLogWriter
-android.util.Log$TerribleFailure
-android.util.Log$TerribleFailureHandler
-android.util.LogPrinter
-android.util.LongArray
-android.util.LongSparseArray
-android.util.LongSparseLongArray
-android.util.LruCache
-android.util.MapCollections
-android.util.MapCollections$ArrayIterator
-android.util.MapCollections$EntrySet
-android.util.MapCollections$KeySet
-android.util.MapCollections$MapIterator
-android.util.MapCollections$ValuesCollection
-android.util.MathUtils
-android.util.MemoryIntArray
-android.util.MemoryIntArray$1
-android.util.MergedConfiguration
-android.util.MergedConfiguration$1
-android.util.MutableBoolean
-android.util.MutableInt
-android.util.MutableLong
-android.util.NtpTrustedTime
-android.util.Pair
-android.util.PathParser
-android.util.PathParser$PathData
-android.util.Patterns
-android.util.Pools$Pool
-android.util.Pools$SimplePool
-android.util.Pools$SynchronizedPool
-android.util.Printer
-android.util.Property
-android.util.Range
-android.util.Rational
-android.util.ReflectiveProperty
-android.util.Singleton
-android.util.Size
-android.util.SizeF
-android.util.Slog
-android.util.SparseArray
-android.util.SparseBooleanArray
-android.util.SparseIntArray
-android.util.SparseLongArray
-android.util.Spline
-android.util.Spline$MonotoneCubicSpline
-android.util.StateSet
-android.util.SuperNotCalledException
-android.util.TimeFormatException
-android.util.TimeUtils
-android.util.TimedRemoteCaller
-android.util.TimingLogger
-android.util.TrustedTime
-android.util.TypedValue
-android.util.Xml
-android.util.Xml$XmlSerializerFactory
-android.util.XmlPullAttributes
-android.util.apk.ApkSignatureSchemeV2Verifier
-android.util.apk.ApkSignatureSchemeV2Verifier$SignatureNotFoundException
-android.util.apk.ZipUtils
-android.util.jar.StrictJarFile
-android.util.jar.StrictJarFile$EntryIterator
-android.util.jar.StrictJarFile$FDStream
-android.util.jar.StrictJarFile$JarFileInputStream
-android.util.jar.StrictJarFile$ZipInflaterInputStream
-android.util.jar.StrictJarManifest
-android.util.jar.StrictJarManifest$Chunk
-android.util.jar.StrictJarManifestReader
-android.util.jar.StrictJarVerifier
-android.util.jar.StrictJarVerifier$VerifierEntry
-android.view.-$Lambda$6k_RnLLpNi5zg27ubDxN4lDdBbk
-android.view.-$Lambda$6k_RnLLpNi5zg27ubDxN4lDdBbk$1
-android.view.-$Lambda$6k_RnLLpNi5zg27ubDxN4lDdBbk$2
-android.view.-$Lambda$6k_RnLLpNi5zg27ubDxN4lDdBbk$3
-android.view.-$Lambda$P6MTGFSudLpwrqb6oVD8FdorW1c
-android.view.-$Lambda$iU_USrtPm1XIm5H9QYQvXfBGDE4
-android.view.-$Lambda$iU_USrtPm1XIm5H9QYQvXfBGDE4$1
-android.view.AbsSavedState
-android.view.AbsSavedState$1
-android.view.AbsSavedState$2
-android.view.ActionMode
-android.view.ActionMode$Callback
-android.view.ActionMode$Callback2
-android.view.ActionProvider
-android.view.ActionProvider$SubUiVisibilityListener
-android.view.AppTransitionAnimationSpec
-android.view.AppTransitionAnimationSpec$1
-android.view.Choreographer
-android.view.Choreographer$1
-android.view.Choreographer$2
-android.view.Choreographer$CallbackQueue
-android.view.Choreographer$CallbackRecord
-android.view.Choreographer$FrameCallback
-android.view.Choreographer$FrameDisplayEventReceiver
-android.view.Choreographer$FrameHandler
-android.view.ContextMenu
-android.view.ContextMenu$ContextMenuInfo
-android.view.ContextThemeWrapper
-android.view.Display
-android.view.Display$HdrCapabilities
-android.view.Display$HdrCapabilities$1
-android.view.Display$Mode
-android.view.Display$Mode$1
-android.view.DisplayAdjustments
-android.view.DisplayEventReceiver
-android.view.DisplayInfo
-android.view.DisplayInfo$1
-android.view.DisplayListCanvas
-android.view.DragEvent
-android.view.FallbackEventHandler
-android.view.FocusFinder
-android.view.FocusFinder$1
-android.view.FocusFinder$FocusSorter
-android.view.FocusFinder$UserSpecifiedFocusComparator
-android.view.FocusFinder$UserSpecifiedFocusComparator$NextFocusGetter
-android.view.FrameInfo
-android.view.FrameMetrics
-android.view.FrameMetricsObserver
-android.view.FrameStats
-android.view.GestureDetector
-android.view.GestureDetector$GestureHandler
-android.view.GestureDetector$OnContextClickListener
-android.view.GestureDetector$OnDoubleTapListener
-android.view.GestureDetector$OnGestureListener
-android.view.GestureDetector$SimpleOnGestureListener
-android.view.GhostView
-android.view.Gravity
-android.view.HandlerActionQueue
-android.view.HandlerActionQueue$HandlerAction
-android.view.HardwareLayer
-android.view.IAppTransitionAnimationSpecsFuture
-android.view.IAppTransitionAnimationSpecsFuture$Stub
-android.view.IAppTransitionAnimationSpecsFuture$Stub$Proxy
-android.view.IApplicationToken
-android.view.IApplicationToken$Stub
-android.view.IDockedStackListener
-android.view.IDockedStackListener$Stub
-android.view.IDockedStackListener$Stub$Proxy
-android.view.IGraphicsStats
-android.view.IGraphicsStats$Stub
-android.view.IGraphicsStats$Stub$Proxy
-android.view.IGraphicsStatsCallback
-android.view.IGraphicsStatsCallback$Stub
-android.view.IGraphicsStatsCallback$Stub$Proxy
-android.view.IInputFilter
-android.view.IOnKeyguardExitResult
-android.view.IPinnedStackController
-android.view.IPinnedStackController$Stub
-android.view.IPinnedStackController$Stub$Proxy
-android.view.IPinnedStackListener
-android.view.IPinnedStackListener$Stub
-android.view.IPinnedStackListener$Stub$Proxy
-android.view.IRotationWatcher
-android.view.IRotationWatcher$Stub
-android.view.IRotationWatcher$Stub$Proxy
-android.view.IWindow
-android.view.IWindow$Stub
-android.view.IWindow$Stub$Proxy
-android.view.IWindowFocusObserver
-android.view.IWindowId
-android.view.IWindowId$Stub
-android.view.IWindowId$Stub$Proxy
-android.view.IWindowManager
-android.view.IWindowManager$Stub
-android.view.IWindowManager$Stub$Proxy
-android.view.IWindowSession
-android.view.IWindowSession$Stub
-android.view.IWindowSession$Stub$Proxy
-android.view.IWindowSessionCallback
-android.view.IWindowSessionCallback$Stub
-android.view.IWindowSessionCallback$Stub$Proxy
-android.view.InflateException
-android.view.InputChannel
-android.view.InputChannel$1
-android.view.InputDevice
-android.view.InputDevice$1
-android.view.InputDevice$MotionRange
-android.view.InputEvent
-android.view.InputEvent$1
-android.view.InputEventConsistencyVerifier
-android.view.InputEventReceiver
-android.view.InputEventReceiver$Factory
-android.view.InputEventSender
-android.view.InputQueue
-android.view.InputQueue$Callback
-android.view.InputQueue$FinishedInputEventCallback
-android.view.KeyCharacterMap
-android.view.KeyCharacterMap$1
-android.view.KeyCharacterMap$FallbackAction
-android.view.KeyEvent
-android.view.KeyEvent$1
-android.view.KeyEvent$Callback
-android.view.KeyEvent$DispatcherState
-android.view.KeyboardShortcutGroup
-android.view.KeyboardShortcutGroup$1
-android.view.KeyboardShortcutInfo
-android.view.KeyboardShortcutInfo$1
-android.view.LayoutInflater
-android.view.LayoutInflater$Factory
-android.view.LayoutInflater$Factory2
-android.view.LayoutInflater$FactoryMerger
-android.view.LayoutInflater$Filter
-android.view.MagnificationSpec
-android.view.Menu
-android.view.MenuInflater
-android.view.MenuInflater$MenuState
-android.view.MenuItem
-android.view.MenuItem$OnActionExpandListener
-android.view.MenuItem$OnMenuItemClickListener
-android.view.MotionEvent
-android.view.MotionEvent$1
-android.view.MotionEvent$PointerCoords
-android.view.MotionEvent$PointerProperties
-android.view.NotificationHeaderView
-android.view.NotificationHeaderView$1
-android.view.NotificationHeaderView$HeaderTouchListener
-android.view.OrientationEventListener
-android.view.OrientationEventListener$SensorEventListenerImpl
-android.view.PointerIcon
-android.view.PointerIcon$1
-android.view.RecordingCanvas
-android.view.RemotableViewMethod
-android.view.RenderNode
-android.view.RenderNode$NoImagePreloadHolder
-android.view.RenderNodeAnimator
-android.view.RenderNodeAnimator$1
-android.view.RenderNodeAnimatorSetHelper
-android.view.ScaleGestureDetector
-android.view.ScaleGestureDetector$1
-android.view.ScaleGestureDetector$OnScaleGestureListener
-android.view.ScaleGestureDetector$SimpleOnScaleGestureListener
-android.view.SearchEvent
-android.view.SubMenu
-android.view.Surface
-android.view.Surface$1
-android.view.Surface$CompatibleCanvas
-android.view.Surface$OutOfResourcesException
-android.view.SurfaceControl
-android.view.SurfaceControl$PhysicalDisplayInfo
-android.view.SurfaceHolder
-android.view.SurfaceHolder$Callback
-android.view.SurfaceHolder$Callback2
-android.view.SurfaceSession
-android.view.SurfaceView
-android.view.SurfaceView$1
-android.view.SurfaceView$2
-android.view.SurfaceView$3
-android.view.SurfaceView$4
-android.view.TextureView
-android.view.TextureView$1
-android.view.TextureView$SurfaceTextureListener
-android.view.ThreadedRenderer
-android.view.ThreadedRenderer$DrawCallbacks
-android.view.ThreadedRenderer$ProcessInitializer
-android.view.ThreadedRenderer$ProcessInitializer$1
-android.view.TouchDelegate
-android.view.VelocityTracker
-android.view.VelocityTracker$Estimator
-android.view.View
-android.view.View$1
-android.view.View$10
-android.view.View$11
-android.view.View$12
-android.view.View$2
-android.view.View$3
-android.view.View$4
-android.view.View$5
-android.view.View$6
-android.view.View$7
-android.view.View$8
-android.view.View$9
-android.view.View$AccessibilityDelegate
-android.view.View$AttachInfo
-android.view.View$AttachInfo$Callbacks
-android.view.View$BaseSavedState
-android.view.View$BaseSavedState$1
-android.view.View$CheckForLongPress
-android.view.View$CheckForTap
-android.view.View$DeclaredOnClickListener
-android.view.View$ForegroundInfo
-android.view.View$ListenerInfo
-android.view.View$MatchIdPredicate
-android.view.View$MeasureSpec
-android.view.View$OnApplyWindowInsetsListener
-android.view.View$OnAttachStateChangeListener
-android.view.View$OnClickListener
-android.view.View$OnCreateContextMenuListener
-android.view.View$OnDragListener
-android.view.View$OnFocusChangeListener
-android.view.View$OnGenericMotionListener
-android.view.View$OnHoverListener
-android.view.View$OnKeyListener
-android.view.View$OnLayoutChangeListener
-android.view.View$OnLongClickListener
-android.view.View$OnScrollChangeListener
-android.view.View$OnSystemUiVisibilityChangeListener
-android.view.View$OnTouchListener
-android.view.View$PerformClick
-android.view.View$ScrollabilityCache
-android.view.View$TintInfo
-android.view.View$TooltipInfo
-android.view.View$TransformationInfo
-android.view.View$UnsetPressedState
-android.view.View$VisibilityChangeForAutofillHandler
-android.view.ViewAnimationUtils
-android.view.ViewConfiguration
-android.view.ViewDebug$CapturedViewProperty
-android.view.ViewDebug$ExportedProperty
-android.view.ViewDebug$HierarchyHandler
-android.view.ViewGroup
-android.view.ViewGroup$1
-android.view.ViewGroup$2
-android.view.ViewGroup$3
-android.view.ViewGroup$4
-android.view.ViewGroup$ChildListForAutoFill
-android.view.ViewGroup$LayoutParams
-android.view.ViewGroup$MarginLayoutParams
-android.view.ViewGroup$OnHierarchyChangeListener
-android.view.ViewGroup$TouchTarget
-android.view.ViewGroupOverlay
-android.view.ViewManager
-android.view.ViewOutlineProvider
-android.view.ViewOutlineProvider$1
-android.view.ViewOutlineProvider$2
-android.view.ViewOutlineProvider$3
-android.view.ViewOverlay
-android.view.ViewOverlay$OverlayViewGroup
-android.view.ViewParent
-android.view.ViewPropertyAnimator
-android.view.ViewPropertyAnimator$1
-android.view.ViewPropertyAnimator$2
-android.view.ViewPropertyAnimator$3
-android.view.ViewPropertyAnimator$AnimatorEventListener
-android.view.ViewPropertyAnimator$NameValuesHolder
-android.view.ViewPropertyAnimator$PropertyBundle
-android.view.ViewRootImpl
-android.view.ViewRootImpl$1
-android.view.ViewRootImpl$2
-android.view.ViewRootImpl$4
-android.view.ViewRootImpl$AccessibilityInteractionConnectionManager
-android.view.ViewRootImpl$ActivityConfigCallback
-android.view.ViewRootImpl$AsyncInputStage
-android.view.ViewRootImpl$ConfigChangedCallback
-android.view.ViewRootImpl$ConsumeBatchedInputImmediatelyRunnable
-android.view.ViewRootImpl$ConsumeBatchedInputRunnable
-android.view.ViewRootImpl$EarlyPostImeInputStage
-android.view.ViewRootImpl$HighContrastTextManager
-android.view.ViewRootImpl$ImeInputStage
-android.view.ViewRootImpl$InputStage
-android.view.ViewRootImpl$InvalidateOnAnimationRunnable
-android.view.ViewRootImpl$NativePostImeInputStage
-android.view.ViewRootImpl$NativePreImeInputStage
-android.view.ViewRootImpl$QueuedInputEvent
-android.view.ViewRootImpl$SyntheticInputStage
-android.view.ViewRootImpl$SyntheticJoystickHandler
-android.view.ViewRootImpl$SyntheticKeyboardHandler
-android.view.ViewRootImpl$SyntheticTouchNavigationHandler
-android.view.ViewRootImpl$SyntheticTouchNavigationHandler$1
-android.view.ViewRootImpl$SyntheticTrackballHandler
-android.view.ViewRootImpl$SystemUiVisibilityInfo
-android.view.ViewRootImpl$TrackballAxis
-android.view.ViewRootImpl$TraversalRunnable
-android.view.ViewRootImpl$ViewPostImeInputStage
-android.view.ViewRootImpl$ViewPreImeInputStage
-android.view.ViewRootImpl$ViewRootHandler
-android.view.ViewRootImpl$W
-android.view.ViewRootImpl$WindowInputEventReceiver
-android.view.ViewRootImpl$WindowStoppedCallback
-android.view.ViewStructure
-android.view.ViewStructure$HtmlInfo
-android.view.ViewStub
-android.view.ViewStub$OnInflateListener
-android.view.ViewStub$ViewReplaceRunnable
-android.view.ViewTreeObserver
-android.view.ViewTreeObserver$CopyOnWriteArray
-android.view.ViewTreeObserver$CopyOnWriteArray$Access
-android.view.ViewTreeObserver$InternalInsetsInfo
-android.view.ViewTreeObserver$OnComputeInternalInsetsListener
-android.view.ViewTreeObserver$OnDrawListener
-android.view.ViewTreeObserver$OnGlobalFocusChangeListener
-android.view.ViewTreeObserver$OnGlobalLayoutListener
-android.view.ViewTreeObserver$OnPreDrawListener
-android.view.ViewTreeObserver$OnScrollChangedListener
-android.view.ViewTreeObserver$OnTouchModeChangeListener
-android.view.Window
-android.view.Window$Callback
-android.view.Window$OnWindowDismissedCallback
-android.view.Window$OnWindowSwipeDismissedCallback
-android.view.Window$WindowControllerCallback
-android.view.WindowAnimationFrameStats
-android.view.WindowAnimationFrameStats$1
-android.view.WindowCallbackWrapper
-android.view.WindowCallbacks
-android.view.WindowContentFrameStats
-android.view.WindowContentFrameStats$1
-android.view.WindowId
-android.view.WindowId$1
-android.view.WindowInsets
-android.view.WindowLeaked
-android.view.WindowManager
-android.view.WindowManager$BadTokenException
-android.view.WindowManager$InvalidDisplayException
-android.view.WindowManager$KeyboardShortcutsReceiver
-android.view.WindowManager$LayoutParams
-android.view.WindowManager$LayoutParams$1
-android.view.WindowManagerGlobal
-android.view.WindowManagerGlobal$1
-android.view.WindowManagerGlobal$2
-android.view.WindowManagerImpl
-android.view.WindowManagerPolicyConstants
-android.view.WindowManagerPolicyConstants$PointerEventListener
-android.view.accessibility.AccessibilityEvent
-android.view.accessibility.AccessibilityEvent$1
-android.view.accessibility.AccessibilityEventSource
-android.view.accessibility.AccessibilityManager
-android.view.accessibility.AccessibilityManager$1
-android.view.accessibility.AccessibilityManager$AccessibilityServicesStateChangeListener
-android.view.accessibility.AccessibilityManager$AccessibilityStateChangeListener
-android.view.accessibility.AccessibilityManager$HighTextContrastChangeListener
-android.view.accessibility.AccessibilityManager$MyCallback
-android.view.accessibility.AccessibilityManager$TouchExplorationStateChangeListener
-android.view.accessibility.AccessibilityNodeInfo
-android.view.accessibility.AccessibilityNodeInfo$AccessibilityAction
-android.view.accessibility.AccessibilityNodeProvider
-android.view.accessibility.AccessibilityRecord
-android.view.accessibility.CaptioningManager
-android.view.accessibility.CaptioningManager$1
-android.view.accessibility.CaptioningManager$CaptionStyle
-android.view.accessibility.CaptioningManager$CaptioningChangeListener
-android.view.accessibility.CaptioningManager$MyContentObserver
-android.view.accessibility.IAccessibilityInteractionConnection
-android.view.accessibility.IAccessibilityInteractionConnection$Stub
-android.view.accessibility.IAccessibilityInteractionConnection$Stub$Proxy
-android.view.accessibility.IAccessibilityInteractionConnectionCallback
-android.view.accessibility.IAccessibilityManager
-android.view.accessibility.IAccessibilityManager$Stub
-android.view.accessibility.IAccessibilityManager$Stub$Proxy
-android.view.accessibility.IAccessibilityManagerClient
-android.view.accessibility.IAccessibilityManagerClient$Stub
-android.view.accessibility.IAccessibilityManagerClient$Stub$Proxy
-android.view.animation.AccelerateDecelerateInterpolator
-android.view.animation.AccelerateInterpolator
-android.view.animation.AlphaAnimation
-android.view.animation.Animation
-android.view.animation.Animation$1
-android.view.animation.Animation$2
-android.view.animation.Animation$3
-android.view.animation.Animation$AnimationListener
-android.view.animation.Animation$Description
-android.view.animation.Animation$NoImagePreloadHolder
-android.view.animation.AnimationSet
-android.view.animation.AnimationUtils
-android.view.animation.AnimationUtils$1
-android.view.animation.AnimationUtils$AnimationState
-android.view.animation.BaseInterpolator
-android.view.animation.ClipRectAnimation
-android.view.animation.CycleInterpolator
-android.view.animation.DecelerateInterpolator
-android.view.animation.GridLayoutAnimationController$AnimationParameters
-android.view.animation.Interpolator
-android.view.animation.LayoutAnimationController
-android.view.animation.LayoutAnimationController$AnimationParameters
-android.view.animation.LinearInterpolator
-android.view.animation.OvershootInterpolator
-android.view.animation.PathInterpolator
-android.view.animation.ScaleAnimation
-android.view.animation.Transformation
-android.view.animation.TranslateAnimation
-android.view.autofill.AutofillId
-android.view.autofill.AutofillId$1
-android.view.autofill.AutofillManager
-android.view.autofill.AutofillManager$AutofillClient
-android.view.autofill.AutofillManager$AutofillManagerClient
-android.view.autofill.AutofillValue
-android.view.autofill.AutofillValue$1
-android.view.autofill.Helper
-android.view.autofill.IAutoFillManager
-android.view.autofill.IAutoFillManager$Stub
-android.view.autofill.IAutoFillManager$Stub$Proxy
-android.view.autofill.IAutoFillManagerClient
-android.view.autofill.IAutoFillManagerClient$Stub
-android.view.autofill.IAutoFillManagerClient$Stub$Proxy
-android.view.autofill.IAutofillWindowPresenter
-android.view.inputmethod.BaseInputConnection
-android.view.inputmethod.CompletionInfo
-android.view.inputmethod.CompletionInfo$1
-android.view.inputmethod.ComposingText
-android.view.inputmethod.CorrectionInfo
-android.view.inputmethod.CursorAnchorInfo
-android.view.inputmethod.CursorAnchorInfo$Builder
-android.view.inputmethod.EditorInfo
-android.view.inputmethod.EditorInfo$1
-android.view.inputmethod.ExtractedText
-android.view.inputmethod.ExtractedText$1
-android.view.inputmethod.ExtractedTextRequest
-android.view.inputmethod.ExtractedTextRequest$1
-android.view.inputmethod.InputBinding
-android.view.inputmethod.InputBinding$1
-android.view.inputmethod.InputConnection
-android.view.inputmethod.InputConnectionInspector
-android.view.inputmethod.InputConnectionWrapper
-android.view.inputmethod.InputContentInfo
-android.view.inputmethod.InputMethod
-android.view.inputmethod.InputMethod$SessionCallback
-android.view.inputmethod.InputMethodInfo
-android.view.inputmethod.InputMethodInfo$1
-android.view.inputmethod.InputMethodManager
-android.view.inputmethod.InputMethodManager$1
-android.view.inputmethod.InputMethodManager$2
-android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper
-android.view.inputmethod.InputMethodManager$FinishedInputEventCallback
-android.view.inputmethod.InputMethodManager$H
-android.view.inputmethod.InputMethodManager$ImeInputEventSender
-android.view.inputmethod.InputMethodManager$PendingEvent
-android.view.inputmethod.InputMethodManagerInternal
-android.view.inputmethod.InputMethodSession
-android.view.inputmethod.InputMethodSession$EventCallback
-android.view.inputmethod.InputMethodSubtype
-android.view.inputmethod.InputMethodSubtype$1
-android.view.inputmethod.InputMethodSubtype$InputMethodSubtypeBuilder
-android.view.inputmethod.InputMethodSubtypeArray
-android.view.textclassifier.TextClassificationManager
-android.view.textclassifier.TextClassifier
-android.view.textclassifier.TextClassifier$1
-android.view.textclassifier.TextClassifierImpl
-android.view.textservice.SpellCheckerInfo
-android.view.textservice.SpellCheckerInfo$1
-android.view.textservice.SpellCheckerSession
-android.view.textservice.SpellCheckerSession$1
-android.view.textservice.SpellCheckerSession$InternalListener
-android.view.textservice.SpellCheckerSession$SpellCheckerSessionListener
-android.view.textservice.SpellCheckerSession$SpellCheckerSessionListenerImpl
-android.view.textservice.SpellCheckerSession$SpellCheckerSessionListenerImpl$SpellCheckerParams
-android.view.textservice.SpellCheckerSubtype
-android.view.textservice.SpellCheckerSubtype$1
-android.view.textservice.SuggestionsInfo
-android.view.textservice.TextInfo
-android.view.textservice.TextServicesManager
-android.webkit.ConsoleMessage
-android.webkit.ConsoleMessage$MessageLevel
-android.webkit.CookieManager
-android.webkit.CookieSyncManager
-android.webkit.DownloadListener
-android.webkit.GeolocationPermissions
-android.webkit.IWebViewUpdateService
-android.webkit.IWebViewUpdateService$Stub
-android.webkit.IWebViewUpdateService$Stub$Proxy
-android.webkit.JavascriptInterface
-android.webkit.MimeTypeMap
-android.webkit.ServiceWorkerClient
-android.webkit.ServiceWorkerController
-android.webkit.ServiceWorkerWebSettings
-android.webkit.TokenBindingService
-android.webkit.URLUtil
-android.webkit.UserPackage
-android.webkit.ValueCallback
-android.webkit.WebBackForwardList
-android.webkit.WebChromeClient
-android.webkit.WebChromeClient$CustomViewCallback
-android.webkit.WebIconDatabase
-android.webkit.WebMessage
-android.webkit.WebMessagePort
-android.webkit.WebResourceRequest
-android.webkit.WebSettings
-android.webkit.WebSettings$LayoutAlgorithm
-android.webkit.WebSettings$PluginState
-android.webkit.WebSettings$RenderPriority
-android.webkit.WebSettings$ZoomDensity
-android.webkit.WebStorage
-android.webkit.WebSyncManager
-android.webkit.WebView
-android.webkit.WebView$FindListener
-android.webkit.WebView$HitTestResult
-android.webkit.WebView$PictureListener
-android.webkit.WebView$PrivateAccess
-android.webkit.WebView$VisualStateCallback
-android.webkit.WebViewClient
-android.webkit.WebViewDatabase
-android.webkit.WebViewDelegate
-android.webkit.WebViewDelegate$1
-android.webkit.WebViewDelegate$OnTraceEnabledChangeListener
-android.webkit.WebViewFactory
-android.webkit.WebViewFactory$1
-android.webkit.WebViewFactory$MissingWebViewPackageException
-android.webkit.WebViewFactory$RelroFileCreator
-android.webkit.WebViewFactoryProvider
-android.webkit.WebViewFactoryProvider$Statics
-android.webkit.WebViewProvider
-android.webkit.WebViewProvider$ScrollDelegate
-android.webkit.WebViewProvider$ViewDelegate
-android.webkit.WebViewProviderInfo
-android.webkit.WebViewProviderInfo$1
-android.webkit.WebViewProviderResponse
-android.webkit.WebViewProviderResponse$1
-android.webkit.WebViewZygote
-android.widget.-$Lambda$ISuHLqeK-K4pmesAfzlFglc3xF4
-android.widget.-$Lambda$ISuHLqeK-K4pmesAfzlFglc3xF4$1
-android.widget.-$Lambda$ISuHLqeK-K4pmesAfzlFglc3xF4$2
-android.widget.-$Lambda$ISuHLqeK-K4pmesAfzlFglc3xF4$3
-android.widget.-$Lambda$tfOQKOmkDz_xLYaBQX_cysn8vbE
-android.widget.AbsListView
-android.widget.AbsListView$3
-android.widget.AbsListView$AdapterDataSetObserver
-android.widget.AbsListView$CheckForTap
-android.widget.AbsListView$FlingRunnable
-android.widget.AbsListView$FlingRunnable$1
-android.widget.AbsListView$LayoutParams
-android.widget.AbsListView$MultiChoiceModeListener
-android.widget.AbsListView$MultiChoiceModeWrapper
-android.widget.AbsListView$OnScrollListener
-android.widget.AbsListView$PerformClick
-android.widget.AbsListView$RecycleBin
-android.widget.AbsListView$RecyclerListener
-android.widget.AbsListView$SavedState
-android.widget.AbsListView$SavedState$1
-android.widget.AbsListView$SelectionBoundsAdjuster
-android.widget.AbsListView$WindowRunnnable
-android.widget.AbsSeekBar
-android.widget.AbsSpinner
-android.widget.AbsSpinner$RecycleBin
-android.widget.AbsoluteLayout
-android.widget.ActionMenuPresenter
-android.widget.ActionMenuPresenter$1
-android.widget.ActionMenuPresenter$2
-android.widget.ActionMenuPresenter$ActionMenuPopupCallback
-android.widget.ActionMenuPresenter$OverflowMenuButton
-android.widget.ActionMenuPresenter$OverflowMenuButton$1
-android.widget.ActionMenuPresenter$PopupPresenterCallback
-android.widget.ActionMenuView
-android.widget.ActionMenuView$ActionMenuChildView
-android.widget.ActionMenuView$ActionMenuPresenterCallback
-android.widget.ActionMenuView$LayoutParams
-android.widget.ActionMenuView$MenuBuilderCallback
-android.widget.ActionMenuView$OnMenuItemClickListener
-android.widget.Adapter
-android.widget.AdapterView
-android.widget.AdapterView$AdapterDataSetObserver
-android.widget.AdapterView$OnItemClickListener
-android.widget.AdapterView$OnItemLongClickListener
-android.widget.AdapterView$OnItemSelectedListener
-android.widget.AdapterView$SelectionNotifier
-android.widget.ArrayAdapter
-android.widget.AutoCompleteTextView
-android.widget.AutoCompleteTextView$DropDownItemClickListener
-android.widget.AutoCompleteTextView$MyWatcher
-android.widget.AutoCompleteTextView$PassThroughClickListener
-android.widget.AutoCompleteTextView$PopupDataSetObserver
-android.widget.AutoCompleteTextView$PopupDataSetObserver$1
-android.widget.AutoCompleteTextView$Validator
-android.widget.BaseAdapter
-android.widget.Button
-android.widget.CheckBox
-android.widget.Checkable
-android.widget.CheckedTextView
-android.widget.Chronometer
-android.widget.Chronometer$1
-android.widget.CompoundButton
-android.widget.CompoundButton$OnCheckedChangeListener
-android.widget.CursorAdapter
-android.widget.CursorFilter$CursorFilterClient
-android.widget.DatePicker
-android.widget.DateTimeView
-android.widget.DateTimeView$ReceiverInfo
-android.widget.DateTimeView$ReceiverInfo$1
-android.widget.DateTimeView$ReceiverInfo$2
-android.widget.DropDownListView
-android.widget.EdgeEffect
-android.widget.EditText
-android.widget.Editor
-android.widget.Editor$1
-android.widget.Editor$2
-android.widget.Editor$Blink
-android.widget.Editor$CursorAnchorInfoNotifier
-android.widget.Editor$CursorController
-android.widget.Editor$EditOperation
-android.widget.Editor$EditOperation$1
-android.widget.Editor$HandleView
-android.widget.Editor$InputContentType
-android.widget.Editor$InputMethodState
-android.widget.Editor$InsertionHandleView
-android.widget.Editor$InsertionHandleView$2
-android.widget.Editor$InsertionPointCursorController
-android.widget.Editor$PositionListener
-android.widget.Editor$ProcessTextIntentActionsHandler
-android.widget.Editor$SelectionModifierCursorController
-android.widget.Editor$SpanController
-android.widget.Editor$SuggestionHelper
-android.widget.Editor$SuggestionHelper$SuggestionSpanComparator
-android.widget.Editor$TextRenderNode
-android.widget.Editor$TextViewPositionListener
-android.widget.Editor$UndoInputFilter
-android.widget.ExpandableListConnector
-android.widget.FastScroller
-android.widget.FastScroller$1
-android.widget.FastScroller$2
-android.widget.FastScroller$3
-android.widget.FastScroller$4
-android.widget.FastScroller$5
-android.widget.FastScroller$6
-android.widget.Filter
-android.widget.Filter$FilterListener
-android.widget.Filter$FilterResults
-android.widget.Filter$RequestArguments
-android.widget.Filter$RequestHandler
-android.widget.Filter$ResultsHandler
-android.widget.Filterable
-android.widget.ForwardingListener
-android.widget.FrameLayout
-android.widget.FrameLayout$LayoutParams
-android.widget.GridLayout
-android.widget.GridLayout$1
-android.widget.GridLayout$2
-android.widget.GridLayout$3
-android.widget.GridLayout$4
-android.widget.GridLayout$5
-android.widget.GridLayout$6
-android.widget.GridLayout$6$1
-android.widget.GridLayout$7
-android.widget.GridLayout$8
-android.widget.GridLayout$Alignment
-android.widget.GridLayout$Arc
-android.widget.GridLayout$Assoc
-android.widget.GridLayout$Axis
-android.widget.GridLayout$Axis$1
-android.widget.GridLayout$Bounds
-android.widget.GridLayout$Interval
-android.widget.GridLayout$LayoutParams
-android.widget.GridLayout$MutableInt
-android.widget.GridLayout$PackedMap
-android.widget.GridLayout$Spec
-android.widget.GridView
-android.widget.HeaderViewListAdapter
-android.widget.HorizontalScrollView
-android.widget.HorizontalScrollView$SavedState
-android.widget.HorizontalScrollView$SavedState$1
-android.widget.ImageButton
-android.widget.ImageView
-android.widget.ImageView$ImageDrawableCallback
-android.widget.ImageView$ScaleType
-android.widget.LinearLayout
-android.widget.LinearLayout$LayoutParams
-android.widget.ListAdapter
-android.widget.ListPopupWindow
-android.widget.ListPopupWindow$2
-android.widget.ListPopupWindow$3
-android.widget.ListPopupWindow$ListSelectorHider
-android.widget.ListPopupWindow$PopupDataSetObserver
-android.widget.ListPopupWindow$PopupScrollListener
-android.widget.ListPopupWindow$PopupTouchInterceptor
-android.widget.ListPopupWindow$ResizePopupRunnable
-android.widget.ListView
-android.widget.ListView$ArrowScrollFocusResult
-android.widget.ListView$FixedViewInfo
-android.widget.MediaController
-android.widget.MediaController$MediaPlayerControl
-android.widget.MultiAutoCompleteTextView
-android.widget.MultiAutoCompleteTextView$Tokenizer
-android.widget.NumberPicker
-android.widget.NumberPicker$Formatter
-android.widget.NumberPicker$OnValueChangeListener
-android.widget.OverScroller
-android.widget.OverScroller$SplineOverScroller
-android.widget.PopupMenu
-android.widget.PopupMenu$1
-android.widget.PopupMenu$2
-android.widget.PopupMenu$3
-android.widget.PopupMenu$OnMenuItemClickListener
-android.widget.PopupWindow
-android.widget.PopupWindow$1
-android.widget.PopupWindow$2
-android.widget.PopupWindow$OnDismissListener
-android.widget.PopupWindow$PopupBackgroundView
-android.widget.PopupWindow$PopupDecorView
-android.widget.PopupWindow$PopupDecorView$1
-android.widget.ProgressBar
-android.widget.ProgressBar$1
-android.widget.ProgressBar$ProgressTintInfo
-android.widget.ProgressBar$RefreshData
-android.widget.ProgressBar$RefreshProgressRunnable
-android.widget.ProgressBar$SavedState
-android.widget.ProgressBar$SavedState$1
-android.widget.QuickContactBadge
-android.widget.QuickContactBadge$QueryHandler
-android.widget.RadioButton
-android.widget.RadioGroup
-android.widget.RadioGroup$CheckedStateTracker
-android.widget.RadioGroup$LayoutParams
-android.widget.RadioGroup$OnCheckedChangeListener
-android.widget.RadioGroup$PassThroughHierarchyChangeListener
-android.widget.RatingBar
-android.widget.RelativeLayout
-android.widget.RelativeLayout$DependencyGraph
-android.widget.RelativeLayout$DependencyGraph$Node
-android.widget.RelativeLayout$LayoutParams
-android.widget.RemoteViews
-android.widget.RemoteViews$1
-android.widget.RemoteViews$2
-android.widget.RemoteViews$3
-android.widget.RemoteViews$Action
-android.widget.RemoteViews$ActionException
-android.widget.RemoteViews$AsyncApplyTask
-android.widget.RemoteViews$BitmapCache
-android.widget.RemoteViews$BitmapReflectionAction
-android.widget.RemoteViews$LayoutParamAction
-android.widget.RemoteViews$OnClickHandler
-android.widget.RemoteViews$OnViewAppliedListener
-android.widget.RemoteViews$ReflectionAction
-android.widget.RemoteViews$RemoteView
-android.widget.RemoteViews$RemoteViewsContextWrapper
-android.widget.RemoteViews$RunnableAction
-android.widget.RemoteViews$RuntimeAction
-android.widget.RemoteViews$SetDrawableTint
-android.widget.RemoteViews$SetOnClickPendingIntent
-android.widget.RemoteViews$SetOnClickPendingIntent$1
-android.widget.RemoteViews$ViewGroupAction
-android.widget.RemoteViews$ViewGroupAction$1
-android.widget.RemoteViews$ViewGroupAction$2
-android.widget.RemoteViews$ViewPaddingAction
-android.widget.RemoteViews$ViewTree
-android.widget.RemoteViewsAdapter$RemoteAdapterConnectionCallback
-android.widget.ResourceCursorAdapter
-android.widget.RtlSpacingHelper
-android.widget.ScrollBarDrawable
-android.widget.ScrollView
-android.widget.ScrollView$SavedState
-android.widget.ScrollView$SavedState$1
-android.widget.Scroller
-android.widget.Scroller$ViscousFluidInterpolator
-android.widget.SearchView$OnCloseListener
-android.widget.SectionIndexer
-android.widget.SeekBar
-android.widget.SeekBar$OnSeekBarChangeListener
-android.widget.SelectionActionModeHelper
-android.widget.SelectionActionModeHelper$SelectionTracker
-android.widget.SelectionActionModeHelper$TextClassificationHelper
-android.widget.SimpleCursorAdapter
-android.widget.Space
-android.widget.SpellChecker
-android.widget.SpellChecker$SpellParser
-android.widget.Spinner
-android.widget.Spinner$1
-android.widget.Spinner$DialogPopup
-android.widget.Spinner$DropDownAdapter
-android.widget.Spinner$DropdownPopup
-android.widget.Spinner$DropdownPopup$1
-android.widget.Spinner$SpinnerPopup
-android.widget.SpinnerAdapter
-android.widget.Switch
-android.widget.Switch$1
-android.widget.TabHost
-android.widget.TabHost$1
-android.widget.TabHost$2
-android.widget.TabHost$ContentStrategy
-android.widget.TabHost$FactoryContentStrategy
-android.widget.TabHost$IndicatorStrategy
-android.widget.TabHost$OnTabChangeListener
-android.widget.TabHost$TabContentFactory
-android.widget.TabHost$TabSpec
-android.widget.TabHost$ViewIndicatorStrategy
-android.widget.TabWidget
-android.widget.TabWidget$OnTabSelectionChanged
-android.widget.TabWidget$TabClickListener
-android.widget.TableLayout
-android.widget.TextClock
-android.widget.TextClock$1
-android.widget.TextClock$2
-android.widget.TextClock$FormatChangeObserver
-android.widget.TextSwitcher
-android.widget.TextView
-android.widget.TextView$2
-android.widget.TextView$3
-android.widget.TextView$BufferType
-android.widget.TextView$ChangeWatcher
-android.widget.TextView$CharWrapper
-android.widget.TextView$Drawables
-android.widget.TextView$Marquee
-android.widget.TextView$Marquee$1
-android.widget.TextView$Marquee$2
-android.widget.TextView$Marquee$3
-android.widget.TextView$OnEditorActionListener
-android.widget.TextView$SavedState
-android.widget.TextView$SavedState$1
-android.widget.ThemedSpinnerAdapter
-android.widget.TimePicker
-android.widget.TimePicker$OnTimeChangedListener
-android.widget.Toast
-android.widget.Toast$TN
-android.widget.Toast$TN$1
-android.widget.ToggleButton
-android.widget.Toolbar
-android.widget.Toolbar$1
-android.widget.Toolbar$2
-android.widget.Toolbar$ExpandedActionViewMenuPresenter
-android.widget.Toolbar$LayoutParams
-android.widget.Toolbar$OnMenuItemClickListener
-android.widget.VideoView
-android.widget.ViewAnimator
-android.widget.ViewFlipper
-android.widget.ViewFlipper$1
-android.widget.ViewFlipper$2
-android.widget.ViewSwitcher
-android.widget.WrapperListAdapter
-com.android.framework.protobuf.nano.CodedInputByteBufferNano
-com.android.framework.protobuf.nano.CodedOutputByteBufferNano
-com.android.framework.protobuf.nano.InternalNano
-com.android.framework.protobuf.nano.InvalidProtocolBufferNanoException
-com.android.framework.protobuf.nano.MessageNano
-com.android.framework.protobuf.nano.WireFormatNano
-com.android.i18n.phonenumbers.AlternateFormatsCountryCodeSet
-com.android.i18n.phonenumbers.AsYouTypeFormatter
-com.android.i18n.phonenumbers.CountryCodeToRegionCodeMap
-com.android.i18n.phonenumbers.MetadataLoader
-com.android.i18n.phonenumbers.MetadataManager
-com.android.i18n.phonenumbers.MetadataManager$1
-com.android.i18n.phonenumbers.MetadataSource
-com.android.i18n.phonenumbers.MultiFileMetadataSourceImpl
-com.android.i18n.phonenumbers.NumberParseException
-com.android.i18n.phonenumbers.NumberParseException$ErrorType
-com.android.i18n.phonenumbers.PhoneNumberMatcher
-com.android.i18n.phonenumbers.PhoneNumberMatcher$State
-com.android.i18n.phonenumbers.PhoneNumberUtil
-com.android.i18n.phonenumbers.PhoneNumberUtil$1
-com.android.i18n.phonenumbers.PhoneNumberUtil$Leniency
-com.android.i18n.phonenumbers.PhoneNumberUtil$Leniency$1
-com.android.i18n.phonenumbers.PhoneNumberUtil$Leniency$2
-com.android.i18n.phonenumbers.PhoneNumberUtil$Leniency$3
-com.android.i18n.phonenumbers.PhoneNumberUtil$Leniency$4
-com.android.i18n.phonenumbers.PhoneNumberUtil$PhoneNumberFormat
-com.android.i18n.phonenumbers.PhoneNumberUtil$PhoneNumberType
-com.android.i18n.phonenumbers.PhoneNumberUtil$ValidationResult
-com.android.i18n.phonenumbers.Phonemetadata$NumberFormat
-com.android.i18n.phonenumbers.Phonemetadata$PhoneMetadata
-com.android.i18n.phonenumbers.Phonemetadata$PhoneMetadataCollection
-com.android.i18n.phonenumbers.Phonemetadata$PhoneNumberDesc
-com.android.i18n.phonenumbers.Phonenumber$PhoneNumber
-com.android.i18n.phonenumbers.Phonenumber$PhoneNumber$CountryCodeSource
-com.android.i18n.phonenumbers.RegexCache
-com.android.i18n.phonenumbers.RegexCache$LRUCache
-com.android.i18n.phonenumbers.RegexCache$LRUCache$1
-com.android.i18n.phonenumbers.ShortNumbersRegionCodeSet
-com.android.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder
-com.android.i18n.phonenumbers.prefixmapper.MappingFileProvider
-com.android.i18n.phonenumbers.prefixmapper.PrefixFileReader
-com.android.ims.-$Lambda$AvFHcs3Z6Dq6dkOugMW9Kc7Qzng$4
-com.android.ims.ImsCall$Listener
-com.android.ims.ImsCallForwardInfo
-com.android.ims.ImsCallProfile
-com.android.ims.ImsConfig
-com.android.ims.ImsConfigListener
-com.android.ims.ImsConfigListener$Stub
-com.android.ims.ImsConnectionStateListener
-com.android.ims.ImsEcbm
-com.android.ims.ImsEcbm$ImsEcbmListenerProxy
-com.android.ims.ImsEcbmStateListener
-com.android.ims.ImsException
-com.android.ims.ImsExternalCallStateListener
-com.android.ims.ImsManager
-com.android.ims.ImsManager$1
-com.android.ims.ImsManager$ImsRegistrationListenerProxy
-com.android.ims.ImsManager$ImsServiceDeathRecipient
-com.android.ims.ImsReasonInfo
-com.android.ims.ImsReasonInfo$1
-com.android.ims.ImsSsInfo
-com.android.ims.internal.IImsCallSession
-com.android.ims.internal.IImsCallSessionListener
-com.android.ims.internal.IImsConfig
-com.android.ims.internal.IImsConfig$Stub
-com.android.ims.internal.IImsEcbm
-com.android.ims.internal.IImsEcbm$Stub
-com.android.ims.internal.IImsEcbmListener
-com.android.ims.internal.IImsEcbmListener$Stub
-com.android.ims.internal.IImsMultiEndpoint
-com.android.ims.internal.IImsRegistrationListener
-com.android.ims.internal.IImsRegistrationListener$Stub
-com.android.ims.internal.IImsService
-com.android.ims.internal.IImsService$Stub
-com.android.ims.internal.IImsServiceController
-com.android.ims.internal.IImsServiceFeatureCallback
-com.android.ims.internal.IImsUt
-com.android.ims.internal.IImsUt$Stub
-com.android.ims.internal.IImsUtListener
-com.android.ims.internal.IImsUtListener$Stub
-com.android.internal.R$styleable
-com.android.internal.alsa.AlsaCardsParser
-com.android.internal.alsa.AlsaCardsParser$AlsaCardRecord
-com.android.internal.alsa.AlsaDevicesParser
-com.android.internal.alsa.LineTokenizer
-com.android.internal.app.AlertController
-com.android.internal.app.AlertController$1
-com.android.internal.app.AlertController$AlertParams
-com.android.internal.app.AlertController$AlertParams$1
-com.android.internal.app.AlertController$AlertParams$4
-com.android.internal.app.AlertController$ButtonHandler
-com.android.internal.app.AlertController$RecycleListView
-com.android.internal.app.AssistUtils
-com.android.internal.app.ColorDisplayController
-com.android.internal.app.ColorDisplayController$Callback
-com.android.internal.app.IAppOpsCallback
-com.android.internal.app.IAppOpsCallback$Stub
-com.android.internal.app.IAppOpsCallback$Stub$Proxy
-com.android.internal.app.IAppOpsService
-com.android.internal.app.IAppOpsService$Stub
-com.android.internal.app.IAppOpsService$Stub$Proxy
-com.android.internal.app.IAssistScreenshotReceiver
-com.android.internal.app.IBatteryStats
-com.android.internal.app.IBatteryStats$Stub
-com.android.internal.app.IBatteryStats$Stub$Proxy
-com.android.internal.app.IMediaContainerService
-com.android.internal.app.IMediaContainerService$Stub
-com.android.internal.app.IMediaContainerService$Stub$Proxy
-com.android.internal.app.ISoundTriggerService
-com.android.internal.app.ISoundTriggerService$Stub
-com.android.internal.app.IVoiceInteractionManagerService
-com.android.internal.app.IVoiceInteractionManagerService$Stub
-com.android.internal.app.IVoiceInteractionManagerService$Stub$Proxy
-com.android.internal.app.IVoiceInteractionSessionListener
-com.android.internal.app.IVoiceInteractionSessionListener$Stub
-com.android.internal.app.IVoiceInteractionSessionListener$Stub$Proxy
-com.android.internal.app.IVoiceInteractionSessionShowCallback
-com.android.internal.app.IVoiceInteractionSessionShowCallback$Stub
-com.android.internal.app.IVoiceInteractor
-com.android.internal.app.IVoiceInteractor$Stub
-com.android.internal.app.ProcessMap
-com.android.internal.app.ResolverActivity
-com.android.internal.app.ToolbarActionBar
-com.android.internal.app.ToolbarActionBar$1
-com.android.internal.app.ToolbarActionBar$2
-com.android.internal.app.ToolbarActionBar$ActionMenuPresenterCallback
-com.android.internal.app.ToolbarActionBar$MenuBuilderCallback
-com.android.internal.app.ToolbarActionBar$ToolbarCallbackWrapper
-com.android.internal.app.WindowDecorActionBar
-com.android.internal.app.WindowDecorActionBar$1
-com.android.internal.app.WindowDecorActionBar$2
-com.android.internal.app.WindowDecorActionBar$3
-com.android.internal.app.WindowDecorActionBar$ActionModeImpl
-com.android.internal.app.procstats.DurationsTable
-com.android.internal.app.procstats.IProcessStats
-com.android.internal.app.procstats.IProcessStats$Stub
-com.android.internal.app.procstats.ProcessState
-com.android.internal.app.procstats.ProcessState$1
-com.android.internal.app.procstats.ProcessStats
-com.android.internal.app.procstats.ProcessStats$1
-com.android.internal.app.procstats.ProcessStats$PackageState
-com.android.internal.app.procstats.ProcessStats$ProcessStateHolder
-com.android.internal.app.procstats.PssTable
-com.android.internal.app.procstats.ServiceState
-com.android.internal.app.procstats.SparseMappingTable
-com.android.internal.app.procstats.SparseMappingTable$Table
-com.android.internal.app.procstats.SysMemUsageTable
-com.android.internal.appwidget.IAppWidgetHost
-com.android.internal.appwidget.IAppWidgetHost$Stub
-com.android.internal.appwidget.IAppWidgetHost$Stub$Proxy
-com.android.internal.appwidget.IAppWidgetService
-com.android.internal.appwidget.IAppWidgetService$Stub
-com.android.internal.appwidget.IAppWidgetService$Stub$Proxy
-com.android.internal.backup.IBackupTransport
-com.android.internal.backup.IBackupTransport$Stub
-com.android.internal.backup.IBackupTransport$Stub$Proxy
-com.android.internal.backup.LocalTransport
-com.android.internal.backup.LocalTransportService
-com.android.internal.content.FileSystemProvider
-com.android.internal.content.NativeLibraryHelper
-com.android.internal.content.NativeLibraryHelper$Handle
-com.android.internal.content.PackageHelper
-com.android.internal.content.PackageHelper$1
-com.android.internal.content.PackageHelper$TestableInterface
-com.android.internal.content.PackageMonitor
-com.android.internal.content.ReferrerIntent
-com.android.internal.content.ReferrerIntent$1
-com.android.internal.graphics.drawable.AnimationScaleListDrawable
-com.android.internal.graphics.drawable.AnimationScaleListDrawable$AnimationScaleListState
-com.android.internal.hardware.AmbientDisplayConfiguration
-com.android.internal.inputmethod.IInputContentUriToken
-com.android.internal.inputmethod.InputMethodSubtypeHandle
-com.android.internal.inputmethod.InputMethodSubtypeSwitchingController
-com.android.internal.inputmethod.InputMethodSubtypeSwitchingController$ControllerImpl
-com.android.internal.inputmethod.InputMethodSubtypeSwitchingController$DynamicRotationList
-com.android.internal.inputmethod.InputMethodSubtypeSwitchingController$ImeSubtypeListItem
-com.android.internal.inputmethod.InputMethodSubtypeSwitchingController$InputMethodAndSubtypeList
-com.android.internal.inputmethod.InputMethodSubtypeSwitchingController$InputMethodAndSubtypeList$1
-com.android.internal.inputmethod.InputMethodSubtypeSwitchingController$StaticRotationList
-com.android.internal.inputmethod.InputMethodUtils
-com.android.internal.inputmethod.InputMethodUtils$1
-com.android.internal.inputmethod.InputMethodUtils$InputMethodListBuilder
-com.android.internal.inputmethod.InputMethodUtils$InputMethodSettings
-com.android.internal.inputmethod.LocaleUtils
-com.android.internal.inputmethod.LocaleUtils$LocaleExtractor
-com.android.internal.inputmethod.LocaleUtils$ScoreEntry
-com.android.internal.location.GpsNetInitiatedHandler
-com.android.internal.location.GpsNetInitiatedHandler$1
-com.android.internal.location.GpsNetInitiatedHandler$2
-com.android.internal.location.ILocationProvider
-com.android.internal.location.ILocationProvider$Stub
-com.android.internal.location.ILocationProvider$Stub$Proxy
-com.android.internal.location.ProviderProperties
-com.android.internal.location.ProviderProperties$1
-com.android.internal.location.ProviderRequest
-com.android.internal.location.ProviderRequest$1
-com.android.internal.logging.AndroidConfig
-com.android.internal.logging.AndroidHandler
-com.android.internal.logging.AndroidHandler$1
-com.android.internal.logging.EventLogTags
-com.android.internal.logging.MetricsLogger
-com.android.internal.net.LegacyVpnInfo
-com.android.internal.net.NetworkStatsFactory
-com.android.internal.net.VpnConfig
-com.android.internal.net.VpnInfo
-com.android.internal.net.VpnProfile
-com.android.internal.notification.SystemNotificationChannels
-com.android.internal.os.AndroidPrintStream
-com.android.internal.os.AppFuseMount
-com.android.internal.os.AtomicFile
-com.android.internal.os.BackgroundThread
-com.android.internal.os.BatteryStatsHelper
-com.android.internal.os.BatteryStatsImpl
-com.android.internal.os.BatteryStatsImpl$1
-com.android.internal.os.BatteryStatsImpl$6
-com.android.internal.os.BatteryStatsImpl$BatchTimer
-com.android.internal.os.BatteryStatsImpl$BatteryCallback
-com.android.internal.os.BatteryStatsImpl$Clocks
-com.android.internal.os.BatteryStatsImpl$ControllerActivityCounterImpl
-com.android.internal.os.BatteryStatsImpl$Counter
-com.android.internal.os.BatteryStatsImpl$DualTimer
-com.android.internal.os.BatteryStatsImpl$DurationTimer
-com.android.internal.os.BatteryStatsImpl$ExternalStatsSync
-com.android.internal.os.BatteryStatsImpl$LongSamplingCounter
-com.android.internal.os.BatteryStatsImpl$LongSamplingCounterArray
-com.android.internal.os.BatteryStatsImpl$MyHandler
-com.android.internal.os.BatteryStatsImpl$OverflowArrayMap
-com.android.internal.os.BatteryStatsImpl$PlatformIdleStateCallback
-com.android.internal.os.BatteryStatsImpl$SamplingTimer
-com.android.internal.os.BatteryStatsImpl$StopwatchTimer
-com.android.internal.os.BatteryStatsImpl$SystemClocks
-com.android.internal.os.BatteryStatsImpl$TimeBase
-com.android.internal.os.BatteryStatsImpl$TimeBaseObs
-com.android.internal.os.BatteryStatsImpl$Timer
-com.android.internal.os.BatteryStatsImpl$Uid
-com.android.internal.os.BatteryStatsImpl$Uid$1
-com.android.internal.os.BatteryStatsImpl$Uid$2
-com.android.internal.os.BatteryStatsImpl$Uid$3
-com.android.internal.os.BatteryStatsImpl$Uid$Pkg
-com.android.internal.os.BatteryStatsImpl$Uid$Pkg$Serv
-com.android.internal.os.BatteryStatsImpl$Uid$Sensor
-com.android.internal.os.BatteryStatsImpl$Uid$Wakelock
-com.android.internal.os.BinderInternal
-com.android.internal.os.BinderInternal$GcWatcher
-com.android.internal.os.FuseAppLoop
-com.android.internal.os.FuseAppLoop$1
-com.android.internal.os.FuseUnavailableMountException
-com.android.internal.os.HandlerCaller
-com.android.internal.os.HandlerCaller$Callback
-com.android.internal.os.HandlerCaller$MyHandler
-com.android.internal.os.IDropBoxManagerService
-com.android.internal.os.IDropBoxManagerService$Stub
-com.android.internal.os.IDropBoxManagerService$Stub$Proxy
-com.android.internal.os.IResultReceiver
-com.android.internal.os.IResultReceiver$Stub
-com.android.internal.os.IResultReceiver$Stub$Proxy
-com.android.internal.os.KernelCpuSpeedReader
-com.android.internal.os.KernelMemoryBandwidthStats
-com.android.internal.os.KernelUidCpuFreqTimeReader
-com.android.internal.os.KernelUidCpuTimeReader
-com.android.internal.os.KernelWakelockReader
-com.android.internal.os.KernelWakelockStats
-com.android.internal.os.KernelWakelockStats$Entry
-com.android.internal.os.LoggingPrintStream
-com.android.internal.os.LoggingPrintStream$1
-com.android.internal.os.PathClassLoaderFactory
-com.android.internal.os.PowerProfile
-com.android.internal.os.PowerProfile$CpuClusterKey
-com.android.internal.os.ProcessCpuTracker
-com.android.internal.os.ProcessCpuTracker$1
-com.android.internal.os.ProcessCpuTracker$FilterStats
-com.android.internal.os.ProcessCpuTracker$Stats
-com.android.internal.os.RoSystemProperties
-com.android.internal.os.RuntimeInit
-com.android.internal.os.RuntimeInit$1
-com.android.internal.os.RuntimeInit$Arguments
-com.android.internal.os.RuntimeInit$KillApplicationHandler
-com.android.internal.os.RuntimeInit$LoggingHandler
-com.android.internal.os.SamplingProfilerIntegration
-com.android.internal.os.SomeArgs
-com.android.internal.os.Zygote
-com.android.internal.os.Zygote$MethodAndArgsCaller
-com.android.internal.os.ZygoteConnection
-com.android.internal.os.ZygoteConnection$Arguments
-com.android.internal.os.ZygoteInit
-com.android.internal.os.ZygoteSecurityException
-com.android.internal.os.ZygoteServer
-com.android.internal.policy.DecorContext
-com.android.internal.policy.DecorView
-com.android.internal.policy.DecorView$1
-com.android.internal.policy.DecorView$ActionModeCallback2Wrapper
-com.android.internal.policy.DecorView$ColorViewAttributes
-com.android.internal.policy.DecorView$ColorViewState
-com.android.internal.policy.DividerSnapAlgorithm
-com.android.internal.policy.DividerSnapAlgorithm$SnapTarget
-com.android.internal.policy.DockedDividerUtils
-com.android.internal.policy.IKeyguardDismissCallback
-com.android.internal.policy.IKeyguardDrawnCallback
-com.android.internal.policy.IKeyguardDrawnCallback$Stub
-com.android.internal.policy.IKeyguardDrawnCallback$Stub$Proxy
-com.android.internal.policy.IKeyguardExitCallback
-com.android.internal.policy.IKeyguardService
-com.android.internal.policy.IKeyguardService$Stub
-com.android.internal.policy.IKeyguardService$Stub$Proxy
-com.android.internal.policy.IKeyguardStateCallback
-com.android.internal.policy.IKeyguardStateCallback$Stub
-com.android.internal.policy.IKeyguardStateCallback$Stub$Proxy
-com.android.internal.policy.IShortcutService
-com.android.internal.policy.IShortcutService$Stub
-com.android.internal.policy.IShortcutService$Stub$Proxy
-com.android.internal.policy.PhoneFallbackEventHandler
-com.android.internal.policy.PhoneLayoutInflater
-com.android.internal.policy.PhoneWindow
-com.android.internal.policy.PhoneWindow$1
-com.android.internal.policy.PhoneWindow$ActionMenuPresenterCallback
-com.android.internal.policy.PhoneWindow$PanelFeatureState
-com.android.internal.policy.PhoneWindow$PhoneWindowMenuCallback
-com.android.internal.policy.PhoneWindow$RotationWatcher
-com.android.internal.policy.PhoneWindow$RotationWatcher$1
-com.android.internal.policy.PipSnapAlgorithm
-com.android.internal.statusbar.IStatusBar
-com.android.internal.statusbar.IStatusBar$Stub
-com.android.internal.statusbar.IStatusBar$Stub$Proxy
-com.android.internal.statusbar.IStatusBarService
-com.android.internal.statusbar.IStatusBarService$Stub
-com.android.internal.statusbar.IStatusBarService$Stub$Proxy
-com.android.internal.statusbar.NotificationVisibility
-com.android.internal.statusbar.NotificationVisibility$1
-com.android.internal.statusbar.StatusBarIcon
-com.android.internal.statusbar.StatusBarIcon$1
-com.android.internal.telecom.IConnectionService
-com.android.internal.telecom.IConnectionService$Stub
-com.android.internal.telecom.IConnectionService$Stub$Proxy
-com.android.internal.telecom.IConnectionServiceAdapter
-com.android.internal.telecom.IConnectionServiceAdapter$Stub
-com.android.internal.telecom.IConnectionServiceAdapter$Stub$Proxy
-com.android.internal.telecom.IInCallAdapter
-com.android.internal.telecom.IInCallAdapter$Stub
-com.android.internal.telecom.IInCallService
-com.android.internal.telecom.IInCallService$Stub
-com.android.internal.telecom.IInCallService$Stub$Proxy
-com.android.internal.telecom.ITelecomService
-com.android.internal.telecom.ITelecomService$Stub
-com.android.internal.telecom.ITelecomService$Stub$Proxy
-com.android.internal.telecom.IVideoProvider
-com.android.internal.telecom.IVideoProvider$Stub
-com.android.internal.telecom.RemoteServiceCallback
-com.android.internal.telecom.RemoteServiceCallback$Stub
-com.android.internal.telecom.RemoteServiceCallback$Stub$Proxy
-com.android.internal.telephony.AppSmsManager
-com.android.internal.telephony.BaseCommands
-com.android.internal.telephony.Call
-com.android.internal.telephony.Call$SrvccState
-com.android.internal.telephony.Call$State
-com.android.internal.telephony.CallManager
-com.android.internal.telephony.CallManager$CallManagerHandler
-com.android.internal.telephony.CallStateException
-com.android.internal.telephony.CallTracker
-com.android.internal.telephony.CallerInfo
-com.android.internal.telephony.CallerInfoAsyncQuery
-com.android.internal.telephony.CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler
-com.android.internal.telephony.CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler$CallerInfoWorkerHandler
-com.android.internal.telephony.CallerInfoAsyncQuery$CookieWrapper
-com.android.internal.telephony.CallerInfoAsyncQuery$OnQueryCompleteListener
-com.android.internal.telephony.CarrierActionAgent
-com.android.internal.telephony.CarrierActionAgent$1
-com.android.internal.telephony.CarrierActionAgent$SettingsObserver
-com.android.internal.telephony.CarrierAppUtils
-com.android.internal.telephony.CarrierServiceBindHelper
-com.android.internal.telephony.CarrierServiceBindHelper$1
-com.android.internal.telephony.CarrierServiceBindHelper$2
-com.android.internal.telephony.CarrierServiceBindHelper$AppBinding
-com.android.internal.telephony.CarrierServiceBindHelper$CarrierServicePackageMonitor
-com.android.internal.telephony.CarrierServiceStateTracker
-com.android.internal.telephony.CarrierServiceStateTracker$1
-com.android.internal.telephony.CarrierSignalAgent
-com.android.internal.telephony.CarrierSignalAgent$1
-com.android.internal.telephony.CellBroadcastHandler
-com.android.internal.telephony.CellNetworkScanResult
-com.android.internal.telephony.ClientWakelockAccountant
-com.android.internal.telephony.ClientWakelockTracker
-com.android.internal.telephony.CommandException
-com.android.internal.telephony.CommandException$Error
-com.android.internal.telephony.CommandsInterface
-com.android.internal.telephony.CommandsInterface$RadioState
-com.android.internal.telephony.Connection
-com.android.internal.telephony.DctConstants$Activity
-com.android.internal.telephony.DctConstants$State
-com.android.internal.telephony.DebugService
-com.android.internal.telephony.DefaultPhoneNotifier
-com.android.internal.telephony.DeviceStateMonitor
-com.android.internal.telephony.DeviceStateMonitor$1
-com.android.internal.telephony.DeviceStateMonitor$2
-com.android.internal.telephony.EncodeException
-com.android.internal.telephony.GsmAlphabet
-com.android.internal.telephony.GsmAlphabet$TextEncodingDetails
-com.android.internal.telephony.GsmCdmaCall
-com.android.internal.telephony.GsmCdmaCallTracker
-com.android.internal.telephony.GsmCdmaCallTracker$1
-com.android.internal.telephony.GsmCdmaConnection
-com.android.internal.telephony.GsmCdmaPhone
-com.android.internal.telephony.GsmCdmaPhone$1
-com.android.internal.telephony.GsmCdmaPhone$2
-com.android.internal.telephony.HardwareConfig
-com.android.internal.telephony.ICarrierConfigLoader
-com.android.internal.telephony.ICarrierConfigLoader$Stub
-com.android.internal.telephony.ICarrierConfigLoader$Stub$Proxy
-com.android.internal.telephony.IIccPhoneBook
-com.android.internal.telephony.IIccPhoneBook$Stub
-com.android.internal.telephony.IMms
-com.android.internal.telephony.IMms$Stub
-com.android.internal.telephony.IOnSubscriptionsChangedListener
-com.android.internal.telephony.IOnSubscriptionsChangedListener$Stub
-com.android.internal.telephony.IOnSubscriptionsChangedListener$Stub$Proxy
-com.android.internal.telephony.IPhoneStateListener
-com.android.internal.telephony.IPhoneStateListener$Stub
-com.android.internal.telephony.IPhoneStateListener$Stub$Proxy
-com.android.internal.telephony.IPhoneSubInfo
-com.android.internal.telephony.IPhoneSubInfo$Stub
-com.android.internal.telephony.IPhoneSubInfo$Stub$Proxy
-com.android.internal.telephony.ISms
-com.android.internal.telephony.ISms$Stub
-com.android.internal.telephony.ISms$Stub$Proxy
-com.android.internal.telephony.ISub
-com.android.internal.telephony.ISub$Stub
-com.android.internal.telephony.ISub$Stub$Proxy
-com.android.internal.telephony.ITelephony
-com.android.internal.telephony.ITelephony$Stub
-com.android.internal.telephony.ITelephony$Stub$Proxy
-com.android.internal.telephony.ITelephonyRegistry
-com.android.internal.telephony.ITelephonyRegistry$Stub
-com.android.internal.telephony.ITelephonyRegistry$Stub$Proxy
-com.android.internal.telephony.IWapPushManager
-com.android.internal.telephony.IccCard
-com.android.internal.telephony.IccCardConstants$State
-com.android.internal.telephony.IccPhoneBookInterfaceManager
-com.android.internal.telephony.IccPhoneBookInterfaceManager$1
-com.android.internal.telephony.IccProvider
-com.android.internal.telephony.IccSmsInterfaceManager
-com.android.internal.telephony.IccSmsInterfaceManager$1
-com.android.internal.telephony.IccSmsInterfaceManager$CdmaBroadcastRangeManager
-com.android.internal.telephony.IccSmsInterfaceManager$CellBroadcastRangeManager
-com.android.internal.telephony.ImsSMSDispatcher
-com.android.internal.telephony.InboundSmsHandler
-com.android.internal.telephony.InboundSmsHandler$1
-com.android.internal.telephony.InboundSmsHandler$DefaultState
-com.android.internal.telephony.InboundSmsHandler$DeliveringState
-com.android.internal.telephony.InboundSmsHandler$IdleState
-com.android.internal.telephony.InboundSmsHandler$NewMessageNotificationActionReceiver
-com.android.internal.telephony.InboundSmsHandler$StartupState
-com.android.internal.telephony.InboundSmsHandler$WaitingState
-com.android.internal.telephony.IntRangeManager
-com.android.internal.telephony.OemHookIndication
-com.android.internal.telephony.OemHookResponse
-com.android.internal.telephony.OperatorInfo
-com.android.internal.telephony.Phone
-com.android.internal.telephony.Phone$1
-com.android.internal.telephony.PhoneConstantConversions
-com.android.internal.telephony.PhoneConstants$DataState
-com.android.internal.telephony.PhoneConstants$State
-com.android.internal.telephony.PhoneFactory
-com.android.internal.telephony.PhoneInternalInterface
-com.android.internal.telephony.PhoneInternalInterface$DataActivityState
-com.android.internal.telephony.PhoneNotifier
-com.android.internal.telephony.PhoneSubInfoController
-com.android.internal.telephony.PhoneSwitcher
-com.android.internal.telephony.PhoneSwitcher$1
-com.android.internal.telephony.PhoneSwitcher$2
-com.android.internal.telephony.PhoneSwitcher$PhoneState
-com.android.internal.telephony.PhoneSwitcher$PhoneSwitcherNetworkRequestListener
-com.android.internal.telephony.ProxyController
-com.android.internal.telephony.ProxyController$1
-com.android.internal.telephony.RIL
-com.android.internal.telephony.RIL$RadioProxyDeathRecipient
-com.android.internal.telephony.RIL$RilHandler
-com.android.internal.telephony.RILConstants
-com.android.internal.telephony.RILRequest
-com.android.internal.telephony.RadioCapability
-com.android.internal.telephony.RadioIndication
-com.android.internal.telephony.RadioResponse
-com.android.internal.telephony.RatRatcheter
-com.android.internal.telephony.RatRatcheter$1
-com.android.internal.telephony.RestrictedState
-com.android.internal.telephony.RetryManager
-com.android.internal.telephony.RilWakelockInfo
-com.android.internal.telephony.SMSDispatcher
-com.android.internal.telephony.SMSDispatcher$SettingsObserver
-com.android.internal.telephony.ServiceStateTracker
-com.android.internal.telephony.ServiceStateTracker$1
-com.android.internal.telephony.ServiceStateTracker$2
-com.android.internal.telephony.ServiceStateTracker$3
-com.android.internal.telephony.ServiceStateTracker$CellInfoResult
-com.android.internal.telephony.ServiceStateTracker$SstSubscriptionsChangedListener
-com.android.internal.telephony.SimActivationTracker
-com.android.internal.telephony.SimActivationTracker$1
-com.android.internal.telephony.SmsApplication
-com.android.internal.telephony.SmsApplication$SmsApplicationData
-com.android.internal.telephony.SmsApplication$SmsPackageMonitor
-com.android.internal.telephony.SmsBroadcastUndelivered
-com.android.internal.telephony.SmsBroadcastUndelivered$1
-com.android.internal.telephony.SmsBroadcastUndelivered$ScanRawTableThread
-com.android.internal.telephony.SmsMessageBase
-com.android.internal.telephony.SmsStorageMonitor
-com.android.internal.telephony.SmsStorageMonitor$1
-com.android.internal.telephony.SmsUsageMonitor
-com.android.internal.telephony.SmsUsageMonitor$SettingsObserver
-com.android.internal.telephony.SmsUsageMonitor$SettingsObserverHandler
-com.android.internal.telephony.SubscriptionController
-com.android.internal.telephony.SubscriptionController$ScLocalLog
-com.android.internal.telephony.SubscriptionInfoUpdater
-com.android.internal.telephony.SubscriptionInfoUpdater$1
-com.android.internal.telephony.SubscriptionInfoUpdater$2
-com.android.internal.telephony.SubscriptionMonitor
-com.android.internal.telephony.SubscriptionMonitor$1
-com.android.internal.telephony.SubscriptionMonitor$2
-com.android.internal.telephony.TelephonyCapabilities
-com.android.internal.telephony.TelephonyComponentFactory
-com.android.internal.telephony.TelephonyDevController
-com.android.internal.telephony.TelephonyTester
-com.android.internal.telephony.TelephonyTester$1
-com.android.internal.telephony.UiccPhoneBookController
-com.android.internal.telephony.UiccSmsController
-com.android.internal.telephony.WakeLockStateMachine
-com.android.internal.telephony.WakeLockStateMachine$1
-com.android.internal.telephony.WakeLockStateMachine$DefaultState
-com.android.internal.telephony.WakeLockStateMachine$IdleState
-com.android.internal.telephony.WakeLockStateMachine$WaitingState
-com.android.internal.telephony.WapPushOverSms
-com.android.internal.telephony.WapPushOverSms$1
-com.android.internal.telephony.WapPushOverSms$BindServiceThread
-com.android.internal.telephony.cat.AppInterface
-com.android.internal.telephony.cat.CatLog
-com.android.internal.telephony.cat.CatService
-com.android.internal.telephony.cdma.CdmaInboundSmsHandler
-com.android.internal.telephony.cdma.CdmaSMSDispatcher
-com.android.internal.telephony.cdma.CdmaServiceCategoryProgramHandler
-com.android.internal.telephony.cdma.CdmaServiceCategoryProgramHandler$1
-com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager
-com.android.internal.telephony.cdma.EriInfo
-com.android.internal.telephony.cdma.EriManager
-com.android.internal.telephony.cdma.EriManager$EriFile
-com.android.internal.telephony.dataconnection.ApnContext
-com.android.internal.telephony.dataconnection.DataConnection
-com.android.internal.telephony.dataconnection.DataEnabledSettings
-com.android.internal.telephony.dataconnection.DcController
-com.android.internal.telephony.dataconnection.DcController$1
-com.android.internal.telephony.dataconnection.DcController$DccDefaultState
-com.android.internal.telephony.dataconnection.DcFailBringUp
-com.android.internal.telephony.dataconnection.DcFailCause
-com.android.internal.telephony.dataconnection.DcRequest
-com.android.internal.telephony.dataconnection.DcTesterDeactivateAll
-com.android.internal.telephony.dataconnection.DcTesterDeactivateAll$1
-com.android.internal.telephony.dataconnection.DcTesterFailBringUpAll
-com.android.internal.telephony.dataconnection.DcTesterFailBringUpAll$1
-com.android.internal.telephony.dataconnection.DcTracker
-com.android.internal.telephony.dataconnection.DcTracker$1
-com.android.internal.telephony.dataconnection.DcTracker$2
-com.android.internal.telephony.dataconnection.DcTracker$3
-com.android.internal.telephony.dataconnection.DcTracker$4
-com.android.internal.telephony.dataconnection.DcTracker$ApnChangeObserver
-com.android.internal.telephony.dataconnection.DcTracker$DataAllowFailReason
-com.android.internal.telephony.dataconnection.DcTracker$DataAllowFailReasonType
-com.android.internal.telephony.dataconnection.DcTracker$RetryFailures
-com.android.internal.telephony.dataconnection.DcTracker$SettingsObserver
-com.android.internal.telephony.dataconnection.DcTracker$TxRxSum
-com.android.internal.telephony.dataconnection.TelephonyNetworkFactory
-com.android.internal.telephony.dataconnection.TelephonyNetworkFactory$InternalHandler
-com.android.internal.telephony.gsm.GsmCellBroadcastHandler
-com.android.internal.telephony.gsm.GsmInboundSmsHandler
-com.android.internal.telephony.gsm.GsmSMSDispatcher
-com.android.internal.telephony.gsm.SmsMessage
-com.android.internal.telephony.gsm.UsimDataDownloadHandler
-com.android.internal.telephony.ims.-$Lambda$6hDwuvYxqWrzW_Ex5wc53XnUOpg
-com.android.internal.telephony.ims.-$Lambda$6hDwuvYxqWrzW_Ex5wc53XnUOpg$1
-com.android.internal.telephony.ims.-$Lambda$6hDwuvYxqWrzW_Ex5wc53XnUOpg$2
-com.android.internal.telephony.ims.ImsResolver
-com.android.internal.telephony.ims.ImsResolver$1
-com.android.internal.telephony.ims.ImsResolver$2
-com.android.internal.telephony.ims.ImsResolver$3
-com.android.internal.telephony.ims.ImsResolver$ImsServiceControllerFactory
-com.android.internal.telephony.ims.ImsResolver$SubscriptionManagerProxy
-com.android.internal.telephony.ims.ImsServiceController$ImsServiceControllerCallbacks
-com.android.internal.telephony.imsphone.-$Lambda$tILLuSJl16qfDJK1ikBVGFm2D5w
-com.android.internal.telephony.imsphone.-$Lambda$tILLuSJl16qfDJK1ikBVGFm2D5w$1
-com.android.internal.telephony.imsphone.ImsExternalCallTracker
-com.android.internal.telephony.imsphone.ImsExternalCallTracker$1
-com.android.internal.telephony.imsphone.ImsExternalCallTracker$2
-com.android.internal.telephony.imsphone.ImsExternalCallTracker$ExternalCallStateListener
-com.android.internal.telephony.imsphone.ImsExternalCallTracker$ExternalConnectionListener
-com.android.internal.telephony.imsphone.ImsExternalCallTracker$ImsCallNotify
-com.android.internal.telephony.imsphone.ImsExternalConnection$Listener
-com.android.internal.telephony.imsphone.ImsPhone
-com.android.internal.telephony.imsphone.ImsPhone$1
-com.android.internal.telephony.imsphone.ImsPhone$2
-com.android.internal.telephony.imsphone.ImsPhone$3
-com.android.internal.telephony.imsphone.ImsPhoneBase
-com.android.internal.telephony.imsphone.ImsPhoneCall
-com.android.internal.telephony.imsphone.ImsPhoneCallTracker
-com.android.internal.telephony.imsphone.ImsPhoneCallTracker$1
-com.android.internal.telephony.imsphone.ImsPhoneCallTracker$2
-com.android.internal.telephony.imsphone.ImsPhoneCallTracker$3
-com.android.internal.telephony.imsphone.ImsPhoneCallTracker$4
-com.android.internal.telephony.imsphone.ImsPhoneCallTracker$5
-com.android.internal.telephony.imsphone.ImsPhoneCallTracker$IRetryTimeout
-com.android.internal.telephony.imsphone.ImsPhoneCallTracker$PhoneStateListener
-com.android.internal.telephony.imsphone.ImsPhoneCommandInterface
-com.android.internal.telephony.imsphone.ImsPhoneFactory
-com.android.internal.telephony.imsphone.ImsPullCall
-com.android.internal.telephony.metrics.CallSessionEventBuilder
-com.android.internal.telephony.metrics.InProgressCallSession
-com.android.internal.telephony.metrics.InProgressSmsSession
-com.android.internal.telephony.metrics.SmsSessionEventBuilder
-com.android.internal.telephony.metrics.TelephonyEventBuilder
-com.android.internal.telephony.metrics.TelephonyMetrics
-com.android.internal.telephony.nano.TelephonyProto$ImsCapabilities
-com.android.internal.telephony.nano.TelephonyProto$ImsConnectionState
-com.android.internal.telephony.nano.TelephonyProto$ImsReasonInfo
-com.android.internal.telephony.nano.TelephonyProto$RilDataCall
-com.android.internal.telephony.nano.TelephonyProto$SmsSession$Event
-com.android.internal.telephony.nano.TelephonyProto$TelephonyCallSession$Event
-com.android.internal.telephony.nano.TelephonyProto$TelephonyCallSession$Event$RilCall
-com.android.internal.telephony.nano.TelephonyProto$TelephonyEvent
-com.android.internal.telephony.nano.TelephonyProto$TelephonyServiceState
-com.android.internal.telephony.nano.TelephonyProto$TelephonyServiceState$TelephonyOperator
-com.android.internal.telephony.nano.TelephonyProto$TelephonySettings
-com.android.internal.telephony.protobuf.nano.CodedOutputByteBufferNano
-com.android.internal.telephony.protobuf.nano.ExtendableMessageNano
-com.android.internal.telephony.protobuf.nano.InternalNano
-com.android.internal.telephony.protobuf.nano.InvalidProtocolBufferNanoException
-com.android.internal.telephony.protobuf.nano.MessageNano
-com.android.internal.telephony.protobuf.nano.WireFormatNano
-com.android.internal.telephony.test.SimulatedRadioControl
-com.android.internal.telephony.uicc.IccCardApplicationStatus
-com.android.internal.telephony.uicc.IccCardApplicationStatus$AppType
-com.android.internal.telephony.uicc.IccCardProxy
-com.android.internal.telephony.uicc.IccCardStatus
-com.android.internal.telephony.uicc.IccCardStatus$CardState
-com.android.internal.telephony.uicc.IccCardStatus$PinState
-com.android.internal.telephony.uicc.IccConstants
-com.android.internal.telephony.uicc.IccRecords
-com.android.internal.telephony.uicc.IccUtils
-com.android.internal.telephony.uicc.UiccCard
-com.android.internal.telephony.uicc.UiccCard$1
-com.android.internal.telephony.uicc.UiccCardApplication
-com.android.internal.telephony.uicc.UiccController
-com.android.internal.telephony.uicc.UiccStateChangedLauncher
-com.android.internal.telephony.util.NotificationChannelController
-com.android.internal.telephony.util.NotificationChannelController$1
-com.android.internal.textservice.ISpellCheckerService
-com.android.internal.textservice.ISpellCheckerService$Stub
-com.android.internal.textservice.ISpellCheckerService$Stub$Proxy
-com.android.internal.textservice.ISpellCheckerServiceCallback
-com.android.internal.textservice.ISpellCheckerServiceCallback$Stub
-com.android.internal.textservice.ISpellCheckerServiceCallback$Stub$Proxy
-com.android.internal.textservice.ISpellCheckerSession
-com.android.internal.textservice.ISpellCheckerSession$Stub
-com.android.internal.textservice.ISpellCheckerSession$Stub$Proxy
-com.android.internal.textservice.ISpellCheckerSessionListener
-com.android.internal.textservice.ISpellCheckerSessionListener$Stub
-com.android.internal.textservice.ISpellCheckerSessionListener$Stub$Proxy
-com.android.internal.textservice.ITextServicesManager
-com.android.internal.textservice.ITextServicesManager$Stub
-com.android.internal.textservice.ITextServicesManager$Stub$Proxy
-com.android.internal.textservice.ITextServicesSessionListener
-com.android.internal.textservice.ITextServicesSessionListener$Stub
-com.android.internal.textservice.ITextServicesSessionListener$Stub$Proxy
-com.android.internal.transition.EpicenterTranslateClipReveal
-com.android.internal.transition.TransitionConstants
-com.android.internal.util.ArrayUtils
-com.android.internal.util.AsyncChannel
-com.android.internal.util.AsyncChannel$AsyncChannelConnection
-com.android.internal.util.AsyncChannel$DeathMonitor
-com.android.internal.util.AsyncChannel$SyncMessenger
-com.android.internal.util.AsyncChannel$SyncMessenger$SyncHandler
-com.android.internal.util.BitUtils
-com.android.internal.util.CollectionUtils
-com.android.internal.util.ConcurrentUtils
-com.android.internal.util.ConcurrentUtils$1
-com.android.internal.util.ConcurrentUtils$1$1
-com.android.internal.util.DumpUtils$Dump
-com.android.internal.util.ExponentiallyBucketedHistogram
-com.android.internal.util.FastMath
-com.android.internal.util.FastPrintWriter
-com.android.internal.util.FastPrintWriter$DummyWriter
-com.android.internal.util.FastXmlSerializer
-com.android.internal.util.FileRotator
-com.android.internal.util.FileRotator$FileInfo
-com.android.internal.util.FileRotator$Reader
-com.android.internal.util.FileRotator$Rewriter
-com.android.internal.util.FileRotator$Writer
-com.android.internal.util.GrowingArrayUtils
-com.android.internal.util.HexDump
-com.android.internal.util.IState
-com.android.internal.util.ImageUtils
-com.android.internal.util.IndentingPrintWriter
-com.android.internal.util.IntPair
-com.android.internal.util.JournaledFile
-com.android.internal.util.LineBreakBufferedWriter
-com.android.internal.util.LocalLog
-com.android.internal.util.MemInfoReader
-com.android.internal.util.MessageUtils
-com.android.internal.util.NotificationColorUtil
-com.android.internal.util.NotificationColorUtil$ColorUtilsFromCompat
-com.android.internal.util.NotificationMessagingUtil
-com.android.internal.util.NotificationMessagingUtil$1
-com.android.internal.util.Preconditions
-com.android.internal.util.ProcFileReader
-com.android.internal.util.ProgressReporter
-com.android.internal.util.RingBufferIndices
-com.android.internal.util.ScreenShapeHelper
-com.android.internal.util.State
-com.android.internal.util.StateMachine
-com.android.internal.util.StateMachine$LogRec
-com.android.internal.util.StateMachine$LogRecords
-com.android.internal.util.StateMachine$SmHandler
-com.android.internal.util.StateMachine$SmHandler$HaltingState
-com.android.internal.util.StateMachine$SmHandler$QuittingState
-com.android.internal.util.StateMachine$SmHandler$StateInfo
-com.android.internal.util.ToBooleanFunction
-com.android.internal.util.TokenBucket
-com.android.internal.util.VirtualRefBasePtr
-com.android.internal.util.WakeupMessage
-com.android.internal.util.XmlUtils
-com.android.internal.util.XmlUtils$ReadMapCallback
-com.android.internal.util.XmlUtils$WriteMapCallback
-com.android.internal.view.ActionBarPolicy
-com.android.internal.view.BaseIWindow
-com.android.internal.view.BaseSurfaceHolder
-com.android.internal.view.IInputConnectionWrapper
-com.android.internal.view.IInputConnectionWrapper$MyHandler
-com.android.internal.view.IInputContext
-com.android.internal.view.IInputContext$Stub
-com.android.internal.view.IInputContext$Stub$Proxy
-com.android.internal.view.IInputContextCallback
-com.android.internal.view.IInputContextCallback$Stub
-com.android.internal.view.IInputContextCallback$Stub$Proxy
-com.android.internal.view.IInputMethod
-com.android.internal.view.IInputMethod$Stub
-com.android.internal.view.IInputMethod$Stub$Proxy
-com.android.internal.view.IInputMethodClient
-com.android.internal.view.IInputMethodClient$Stub
-com.android.internal.view.IInputMethodClient$Stub$Proxy
-com.android.internal.view.IInputMethodManager
-com.android.internal.view.IInputMethodManager$Stub
-com.android.internal.view.IInputMethodManager$Stub$Proxy
-com.android.internal.view.IInputMethodSession
-com.android.internal.view.IInputMethodSession$Stub
-com.android.internal.view.IInputMethodSession$Stub$Proxy
-com.android.internal.view.IInputSessionCallback
-com.android.internal.view.IInputSessionCallback$Stub
-com.android.internal.view.IInputSessionCallback$Stub$Proxy
-com.android.internal.view.InputBindResult
-com.android.internal.view.InputBindResult$1
-com.android.internal.view.InputConnectionWrapper
-com.android.internal.view.InputConnectionWrapper$InputContextCallback
-com.android.internal.view.OneShotPreDrawListener
-com.android.internal.view.RootViewSurfaceTaker
-com.android.internal.view.RotationPolicy
-com.android.internal.view.RotationPolicy$RotationPolicyListener
-com.android.internal.view.RotationPolicy$RotationPolicyListener$1
-com.android.internal.view.SurfaceCallbackHelper
-com.android.internal.view.SurfaceCallbackHelper$1
-com.android.internal.view.SurfaceFlingerVsyncChoreographer
-com.android.internal.view.WindowManagerPolicyThread
-com.android.internal.view.animation.FallbackLUTInterpolator
-com.android.internal.view.animation.HasNativeInterpolator
-com.android.internal.view.animation.NativeInterpolatorFactory
-com.android.internal.view.animation.NativeInterpolatorFactoryHelper
-com.android.internal.view.menu.ActionMenuItem
-com.android.internal.view.menu.ActionMenuItemView
-com.android.internal.view.menu.ActionMenuItemView$PopupCallback
-com.android.internal.view.menu.BaseMenuPresenter
-com.android.internal.view.menu.ContextMenuBuilder
-com.android.internal.view.menu.MenuBuilder
-com.android.internal.view.menu.MenuBuilder$Callback
-com.android.internal.view.menu.MenuBuilder$ItemInvoker
-com.android.internal.view.menu.MenuHelper
-com.android.internal.view.menu.MenuItemImpl
-com.android.internal.view.menu.MenuPopupHelper
-com.android.internal.view.menu.MenuPopupHelper$1
-com.android.internal.view.menu.MenuPresenter
-com.android.internal.view.menu.MenuPresenter$Callback
-com.android.internal.view.menu.MenuView
-com.android.internal.view.menu.MenuView$ItemView
-com.android.internal.view.menu.ShowableListMenu
-com.android.internal.widget.-$Lambda$LaTFiUorkqfcqmu-zMQbCLeO77c
-com.android.internal.widget.AbsActionBarView
-com.android.internal.widget.AbsActionBarView$VisibilityAnimListener
-com.android.internal.widget.ActionBarContainer
-com.android.internal.widget.ActionBarContainer$ActionBarBackgroundDrawable
-com.android.internal.widget.ActionBarContextView
-com.android.internal.widget.ActionBarContextView$1
-com.android.internal.widget.ActionBarOverlayLayout
-com.android.internal.widget.ActionBarOverlayLayout$1
-com.android.internal.widget.ActionBarOverlayLayout$2
-com.android.internal.widget.ActionBarOverlayLayout$3
-com.android.internal.widget.ActionBarOverlayLayout$4
-com.android.internal.widget.ActionBarOverlayLayout$5
-com.android.internal.widget.ActionBarOverlayLayout$ActionBarVisibilityCallback
-com.android.internal.widget.ActionBarOverlayLayout$LayoutParams
-com.android.internal.widget.AlertDialogLayout
-com.android.internal.widget.BackgroundFallback
-com.android.internal.widget.ButtonBarLayout
-com.android.internal.widget.CachingIconView
-com.android.internal.widget.DecorContentParent
-com.android.internal.widget.DecorToolbar
-com.android.internal.widget.DialogTitle
-com.android.internal.widget.EditableInputConnection
-com.android.internal.widget.ICheckCredentialProgressCallback
-com.android.internal.widget.ILockSettings
-com.android.internal.widget.ILockSettings$Stub
-com.android.internal.widget.ILockSettings$Stub$Proxy
-com.android.internal.widget.ImageFloatingTextView
-com.android.internal.widget.LockPatternUtils
-com.android.internal.widget.LockPatternUtils$RequestThrottledException
-com.android.internal.widget.LockPatternUtils$StrongAuthTracker
-com.android.internal.widget.LockPatternUtils$StrongAuthTracker$1
-com.android.internal.widget.LockPatternUtils$StrongAuthTracker$H
-com.android.internal.widget.MediaNotificationView
-com.android.internal.widget.NotificationActionListLayout
-com.android.internal.widget.NotificationExpandButton
-com.android.internal.widget.ScrollBarUtils
-com.android.internal.widget.ToolbarWidgetWrapper
-com.android.internal.widget.ToolbarWidgetWrapper$1
-com.android.internal.widget.ToolbarWidgetWrapper$2
-com.android.internal.widget.VerifyCredentialResponse
-com.android.okhttp.Address
-com.android.okhttp.AndroidInternal
-com.android.okhttp.AndroidShimResponseCache
-com.android.okhttp.Authenticator
-com.android.okhttp.Cache
-com.android.okhttp.Cache$1
-com.android.okhttp.Cache$CacheRequestImpl
-com.android.okhttp.Cache$CacheRequestImpl$1
-com.android.okhttp.Cache$Entry
-com.android.okhttp.CacheControl
-com.android.okhttp.CacheControl$Builder
-com.android.okhttp.CertificatePinner
-com.android.okhttp.CertificatePinner$Builder
-com.android.okhttp.CipherSuite
-com.android.okhttp.ConfigAwareConnectionPool
-com.android.okhttp.ConfigAwareConnectionPool$1
-com.android.okhttp.Connection
-com.android.okhttp.ConnectionPool
-com.android.okhttp.ConnectionPool$1
-com.android.okhttp.ConnectionSpec
-com.android.okhttp.ConnectionSpec$Builder
-com.android.okhttp.Dispatcher
-com.android.okhttp.Dns
-com.android.okhttp.Dns$1
-com.android.okhttp.Handshake
-com.android.okhttp.Headers
-com.android.okhttp.Headers$Builder
-com.android.okhttp.HttpHandler
-com.android.okhttp.HttpHandler$CleartextURLFilter
-com.android.okhttp.HttpUrl
-com.android.okhttp.HttpUrl$Builder
-com.android.okhttp.HttpUrl$Builder$ParseResult
-com.android.okhttp.HttpsHandler
-com.android.okhttp.OkCacheContainer
-com.android.okhttp.OkHttpClient
-com.android.okhttp.OkHttpClient$1
-com.android.okhttp.OkUrlFactory
-com.android.okhttp.Protocol
-com.android.okhttp.Request
-com.android.okhttp.Request$Builder
-com.android.okhttp.RequestBody
-com.android.okhttp.RequestBody$2
-com.android.okhttp.Response
-com.android.okhttp.Response$Builder
-com.android.okhttp.ResponseBody
-com.android.okhttp.Route
-com.android.okhttp.TlsVersion
-com.android.okhttp.internal.ConnectionSpecSelector
-com.android.okhttp.internal.DiskLruCache
-com.android.okhttp.internal.DiskLruCache$1
-com.android.okhttp.internal.DiskLruCache$2
-com.android.okhttp.internal.DiskLruCache$3
-com.android.okhttp.internal.DiskLruCache$Editor
-com.android.okhttp.internal.DiskLruCache$Editor$1
-com.android.okhttp.internal.DiskLruCache$Entry
-com.android.okhttp.internal.FaultHidingSink
-com.android.okhttp.internal.Internal
-com.android.okhttp.internal.InternalCache
-com.android.okhttp.internal.OptionalMethod
-com.android.okhttp.internal.Platform
-com.android.okhttp.internal.RouteDatabase
-com.android.okhttp.internal.URLFilter
-com.android.okhttp.internal.Util
-com.android.okhttp.internal.Util$1
-com.android.okhttp.internal.http.AuthenticatorAdapter
-com.android.okhttp.internal.http.CacheRequest
-com.android.okhttp.internal.http.CacheStrategy
-com.android.okhttp.internal.http.CacheStrategy$Factory
-com.android.okhttp.internal.http.HeaderParser
-com.android.okhttp.internal.http.Http1xStream
-com.android.okhttp.internal.http.Http1xStream$AbstractSource
-com.android.okhttp.internal.http.Http1xStream$ChunkedSink
-com.android.okhttp.internal.http.Http1xStream$ChunkedSource
-com.android.okhttp.internal.http.Http1xStream$FixedLengthSink
-com.android.okhttp.internal.http.Http1xStream$FixedLengthSource
-com.android.okhttp.internal.http.Http1xStream$UnknownLengthSource
-com.android.okhttp.internal.http.HttpEngine
-com.android.okhttp.internal.http.HttpEngine$1
-com.android.okhttp.internal.http.HttpEngine$2
-com.android.okhttp.internal.http.HttpMethod
-com.android.okhttp.internal.http.HttpStream
-com.android.okhttp.internal.http.OkHeaders
-com.android.okhttp.internal.http.OkHeaders$1
-com.android.okhttp.internal.http.RealResponseBody
-com.android.okhttp.internal.http.RequestException
-com.android.okhttp.internal.http.RequestLine
-com.android.okhttp.internal.http.RetryableSink
-com.android.okhttp.internal.http.RouteException
-com.android.okhttp.internal.http.RouteSelector
-com.android.okhttp.internal.http.StatusLine
-com.android.okhttp.internal.http.StreamAllocation
-com.android.okhttp.internal.huc.DelegatingHttpsURLConnection
-com.android.okhttp.internal.huc.HttpURLConnectionImpl
-com.android.okhttp.internal.huc.HttpsURLConnectionImpl
-com.android.okhttp.internal.io.FileSystem
-com.android.okhttp.internal.io.FileSystem$1
-com.android.okhttp.internal.io.RealConnection
-com.android.okhttp.internal.tls.OkHostnameVerifier
-com.android.okhttp.okio.AsyncTimeout
-com.android.okhttp.okio.AsyncTimeout$1
-com.android.okhttp.okio.AsyncTimeout$2
-com.android.okhttp.okio.AsyncTimeout$Watchdog
-com.android.okhttp.okio.Base64
-com.android.okhttp.okio.Buffer
-com.android.okhttp.okio.BufferedSink
-com.android.okhttp.okio.BufferedSource
-com.android.okhttp.okio.ByteString
-com.android.okhttp.okio.ForwardingSink
-com.android.okhttp.okio.ForwardingTimeout
-com.android.okhttp.okio.GzipSource
-com.android.okhttp.okio.InflaterSource
-com.android.okhttp.okio.Okio
-com.android.okhttp.okio.Okio$1
-com.android.okhttp.okio.Okio$2
-com.android.okhttp.okio.Okio$3
-com.android.okhttp.okio.RealBufferedSink
-com.android.okhttp.okio.RealBufferedSink$1
-com.android.okhttp.okio.RealBufferedSource
-com.android.okhttp.okio.RealBufferedSource$1
-com.android.okhttp.okio.Segment
-com.android.okhttp.okio.SegmentPool
-com.android.okhttp.okio.Sink
-com.android.okhttp.okio.Source
-com.android.okhttp.okio.Timeout
-com.android.okhttp.okio.Timeout$1
-com.android.okhttp.okio.Util
-com.android.org.bouncycastle.asn1.ASN1BitString
-com.android.org.bouncycastle.asn1.ASN1Choice
-com.android.org.bouncycastle.asn1.ASN1Encodable
-com.android.org.bouncycastle.asn1.ASN1EncodableVector
-com.android.org.bouncycastle.asn1.ASN1InputStream
-com.android.org.bouncycastle.asn1.ASN1Integer
-com.android.org.bouncycastle.asn1.ASN1Null
-com.android.org.bouncycastle.asn1.ASN1Object
-com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier
-com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier$OidHandle
-com.android.org.bouncycastle.asn1.ASN1OutputStream
-com.android.org.bouncycastle.asn1.ASN1Primitive
-com.android.org.bouncycastle.asn1.ASN1Sequence
-com.android.org.bouncycastle.asn1.ASN1Set
-com.android.org.bouncycastle.asn1.ASN1StreamParser
-com.android.org.bouncycastle.asn1.ASN1String
-com.android.org.bouncycastle.asn1.ASN1TaggedObject
-com.android.org.bouncycastle.asn1.ASN1TaggedObjectParser
-com.android.org.bouncycastle.asn1.ASN1UTCTime
-com.android.org.bouncycastle.asn1.BERTags
-com.android.org.bouncycastle.asn1.DERBitString
-com.android.org.bouncycastle.asn1.DERFactory
-com.android.org.bouncycastle.asn1.DERNull
-com.android.org.bouncycastle.asn1.DEROutputStream
-com.android.org.bouncycastle.asn1.DERPrintableString
-com.android.org.bouncycastle.asn1.DERSequence
-com.android.org.bouncycastle.asn1.DERSet
-com.android.org.bouncycastle.asn1.DERTaggedObject
-com.android.org.bouncycastle.asn1.DLSequence
-com.android.org.bouncycastle.asn1.DLSet
-com.android.org.bouncycastle.asn1.DefiniteLengthInputStream
-com.android.org.bouncycastle.asn1.InMemoryRepresentable
-com.android.org.bouncycastle.asn1.IndefiniteLengthInputStream
-com.android.org.bouncycastle.asn1.LimitedInputStream
-com.android.org.bouncycastle.asn1.OIDTokenizer
-com.android.org.bouncycastle.asn1.StreamUtil
-com.android.org.bouncycastle.asn1.bc.BCObjectIdentifiers
-com.android.org.bouncycastle.asn1.iana.IANAObjectIdentifiers
-com.android.org.bouncycastle.asn1.misc.MiscObjectIdentifiers
-com.android.org.bouncycastle.asn1.nist.NISTObjectIdentifiers
-com.android.org.bouncycastle.asn1.oiw.OIWObjectIdentifiers
-com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers
-com.android.org.bouncycastle.asn1.x500.RDN
-com.android.org.bouncycastle.asn1.x500.X500Name
-com.android.org.bouncycastle.asn1.x500.X500NameStyle
-com.android.org.bouncycastle.asn1.x500.style.AbstractX500NameStyle
-com.android.org.bouncycastle.asn1.x500.style.BCStyle
-com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier
-com.android.org.bouncycastle.asn1.x509.Certificate
-com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
-com.android.org.bouncycastle.asn1.x509.TBSCertificate
-com.android.org.bouncycastle.asn1.x509.Time
-com.android.org.bouncycastle.asn1.x509.X509ObjectIdentifiers
-com.android.org.bouncycastle.asn1.x9.X9ObjectIdentifiers
-com.android.org.bouncycastle.crypto.AsymmetricBlockCipher
-com.android.org.bouncycastle.crypto.CipherKeyGenerator
-com.android.org.bouncycastle.crypto.CipherParameters
-com.android.org.bouncycastle.crypto.CryptoException
-com.android.org.bouncycastle.crypto.Digest
-com.android.org.bouncycastle.crypto.ExtendedDigest
-com.android.org.bouncycastle.crypto.InvalidCipherTextException
-com.android.org.bouncycastle.crypto.KeyGenerationParameters
-com.android.org.bouncycastle.crypto.Mac
-com.android.org.bouncycastle.crypto.PBEParametersGenerator
-com.android.org.bouncycastle.crypto.digests.AndroidDigestFactory
-com.android.org.bouncycastle.crypto.digests.AndroidDigestFactoryInterface
-com.android.org.bouncycastle.crypto.digests.AndroidDigestFactoryOpenSSL
-com.android.org.bouncycastle.crypto.digests.EncodableDigest
-com.android.org.bouncycastle.crypto.digests.GeneralDigest
-com.android.org.bouncycastle.crypto.digests.OpenSSLDigest
-com.android.org.bouncycastle.crypto.digests.OpenSSLDigest$SHA1
-com.android.org.bouncycastle.crypto.digests.SHA1Digest
-com.android.org.bouncycastle.crypto.encodings.OAEPEncoding
-com.android.org.bouncycastle.crypto.engines.RSABlindedEngine
-com.android.org.bouncycastle.crypto.engines.RSACoreEngine
-com.android.org.bouncycastle.crypto.generators.PKCS12ParametersGenerator
-com.android.org.bouncycastle.crypto.io.MacInputStream
-com.android.org.bouncycastle.crypto.macs.HMac
-com.android.org.bouncycastle.crypto.params.KeyParameter
-com.android.org.bouncycastle.jcajce.provider.asymmetric.DH$Mappings
-com.android.org.bouncycastle.jcajce.provider.asymmetric.DSA$Mappings
-com.android.org.bouncycastle.jcajce.provider.asymmetric.EC$Mappings
-com.android.org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings
-com.android.org.bouncycastle.jcajce.provider.asymmetric.X509$Mappings
-com.android.org.bouncycastle.jcajce.provider.asymmetric.dh.KeyFactorySpi
-com.android.org.bouncycastle.jcajce.provider.asymmetric.dsa.DSAUtil
-com.android.org.bouncycastle.jcajce.provider.asymmetric.dsa.KeyFactorySpi
-com.android.org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi
-com.android.org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi$EC
-com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi
-com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi$NoPadding
-com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyFactorySpi
-com.android.org.bouncycastle.jcajce.provider.asymmetric.util.BaseCipherSpi
-com.android.org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi
-com.android.org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl
-com.android.org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory
-com.android.org.bouncycastle.jcajce.provider.asymmetric.x509.PEMUtil
-com.android.org.bouncycastle.jcajce.provider.asymmetric.x509.X509CertificateObject
-com.android.org.bouncycastle.jcajce.provider.config.ConfigurableProvider
-com.android.org.bouncycastle.jcajce.provider.config.ProviderConfiguration
-com.android.org.bouncycastle.jcajce.provider.config.ProviderConfigurationPermission
-com.android.org.bouncycastle.jcajce.provider.digest.DigestAlgorithmProvider
-com.android.org.bouncycastle.jcajce.provider.digest.MD5
-com.android.org.bouncycastle.jcajce.provider.digest.MD5$Mappings
-com.android.org.bouncycastle.jcajce.provider.digest.SHA1
-com.android.org.bouncycastle.jcajce.provider.digest.SHA1$KeyGenerator
-com.android.org.bouncycastle.jcajce.provider.digest.SHA1$Mappings
-com.android.org.bouncycastle.jcajce.provider.digest.SHA224
-com.android.org.bouncycastle.jcajce.provider.digest.SHA224$Mappings
-com.android.org.bouncycastle.jcajce.provider.digest.SHA256
-com.android.org.bouncycastle.jcajce.provider.digest.SHA256$Mappings
-com.android.org.bouncycastle.jcajce.provider.digest.SHA384
-com.android.org.bouncycastle.jcajce.provider.digest.SHA384$Mappings
-com.android.org.bouncycastle.jcajce.provider.digest.SHA512
-com.android.org.bouncycastle.jcajce.provider.digest.SHA512$Mappings
-com.android.org.bouncycastle.jcajce.provider.keystore.BC$Mappings
-com.android.org.bouncycastle.jcajce.provider.keystore.PKCS12$Mappings
-com.android.org.bouncycastle.jcajce.provider.keystore.bc.BcKeyStoreSpi
-com.android.org.bouncycastle.jcajce.provider.keystore.bc.BcKeyStoreSpi$Std
-com.android.org.bouncycastle.jcajce.provider.keystore.bc.BcKeyStoreSpi$StoreEntry
-com.android.org.bouncycastle.jcajce.provider.symmetric.AES
-com.android.org.bouncycastle.jcajce.provider.symmetric.AES$Mappings
-com.android.org.bouncycastle.jcajce.provider.symmetric.ARC4
-com.android.org.bouncycastle.jcajce.provider.symmetric.ARC4$Mappings
-com.android.org.bouncycastle.jcajce.provider.symmetric.Blowfish
-com.android.org.bouncycastle.jcajce.provider.symmetric.Blowfish$Mappings
-com.android.org.bouncycastle.jcajce.provider.symmetric.DES
-com.android.org.bouncycastle.jcajce.provider.symmetric.DES$Mappings
-com.android.org.bouncycastle.jcajce.provider.symmetric.DESede
-com.android.org.bouncycastle.jcajce.provider.symmetric.DESede$Mappings
-com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2
-com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$Mappings
-com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPKCS12
-com.android.org.bouncycastle.jcajce.provider.symmetric.PBEPKCS12$Mappings
-com.android.org.bouncycastle.jcajce.provider.symmetric.PBES2AlgorithmParameters
-com.android.org.bouncycastle.jcajce.provider.symmetric.PBES2AlgorithmParameters$Mappings
-com.android.org.bouncycastle.jcajce.provider.symmetric.RC2
-com.android.org.bouncycastle.jcajce.provider.symmetric.RC2$Mappings
-com.android.org.bouncycastle.jcajce.provider.symmetric.SymmetricAlgorithmProvider
-com.android.org.bouncycastle.jcajce.provider.symmetric.Twofish
-com.android.org.bouncycastle.jcajce.provider.symmetric.Twofish$Mappings
-com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator
-com.android.org.bouncycastle.jcajce.provider.util.AlgorithmProvider
-com.android.org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider
-com.android.org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter
-com.android.org.bouncycastle.jcajce.provider.util.DigestFactory
-com.android.org.bouncycastle.jcajce.util.BCJcaJceHelper
-com.android.org.bouncycastle.jcajce.util.JcaJceHelper
-com.android.org.bouncycastle.jcajce.util.ProviderJcaJceHelper
-com.android.org.bouncycastle.jce.interfaces.BCKeyStore
-com.android.org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier
-com.android.org.bouncycastle.jce.provider.BouncyCastleProvider
-com.android.org.bouncycastle.jce.provider.BouncyCastleProvider$1
-com.android.org.bouncycastle.jce.provider.BouncyCastleProviderConfiguration
-com.android.org.bouncycastle.jce.provider.CertStoreCollectionSpi
-com.android.org.bouncycastle.util.Arrays
-com.android.org.bouncycastle.util.Encodable
-com.android.org.bouncycastle.util.Integers
-com.android.org.bouncycastle.util.Iterable
-com.android.org.bouncycastle.util.Memoable
-com.android.org.bouncycastle.util.Pack
-com.android.org.bouncycastle.util.Strings
-com.android.org.bouncycastle.util.Strings$1
-com.android.org.bouncycastle.util.io.Streams
-com.android.org.conscrypt.AbstractOpenSSLSession
-com.android.org.conscrypt.AbstractSessionContext
-com.android.org.conscrypt.AbstractSessionContext$1
-com.android.org.conscrypt.AddressUtils
-com.android.org.conscrypt.ArrayUtils
-com.android.org.conscrypt.ByteArray
-com.android.org.conscrypt.CertBlacklist
-com.android.org.conscrypt.CertificatePriorityComparator
-com.android.org.conscrypt.ChainStrengthAnalyzer
-com.android.org.conscrypt.ClientSessionContext
-com.android.org.conscrypt.ClientSessionContext$HostAndPort
-com.android.org.conscrypt.CryptoUpcalls
-com.android.org.conscrypt.EvpMdRef$MD5
-com.android.org.conscrypt.EvpMdRef$SHA1
-com.android.org.conscrypt.EvpMdRef$SHA256
-com.android.org.conscrypt.FileClientSessionCache
-com.android.org.conscrypt.FileClientSessionCache$CacheFile
-com.android.org.conscrypt.FileClientSessionCache$Impl
-com.android.org.conscrypt.Hex
-com.android.org.conscrypt.JSSEProvider
-com.android.org.conscrypt.KeyManagerFactoryImpl
-com.android.org.conscrypt.KeyManagerImpl
-com.android.org.conscrypt.NativeCrypto
-com.android.org.conscrypt.NativeCrypto$SSLHandshakeCallbacks
-com.android.org.conscrypt.NativeCryptoJni
-com.android.org.conscrypt.NativeRef
-com.android.org.conscrypt.NativeRef$EC_GROUP
-com.android.org.conscrypt.NativeRef$EC_POINT
-com.android.org.conscrypt.NativeRef$EVP_CIPHER_CTX
-com.android.org.conscrypt.NativeRef$EVP_MD_CTX
-com.android.org.conscrypt.NativeRef$EVP_PKEY
-com.android.org.conscrypt.NativeRef$HMAC_CTX
-com.android.org.conscrypt.OpenSSLBIOInputStream
-com.android.org.conscrypt.OpenSSLCipher
-com.android.org.conscrypt.OpenSSLCipher$EVP_CIPHER
-com.android.org.conscrypt.OpenSSLCipher$EVP_CIPHER$AES
-com.android.org.conscrypt.OpenSSLCipher$EVP_CIPHER$AES$CBC
-com.android.org.conscrypt.OpenSSLCipher$EVP_CIPHER$AES$CBC$PKCS5Padding
-com.android.org.conscrypt.OpenSSLCipher$EVP_CIPHER$AES_BASE
-com.android.org.conscrypt.OpenSSLCipher$Mode
-com.android.org.conscrypt.OpenSSLCipher$Padding
-com.android.org.conscrypt.OpenSSLContextImpl
-com.android.org.conscrypt.OpenSSLContextImpl$TLSv12
-com.android.org.conscrypt.OpenSSLECGroupContext
-com.android.org.conscrypt.OpenSSLECKeyFactory
-com.android.org.conscrypt.OpenSSLECPointContext
-com.android.org.conscrypt.OpenSSLECPublicKey
-com.android.org.conscrypt.OpenSSLExtendedSessionImpl
-com.android.org.conscrypt.OpenSSLKey
-com.android.org.conscrypt.OpenSSLKeyHolder
-com.android.org.conscrypt.OpenSSLMac
-com.android.org.conscrypt.OpenSSLMac$HmacSHA1
-com.android.org.conscrypt.OpenSSLMac$HmacSHA256
-com.android.org.conscrypt.OpenSSLMessageDigestJDK
-com.android.org.conscrypt.OpenSSLMessageDigestJDK$MD5
-com.android.org.conscrypt.OpenSSLMessageDigestJDK$SHA1
-com.android.org.conscrypt.OpenSSLMessageDigestJDK$SHA256
-com.android.org.conscrypt.OpenSSLProvider
-com.android.org.conscrypt.OpenSSLRSAKeyFactory
-com.android.org.conscrypt.OpenSSLRSAPublicKey
-com.android.org.conscrypt.OpenSSLRandom
-com.android.org.conscrypt.OpenSSLSessionImpl
-com.android.org.conscrypt.OpenSSLSignature
-com.android.org.conscrypt.OpenSSLSignature$EngineType
-com.android.org.conscrypt.OpenSSLSignature$RSAPKCS1Padding
-com.android.org.conscrypt.OpenSSLSignature$SHA1RSA
-com.android.org.conscrypt.OpenSSLSignature$SHA256RSA
-com.android.org.conscrypt.OpenSSLSocketFactoryImpl
-com.android.org.conscrypt.OpenSSLSocketImpl
-com.android.org.conscrypt.OpenSSLSocketImpl$SSLInputStream
-com.android.org.conscrypt.OpenSSLSocketImpl$SSLOutputStream
-com.android.org.conscrypt.OpenSSLSocketImplWrapper
-com.android.org.conscrypt.OpenSSLX509CertPath
-com.android.org.conscrypt.OpenSSLX509CertPath$Encoding
-com.android.org.conscrypt.OpenSSLX509Certificate
-com.android.org.conscrypt.OpenSSLX509CertificateFactory
-com.android.org.conscrypt.OpenSSLX509CertificateFactory$1
-com.android.org.conscrypt.OpenSSLX509CertificateFactory$2
-com.android.org.conscrypt.OpenSSLX509CertificateFactory$Parser
-com.android.org.conscrypt.OpenSSLX509CertificateFactory$ParsingException
-com.android.org.conscrypt.Platform
-com.android.org.conscrypt.SSLClientSessionCache
-com.android.org.conscrypt.SSLParametersImpl
-com.android.org.conscrypt.SSLParametersImpl$AliasChooser
-com.android.org.conscrypt.SSLParametersImpl$PSKCallbacks
-com.android.org.conscrypt.SSLUtils
-com.android.org.conscrypt.ServerSessionContext
-com.android.org.conscrypt.TrustManagerFactoryImpl
-com.android.org.conscrypt.TrustManagerImpl
-com.android.org.conscrypt.TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker
-com.android.org.conscrypt.TrustManagerImpl$TrustAnchorComparator
-com.android.org.conscrypt.TrustedCertificateIndex
-com.android.org.conscrypt.TrustedCertificateKeyStoreSpi
-com.android.org.conscrypt.TrustedCertificateStore
-com.android.org.conscrypt.TrustedCertificateStore$1
-com.android.org.conscrypt.TrustedCertificateStore$2
-com.android.org.conscrypt.TrustedCertificateStore$4
-com.android.org.conscrypt.TrustedCertificateStore$5
-com.android.org.conscrypt.TrustedCertificateStore$CertSelector
-com.android.org.conscrypt.TrustedCertificateStore$PreloadHolder
-com.android.org.conscrypt.ct.CTLogInfo
-com.android.org.conscrypt.ct.CTLogStore
-com.android.org.conscrypt.ct.CTLogStoreImpl
-com.android.org.conscrypt.ct.CTLogStoreImpl$InvalidLogFileException
-com.android.org.conscrypt.ct.CTPolicy
-com.android.org.conscrypt.ct.CTPolicyImpl
-com.android.org.conscrypt.ct.CTVerifier
-com.android.org.conscrypt.ct.KnownLogs
-com.android.org.conscrypt.ct.SerializationException
-com.android.server.AppWidgetBackupBridge
-com.android.server.BootReceiver
-com.android.server.BootReceiver$1
-com.android.server.BootReceiver$2
-com.android.server.LocalServices
-com.android.server.NetworkManagementSocketTagger
-com.android.server.NetworkManagementSocketTagger$1
-com.android.server.NetworkManagementSocketTagger$SocketTags
-com.android.server.SystemConfig
-com.android.server.SystemConfig$PermissionEntry
-com.android.server.WidgetBackupProvider
-com.android.server.backup.AccountSyncSettingsBackupHelper
-com.android.server.net.BaseNetworkObserver
-com.android.server.net.DnsServerEntry
-com.android.server.net.DnsServerRepository
-com.android.server.net.NetlinkTracker
-com.android.server.net.NetlinkTracker$Callback
-com.android.server.sip.SipService
-com.android.server.sip.SipService$ConnectivityReceiver
-com.android.server.sip.SipService$MyExecutor
-com.android.server.sip.SipWakeLock
-com.android.server.sip.SipWakeupTimer
-com.android.server.sip.SipWakeupTimer$MyEventComparator
-com.android.server.wifi.nano.WifiMetricsProto$AlertReasonCount
-com.android.server.wifi.nano.WifiMetricsProto$ConnectionEvent
-com.android.server.wifi.nano.WifiMetricsProto$RouterFingerPrint
-com.android.server.wifi.nano.WifiMetricsProto$RssiPollCount
-com.android.server.wifi.nano.WifiMetricsProto$SoftApDurationBucket
-com.android.server.wifi.nano.WifiMetricsProto$SoftApReturnCodeCount
-com.android.server.wifi.nano.WifiMetricsProto$StaEvent
-com.android.server.wifi.nano.WifiMetricsProto$StaEvent$ConfigInfo
-com.android.server.wifi.nano.WifiMetricsProto$WifiLog
-com.android.server.wifi.nano.WifiMetricsProto$WifiLog$ScanReturnEntry
-com.android.server.wifi.nano.WifiMetricsProto$WifiLog$WifiSystemStateEntry
-com.android.server.wifi.nano.WifiMetricsProto$WifiScoreCount
-com.android.server.wm.nano.WindowManagerProtos$TaskSnapshotProto
-com.google.android.collect.Lists
-com.google.android.collect.Maps
-com.google.android.collect.Sets
-com.google.android.gles_jni.EGLConfigImpl
-com.google.android.gles_jni.EGLContextImpl
-com.google.android.gles_jni.EGLDisplayImpl
-com.google.android.gles_jni.EGLImpl
-com.google.android.gles_jni.EGLSurfaceImpl
-com.google.android.gles_jni.GLImpl
-com.google.android.mms.MmsException
-dalvik.annotation.optimization.CriticalNative
-dalvik.annotation.optimization.FastNative
-dalvik.system.-$Lambda$xxvwQBVHC44UYbpcpA8j0sUqLOo
-dalvik.system.BaseDexClassLoader
-dalvik.system.BaseDexClassLoader$Reporter
-dalvik.system.BlockGuard
-dalvik.system.BlockGuard$1
-dalvik.system.BlockGuard$2
-dalvik.system.BlockGuard$BlockGuardPolicyException
-dalvik.system.BlockGuard$Policy
-dalvik.system.ClassExt
-dalvik.system.CloseGuard
-dalvik.system.CloseGuard$DefaultReporter
-dalvik.system.CloseGuard$DefaultTracker
-dalvik.system.CloseGuard$Reporter
-dalvik.system.CloseGuard$Tracker
-dalvik.system.DalvikLogHandler
-dalvik.system.DalvikLogging
-dalvik.system.DexClassLoader
-dalvik.system.DexFile
-dalvik.system.DexFile$DFEnum
-dalvik.system.DexPathList
-dalvik.system.DexPathList$Element
-dalvik.system.DexPathList$NativeLibraryElement
-dalvik.system.EmulatedStackFrame
-dalvik.system.EmulatedStackFrame$Range
-dalvik.system.PathClassLoader
-dalvik.system.SocketTagger
-dalvik.system.SocketTagger$1
-dalvik.system.VMDebug
-dalvik.system.VMRuntime
-dalvik.system.VMStack
-dalvik.system.ZygoteHooks
-java.io.Bits
-java.io.BufferedInputStream
-java.io.BufferedOutputStream
-java.io.BufferedReader
-java.io.BufferedWriter
-java.io.ByteArrayInputStream
-java.io.ByteArrayOutputStream
-java.io.CharArrayWriter
-java.io.Closeable
-java.io.Console
-java.io.DataInput
-java.io.DataInputStream
-java.io.DataOutput
-java.io.DataOutputStream
-java.io.DefaultFileSystem
-java.io.EOFException
-java.io.ExpiringCache
-java.io.ExpiringCache$1
-java.io.ExpiringCache$Entry
-java.io.Externalizable
-java.io.File
-java.io.File$PathStatus
-java.io.File$TempDirectory
-java.io.FileDescriptor
-java.io.FileDescriptor$1
-java.io.FileFilter
-java.io.FileInputStream
-java.io.FileInputStream$UseManualSkipException
-java.io.FileNotFoundException
-java.io.FileOutputStream
-java.io.FileReader
-java.io.FileSystem
-java.io.FileWriter
-java.io.FilenameFilter
-java.io.FilterInputStream
-java.io.FilterOutputStream
-java.io.FilterReader
-java.io.Flushable
-java.io.IOException
-java.io.InputStream
-java.io.InputStreamReader
-java.io.InterruptedIOException
-java.io.InvalidClassException
-java.io.InvalidObjectException
-java.io.ObjectInput
-java.io.ObjectInputStream
-java.io.ObjectInputStream$BlockDataInputStream
-java.io.ObjectInputStream$HandleTable
-java.io.ObjectInputStream$HandleTable$HandleList
-java.io.ObjectInputStream$PeekInputStream
-java.io.ObjectInputStream$ValidationList
-java.io.ObjectOutput
-java.io.ObjectOutputStream
-java.io.ObjectOutputStream$BlockDataOutputStream
-java.io.ObjectOutputStream$HandleTable
-java.io.ObjectOutputStream$PutField
-java.io.ObjectOutputStream$ReplaceTable
-java.io.ObjectStreamClass
-java.io.ObjectStreamClass$1
-java.io.ObjectStreamClass$2
-java.io.ObjectStreamClass$3
-java.io.ObjectStreamClass$4
-java.io.ObjectStreamClass$5
-java.io.ObjectStreamClass$Caches
-java.io.ObjectStreamClass$ClassDataSlot
-java.io.ObjectStreamClass$EntryFuture
-java.io.ObjectStreamClass$ExceptionInfo
-java.io.ObjectStreamClass$FieldReflector
-java.io.ObjectStreamClass$FieldReflectorKey
-java.io.ObjectStreamClass$MemberSignature
-java.io.ObjectStreamClass$WeakClassKey
-java.io.ObjectStreamConstants
-java.io.ObjectStreamException
-java.io.ObjectStreamField
-java.io.OutputStream
-java.io.OutputStreamWriter
-java.io.PrintStream
-java.io.PrintWriter
-java.io.PushbackInputStream
-java.io.PushbackReader
-java.io.RandomAccessFile
-java.io.Reader
-java.io.SequenceInputStream
-java.io.SerialCallbackContext
-java.io.Serializable
-java.io.SerializablePermission
-java.io.StreamCorruptedException
-java.io.StringReader
-java.io.StringWriter
-java.io.UnixFileSystem
-java.io.UnsupportedEncodingException
-java.io.Writer
-java.lang.-$Lambda$S9HjrJh0nDg7IyU6wZdPArnZWRQ
-java.lang.-$Lambda$S9HjrJh0nDg7IyU6wZdPArnZWRQ$1
-java.lang.AbstractMethodError
-java.lang.AbstractStringBuilder
-java.lang.AndroidHardcodedSystemProperties
-java.lang.Appendable
-java.lang.ArithmeticException
-java.lang.ArrayIndexOutOfBoundsException
-java.lang.ArrayStoreException
-java.lang.AssertionError
-java.lang.AutoCloseable
-java.lang.Boolean
-java.lang.BootClassLoader
-java.lang.Byte
-java.lang.Byte$ByteCache
-java.lang.CaseMapper
-java.lang.CaseMapper$1
-java.lang.CharSequence
-java.lang.CharSequence$1CharIterator
-java.lang.CharSequence$1CodePointIterator
-java.lang.Character
-java.lang.Character$CharacterCache
-java.lang.Character$Subset
-java.lang.Character$UnicodeBlock
-java.lang.Class
-java.lang.Class$Caches
-java.lang.ClassCastException
-java.lang.ClassLoader
-java.lang.ClassLoader$SystemClassLoader
-java.lang.ClassNotFoundException
-java.lang.CloneNotSupportedException
-java.lang.Cloneable
-java.lang.Comparable
-java.lang.Daemons
-java.lang.Daemons$Daemon
-java.lang.Daemons$FinalizerDaemon
-java.lang.Daemons$FinalizerWatchdogDaemon
-java.lang.Daemons$HeapTaskDaemon
-java.lang.Daemons$ReferenceQueueDaemon
-java.lang.Deprecated
-java.lang.DexCache
-java.lang.Double
-java.lang.Enum
-java.lang.Enum$1
-java.lang.EnumConstantNotPresentException
-java.lang.Error
-java.lang.Exception
-java.lang.ExceptionInInitializerError
-java.lang.Float
-java.lang.IllegalAccessError
-java.lang.IllegalAccessException
-java.lang.IllegalArgumentException
-java.lang.IllegalMonitorStateException
-java.lang.IllegalStateException
-java.lang.IllegalThreadStateException
-java.lang.IncompatibleClassChangeError
-java.lang.IndexOutOfBoundsException
-java.lang.InheritableThreadLocal
-java.lang.InstantiationError
-java.lang.InstantiationException
-java.lang.Integer
-java.lang.Integer$IntegerCache
-java.lang.InternalError
-java.lang.InterruptedException
-java.lang.Iterable
-java.lang.JavaLangAccess
-java.lang.LinkageError
-java.lang.Long
-java.lang.Long$LongCache
-java.lang.Math
-java.lang.Math$RandomNumberGeneratorHolder
-java.lang.NegativeArraySizeException
-java.lang.NoClassDefFoundError
-java.lang.NoSuchFieldError
-java.lang.NoSuchFieldException
-java.lang.NoSuchMethodError
-java.lang.NoSuchMethodException
-java.lang.NullPointerException
-java.lang.Number
-java.lang.NumberFormatException
-java.lang.Object
-java.lang.OutOfMemoryError
-java.lang.Package
-java.lang.Process
-java.lang.ProcessBuilder
-java.lang.ProcessEnvironment
-java.lang.Readable
-java.lang.ReflectiveOperationException
-java.lang.Runnable
-java.lang.Runtime
-java.lang.RuntimeException
-java.lang.RuntimePermission
-java.lang.SecurityException
-java.lang.SecurityManager
-java.lang.Short
-java.lang.Short$ShortCache
-java.lang.StackOverflowError
-java.lang.StackTraceElement
-java.lang.StrictMath
-java.lang.String
-java.lang.String$CaseInsensitiveComparator
-java.lang.StringBuffer
-java.lang.StringBuilder
-java.lang.StringFactory
-java.lang.StringIndexOutOfBoundsException
-java.lang.System
-java.lang.System$PropertiesWithNonOverrideableDefaults
-java.lang.Thread
-java.lang.Thread$1
-java.lang.Thread$Caches
-java.lang.Thread$State
-java.lang.Thread$UncaughtExceptionHandler
-java.lang.Thread$WeakClassKey
-java.lang.ThreadDeath
-java.lang.ThreadGroup
-java.lang.ThreadLocal
-java.lang.ThreadLocal$SuppliedThreadLocal
-java.lang.ThreadLocal$ThreadLocalMap
-java.lang.ThreadLocal$ThreadLocalMap$Entry
-java.lang.Throwable
-java.lang.Throwable$PrintStreamOrWriter
-java.lang.Throwable$SentinelHolder
-java.lang.Throwable$WrappedPrintStream
-java.lang.Throwable$WrappedPrintWriter
-java.lang.TypeNotPresentException
-java.lang.UNIXProcess
-java.lang.UnsatisfiedLinkError
-java.lang.UnsupportedOperationException
-java.lang.VMClassLoader
-java.lang.VerifyError
-java.lang.VirtualMachineError
-java.lang.Void
-java.lang.annotation.Annotation
-java.lang.annotation.AnnotationTypeMismatchException
-java.lang.annotation.IncompleteAnnotationException
-java.lang.annotation.Inherited
-java.lang.annotation.Retention
-java.lang.annotation.Target
-java.lang.invoke.CallSite
-java.lang.invoke.ConstantCallSite
-java.lang.invoke.MethodHandle
-java.lang.invoke.MethodHandleImpl
-java.lang.invoke.MethodHandleImpl$HandleInfo
-java.lang.invoke.MethodHandleInfo
-java.lang.invoke.MethodHandleStatics
-java.lang.invoke.MethodHandles
-java.lang.invoke.MethodHandles$Lookup
-java.lang.invoke.MethodType
-java.lang.invoke.MethodType$ConcurrentWeakInternSet
-java.lang.invoke.MethodType$ConcurrentWeakInternSet$WeakEntry
-java.lang.invoke.MethodTypeForm
-java.lang.invoke.Transformers$BindTo
-java.lang.invoke.Transformers$Collector
-java.lang.invoke.Transformers$Construct
-java.lang.invoke.Transformers$Spreader
-java.lang.invoke.Transformers$Transformer
-java.lang.invoke.Transformers$VarargsCollector
-java.lang.invoke.WrongMethodTypeException
-java.lang.ref.FinalizerReference
-java.lang.ref.FinalizerReference$Sentinel
-java.lang.ref.PhantomReference
-java.lang.ref.Reference
-java.lang.ref.ReferenceQueue
-java.lang.ref.SoftReference
-java.lang.ref.WeakReference
-java.lang.reflect.AccessibleObject
-java.lang.reflect.AnnotatedElement
-java.lang.reflect.Array
-java.lang.reflect.Constructor
-java.lang.reflect.Executable
-java.lang.reflect.Executable$GenericInfo
-java.lang.reflect.Field
-java.lang.reflect.GenericArrayType
-java.lang.reflect.GenericDeclaration
-java.lang.reflect.InvocationHandler
-java.lang.reflect.InvocationTargetException
-java.lang.reflect.MalformedParametersException
-java.lang.reflect.Member
-java.lang.reflect.Method
-java.lang.reflect.Method$1
-java.lang.reflect.Modifier
-java.lang.reflect.Parameter
-java.lang.reflect.ParameterizedType
-java.lang.reflect.Proxy
-java.lang.reflect.Proxy$1
-java.lang.reflect.Proxy$Key1
-java.lang.reflect.Proxy$Key2
-java.lang.reflect.Proxy$KeyFactory
-java.lang.reflect.Proxy$KeyX
-java.lang.reflect.Proxy$ProxyClassFactory
-java.lang.reflect.Type
-java.lang.reflect.TypeVariable
-java.lang.reflect.UndeclaredThrowableException
-java.lang.reflect.WeakCache
-java.lang.reflect.WeakCache$CacheKey
-java.lang.reflect.WeakCache$CacheValue
-java.lang.reflect.WeakCache$Factory
-java.lang.reflect.WeakCache$LookupValue
-java.lang.reflect.WeakCache$Value
-java.lang.reflect.WildcardType
-java.math.BigDecimal
-java.math.BigInt
-java.math.BigInteger
-java.math.Conversion
-java.math.Division
-java.math.MathContext
-java.math.Multiplication
-java.math.NativeBN
-java.math.RoundingMode
-java.net.AbstractPlainDatagramSocketImpl
-java.net.AbstractPlainSocketImpl
-java.net.AddressCache
-java.net.AddressCache$AddressCacheEntry
-java.net.AddressCache$AddressCacheKey
-java.net.ConnectException
-java.net.CookieHandler
-java.net.CookieManager
-java.net.CookiePolicy
-java.net.CookiePolicy$1
-java.net.CookiePolicy$2
-java.net.CookiePolicy$3
-java.net.CookieStore
-java.net.DatagramPacket
-java.net.DatagramSocket
-java.net.DatagramSocket$1
-java.net.DatagramSocketImpl
-java.net.DefaultDatagramSocketImplFactory
-java.net.DefaultFileNameMap
-java.net.DefaultInterface
-java.net.FileNameMap
-java.net.HttpURLConnection
-java.net.IDN
-java.net.InMemoryCookieStore
-java.net.Inet4Address
-java.net.Inet6Address
-java.net.Inet6Address$Inet6AddressHolder
-java.net.Inet6AddressImpl
-java.net.InetAddress
-java.net.InetAddress$1
-java.net.InetAddress$InetAddressHolder
-java.net.InetAddressImpl
-java.net.InetSocketAddress
-java.net.InetSocketAddress$InetSocketAddressHolder
-java.net.InterfaceAddress
-java.net.JarURLConnection
-java.net.MalformedURLException
-java.net.MulticastSocket
-java.net.NetworkInterface
-java.net.NetworkInterface$1checkedAddresses
-java.net.NoRouteToHostException
-java.net.Parts
-java.net.PlainDatagramSocketImpl
-java.net.PlainSocketImpl
-java.net.PortUnreachableException
-java.net.ProtocolException
-java.net.ProtocolFamily
-java.net.Proxy
-java.net.Proxy$Type
-java.net.ProxySelector
-java.net.ResponseCache
-java.net.ServerSocket
-java.net.Socket
-java.net.Socket$1
-java.net.Socket$2
-java.net.Socket$3
-java.net.SocketAddress
-java.net.SocketException
-java.net.SocketImpl
-java.net.SocketInputStream
-java.net.SocketOptions
-java.net.SocketOutputStream
-java.net.SocketTimeoutException
-java.net.SocksConsts
-java.net.SocksSocketImpl
-java.net.StandardProtocolFamily
-java.net.URI
-java.net.URI$Parser
-java.net.URISyntaxException
-java.net.URL
-java.net.URLConnection
-java.net.URLDecoder
-java.net.URLEncoder
-java.net.URLStreamHandler
-java.net.URLStreamHandlerFactory
-java.net.UnknownHostException
-java.net.UnknownServiceException
-java.nio.Bits
-java.nio.Buffer
-java.nio.BufferOverflowException
-java.nio.BufferUnderflowException
-java.nio.ByteBuffer
-java.nio.ByteBufferAsCharBuffer
-java.nio.ByteBufferAsDoubleBuffer
-java.nio.ByteBufferAsFloatBuffer
-java.nio.ByteBufferAsIntBuffer
-java.nio.ByteBufferAsLongBuffer
-java.nio.ByteBufferAsShortBuffer
-java.nio.ByteOrder
-java.nio.CharBuffer
-java.nio.DirectByteBuffer
-java.nio.DirectByteBuffer$MemoryRef
-java.nio.DoubleBuffer
-java.nio.FloatBuffer
-java.nio.HeapByteBuffer
-java.nio.HeapCharBuffer
-java.nio.IntBuffer
-java.nio.InvalidMarkException
-java.nio.LongBuffer
-java.nio.MappedByteBuffer
-java.nio.NIOAccess
-java.nio.NioUtils
-java.nio.ReadOnlyBufferException
-java.nio.ShortBuffer
-java.nio.StringCharBuffer
-java.nio.channels.AsynchronousCloseException
-java.nio.channels.ByteChannel
-java.nio.channels.CancelledKeyException
-java.nio.channels.Channel
-java.nio.channels.Channels
-java.nio.channels.Channels$1
-java.nio.channels.ClosedByInterruptException
-java.nio.channels.ClosedChannelException
-java.nio.channels.DatagramChannel
-java.nio.channels.FileChannel
-java.nio.channels.FileChannel$MapMode
-java.nio.channels.FileLock
-java.nio.channels.GatheringByteChannel
-java.nio.channels.InterruptibleChannel
-java.nio.channels.MulticastChannel
-java.nio.channels.NetworkChannel
-java.nio.channels.NonWritableChannelException
-java.nio.channels.OverlappingFileLockException
-java.nio.channels.ReadableByteChannel
-java.nio.channels.ScatteringByteChannel
-java.nio.channels.SeekableByteChannel
-java.nio.channels.SelectableChannel
-java.nio.channels.SelectionKey
-java.nio.channels.Selector
-java.nio.channels.ServerSocketChannel
-java.nio.channels.SocketChannel
-java.nio.channels.WritableByteChannel
-java.nio.channels.spi.AbstractInterruptibleChannel
-java.nio.channels.spi.AbstractInterruptibleChannel$1
-java.nio.channels.spi.AbstractSelectableChannel
-java.nio.channels.spi.AbstractSelectionKey
-java.nio.channels.spi.AbstractSelector
-java.nio.channels.spi.AbstractSelector$1
-java.nio.channels.spi.SelectorProvider
-java.nio.channels.spi.SelectorProvider$1
-java.nio.charset.CharacterCodingException
-java.nio.charset.Charset
-java.nio.charset.CharsetDecoder
-java.nio.charset.CharsetDecoderICU
-java.nio.charset.CharsetEncoder
-java.nio.charset.CharsetEncoderICU
-java.nio.charset.CharsetICU
-java.nio.charset.CoderResult
-java.nio.charset.CoderResult$1
-java.nio.charset.CoderResult$2
-java.nio.charset.CoderResult$Cache
-java.nio.charset.CodingErrorAction
-java.nio.charset.IllegalCharsetNameException
-java.nio.charset.StandardCharsets
-java.nio.charset.UnsupportedCharsetException
-java.nio.file.AccessMode
-java.nio.file.CopyOption
-java.nio.file.FileAlreadyExistsException
-java.nio.file.FileSystem
-java.nio.file.FileSystemException
-java.nio.file.FileSystems
-java.nio.file.FileSystems$DefaultFileSystemHolder
-java.nio.file.FileSystems$DefaultFileSystemHolder$1
-java.nio.file.Files
-java.nio.file.LinkOption
-java.nio.file.NoSuchFileException
-java.nio.file.OpenOption
-java.nio.file.Path
-java.nio.file.Paths
-java.nio.file.StandardOpenOption
-java.nio.file.Watchable
-java.nio.file.attribute.AttributeView
-java.nio.file.attribute.BasicFileAttributeView
-java.nio.file.attribute.BasicFileAttributes
-java.nio.file.attribute.FileAttribute
-java.nio.file.attribute.FileAttributeView
-java.nio.file.attribute.PosixFileAttributes
-java.nio.file.spi.FileSystemProvider
-java.security.AccessControlContext
-java.security.AccessControlException
-java.security.AccessController
-java.security.AlgorithmConstraints
-java.security.AlgorithmParameters
-java.security.AlgorithmParametersSpi
-java.security.BasicPermission
-java.security.CodeSigner
-java.security.CryptoPrimitive
-java.security.DigestException
-java.security.GeneralSecurityException
-java.security.Guard
-java.security.InvalidAlgorithmParameterException
-java.security.InvalidKeyException
-java.security.InvalidParameterException
-java.security.Key
-java.security.KeyException
-java.security.KeyFactory
-java.security.KeyFactorySpi
-java.security.KeyManagementException
-java.security.KeyPair
-java.security.KeyPairGenerator
-java.security.KeyPairGeneratorSpi
-java.security.KeyStore
-java.security.KeyStore$1
-java.security.KeyStoreException
-java.security.KeyStoreSpi
-java.security.MessageDigest
-java.security.MessageDigest$Delegate
-java.security.MessageDigestSpi
-java.security.NoSuchAlgorithmException
-java.security.NoSuchProviderException
-java.security.Permission
-java.security.PermissionCollection
-java.security.Permissions
-java.security.Principal
-java.security.PrivateKey
-java.security.PrivilegedAction
-java.security.PrivilegedActionException
-java.security.PrivilegedExceptionAction
-java.security.ProtectionDomain
-java.security.Provider
-java.security.Provider$EngineDescription
-java.security.Provider$Service
-java.security.Provider$ServiceKey
-java.security.Provider$UString
-java.security.PublicKey
-java.security.SecureRandom
-java.security.SecureRandomSpi
-java.security.Security
-java.security.Signature
-java.security.Signature$Delegate
-java.security.SignatureException
-java.security.SignatureSpi
-java.security.UnrecoverableEntryException
-java.security.UnrecoverableKeyException
-java.security.cert.CRL
-java.security.cert.CRLException
-java.security.cert.CRLReason
-java.security.cert.CertPath
-java.security.cert.CertPathBuilderException
-java.security.cert.CertPathChecker
-java.security.cert.CertPathHelperImpl
-java.security.cert.CertPathParameters
-java.security.cert.CertPathValidator
-java.security.cert.CertPathValidatorException
-java.security.cert.CertPathValidatorResult
-java.security.cert.CertPathValidatorSpi
-java.security.cert.CertSelector
-java.security.cert.CertStore
-java.security.cert.CertStoreException
-java.security.cert.CertStoreParameters
-java.security.cert.CertStoreSpi
-java.security.cert.Certificate
-java.security.cert.CertificateEncodingException
-java.security.cert.CertificateException
-java.security.cert.CertificateExpiredException
-java.security.cert.CertificateFactory
-java.security.cert.CertificateFactorySpi
-java.security.cert.CertificateNotYetValidException
-java.security.cert.CertificateParsingException
-java.security.cert.CollectionCertStoreParameters
-java.security.cert.Extension
-java.security.cert.PKIXCertPathChecker
-java.security.cert.PKIXCertPathValidatorResult
-java.security.cert.PKIXParameters
-java.security.cert.PKIXRevocationChecker
-java.security.cert.PKIXRevocationChecker$Option
-java.security.cert.PolicyNode
-java.security.cert.PolicyQualifierInfo
-java.security.cert.TrustAnchor
-java.security.cert.X509CertSelector
-java.security.cert.X509Certificate
-java.security.cert.X509Extension
-java.security.interfaces.DSAKey
-java.security.interfaces.DSAPublicKey
-java.security.interfaces.ECKey
-java.security.interfaces.ECPrivateKey
-java.security.interfaces.ECPublicKey
-java.security.interfaces.RSAKey
-java.security.interfaces.RSAPrivateKey
-java.security.interfaces.RSAPublicKey
-java.security.spec.AlgorithmParameterSpec
-java.security.spec.ECField
-java.security.spec.ECFieldFp
-java.security.spec.ECGenParameterSpec
-java.security.spec.ECParameterSpec
-java.security.spec.ECPoint
-java.security.spec.ECPrivateKeySpec
-java.security.spec.ECPublicKeySpec
-java.security.spec.EllipticCurve
-java.security.spec.EncodedKeySpec
-java.security.spec.InvalidKeySpecException
-java.security.spec.InvalidParameterSpecException
-java.security.spec.KeySpec
-java.security.spec.MGF1ParameterSpec
-java.security.spec.PKCS8EncodedKeySpec
-java.security.spec.RSAPrivateCrtKeySpec
-java.security.spec.RSAPrivateKeySpec
-java.security.spec.RSAPublicKeySpec
-java.security.spec.X509EncodedKeySpec
-java.sql.Timestamp
-java.text.AttributedCharacterIterator$Attribute
-java.text.Bidi
-java.text.BreakIterator
-java.text.CalendarBuilder
-java.text.CharacterIterator
-java.text.CollationKey
-java.text.Collator
-java.text.DateFormat
-java.text.DateFormat$Field
-java.text.DateFormatSymbols
-java.text.DecimalFormat
-java.text.DecimalFormatSymbols
-java.text.DontCareFieldPosition
-java.text.DontCareFieldPosition$1
-java.text.FieldPosition
-java.text.FieldPosition$Delegate
-java.text.Format
-java.text.Format$Field
-java.text.Format$FieldDelegate
-java.text.IcuIteratorWrapper
-java.text.MessageFormat
-java.text.MessageFormat$Field
-java.text.Normalizer
-java.text.Normalizer$Form
-java.text.NumberFormat
-java.text.ParseException
-java.text.ParsePosition
-java.text.RuleBasedCollator
-java.text.SimpleDateFormat
-java.text.StringCharacterIterator
-java.time.DateTimeException
-java.util.-$Lambda$4EqhxufgNKat19m0CB0-toH_lzo
-java.util.-$Lambda$4EqhxufgNKat19m0CB0-toH_lzo$1
-java.util.-$Lambda$4EqhxufgNKat19m0CB0-toH_lzo$2
-java.util.-$Lambda$4EqhxufgNKat19m0CB0-toH_lzo$3
-java.util.-$Lambda$4EqhxufgNKat19m0CB0-toH_lzo$4
-java.util.-$Lambda$4EqhxufgNKat19m0CB0-toH_lzo$5
-java.util.-$Lambda$aUGKT4ItCOku5-JSG-x8Aqj2pJw
-java.util.-$Lambda$aUGKT4ItCOku5-JSG-x8Aqj2pJw$1
-java.util.-$Lambda$aUGKT4ItCOku5-JSG-x8Aqj2pJw$2
-java.util.-$Lambda$aUGKT4ItCOku5-JSG-x8Aqj2pJw$3
-java.util.AbstractCollection
-java.util.AbstractList
-java.util.AbstractList$Itr
-java.util.AbstractList$ListItr
-java.util.AbstractMap
-java.util.AbstractMap$1
-java.util.AbstractMap$2
-java.util.AbstractMap$2$1
-java.util.AbstractMap$SimpleEntry
-java.util.AbstractMap$SimpleImmutableEntry
-java.util.AbstractQueue
-java.util.AbstractSequentialList
-java.util.AbstractSet
-java.util.ArrayDeque
-java.util.ArrayDeque$DeqIterator
-java.util.ArrayDeque$DescendingIterator
-java.util.ArrayList
-java.util.ArrayList$ArrayListSpliterator
-java.util.ArrayList$Itr
-java.util.ArrayList$ListItr
-java.util.ArrayList$SubList
-java.util.ArrayList$SubList$1
-java.util.ArrayPrefixHelpers$CumulateTask
-java.util.ArrayPrefixHelpers$DoubleCumulateTask
-java.util.ArrayPrefixHelpers$IntCumulateTask
-java.util.ArrayPrefixHelpers$LongCumulateTask
-java.util.Arrays
-java.util.Arrays$ArrayList
-java.util.Arrays$NaturalOrder
-java.util.ArraysParallelSortHelpers$FJByte$Sorter
-java.util.ArraysParallelSortHelpers$FJChar$Sorter
-java.util.ArraysParallelSortHelpers$FJDouble$Sorter
-java.util.ArraysParallelSortHelpers$FJFloat$Sorter
-java.util.ArraysParallelSortHelpers$FJInt$Sorter
-java.util.ArraysParallelSortHelpers$FJLong$Sorter
-java.util.ArraysParallelSortHelpers$FJObject$Sorter
-java.util.ArraysParallelSortHelpers$FJShort$Sorter
-java.util.Base64
-java.util.Base64$Decoder
-java.util.Base64$Encoder
-java.util.BitSet
-java.util.Calendar
-java.util.Collection
-java.util.Collections
-java.util.Collections$1
-java.util.Collections$2
-java.util.Collections$3
-java.util.Collections$AsLIFOQueue
-java.util.Collections$CheckedCollection
-java.util.Collections$CheckedList
-java.util.Collections$CheckedMap
-java.util.Collections$CheckedNavigableMap
-java.util.Collections$CheckedNavigableSet
-java.util.Collections$CheckedQueue
-java.util.Collections$CheckedRandomAccessList
-java.util.Collections$CheckedSet
-java.util.Collections$CheckedSortedMap
-java.util.Collections$CheckedSortedSet
-java.util.Collections$CopiesList
-java.util.Collections$EmptyEnumeration
-java.util.Collections$EmptyIterator
-java.util.Collections$EmptyList
-java.util.Collections$EmptyListIterator
-java.util.Collections$EmptyMap
-java.util.Collections$EmptySet
-java.util.Collections$ReverseComparator
-java.util.Collections$ReverseComparator2
-java.util.Collections$SetFromMap
-java.util.Collections$SingletonList
-java.util.Collections$SingletonMap
-java.util.Collections$SingletonSet
-java.util.Collections$SynchronizedCollection
-java.util.Collections$SynchronizedList
-java.util.Collections$SynchronizedMap
-java.util.Collections$SynchronizedNavigableMap
-java.util.Collections$SynchronizedNavigableSet
-java.util.Collections$SynchronizedRandomAccessList
-java.util.Collections$SynchronizedSet
-java.util.Collections$SynchronizedSortedMap
-java.util.Collections$SynchronizedSortedSet
-java.util.Collections$UnmodifiableCollection
-java.util.Collections$UnmodifiableCollection$1
-java.util.Collections$UnmodifiableList
-java.util.Collections$UnmodifiableList$1
-java.util.Collections$UnmodifiableMap
-java.util.Collections$UnmodifiableMap$UnmodifiableEntrySet
-java.util.Collections$UnmodifiableMap$UnmodifiableEntrySet$1
-java.util.Collections$UnmodifiableMap$UnmodifiableEntrySet$UnmodifiableEntry
-java.util.Collections$UnmodifiableNavigableMap
-java.util.Collections$UnmodifiableNavigableMap$EmptyNavigableMap
-java.util.Collections$UnmodifiableNavigableSet
-java.util.Collections$UnmodifiableNavigableSet$EmptyNavigableSet
-java.util.Collections$UnmodifiableRandomAccessList
-java.util.Collections$UnmodifiableSet
-java.util.Collections$UnmodifiableSortedMap
-java.util.Collections$UnmodifiableSortedSet
-java.util.ComparableTimSort
-java.util.Comparator
-java.util.Comparators$NaturalOrderComparator
-java.util.Comparators$NullComparator
-java.util.ConcurrentModificationException
-java.util.Currency
-java.util.Date
-java.util.Deque
-java.util.Dictionary
-java.util.DualPivotQuicksort
-java.util.EmptyStackException
-java.util.EnumMap
-java.util.EnumMap$1
-java.util.EnumMap$EntryIterator
-java.util.EnumMap$EntryIterator$Entry
-java.util.EnumMap$EntrySet
-java.util.EnumMap$EnumMapIterator
-java.util.EnumMap$KeyIterator
-java.util.EnumMap$KeySet
-java.util.EnumMap$ValueIterator
-java.util.EnumMap$Values
-java.util.EnumSet
-java.util.EnumSet$SerializationProxy
-java.util.Enumeration
-java.util.EventListener
-java.util.FormatFlagsConversionMismatchException
-java.util.Formattable
-java.util.Formatter
-java.util.Formatter$Conversion
-java.util.Formatter$DateTime
-java.util.Formatter$FixedString
-java.util.Formatter$Flags
-java.util.Formatter$FormatSpecifier
-java.util.Formatter$FormatSpecifierParser
-java.util.Formatter$FormatString
-java.util.FormatterClosedException
-java.util.GregorianCalendar
-java.util.HashMap
-java.util.HashMap$EntryIterator
-java.util.HashMap$EntrySet
-java.util.HashMap$HashIterator
-java.util.HashMap$KeyIterator
-java.util.HashMap$KeySet
-java.util.HashMap$Node
-java.util.HashMap$TreeNode
-java.util.HashMap$ValueIterator
-java.util.HashMap$Values
-java.util.HashSet
-java.util.Hashtable
-java.util.Hashtable$EntrySet
-java.util.Hashtable$Enumerator
-java.util.Hashtable$HashtableEntry
-java.util.Hashtable$KeySet
-java.util.Hashtable$ValueCollection
-java.util.IdentityHashMap
-java.util.IdentityHashMap$EntryIterator
-java.util.IdentityHashMap$EntryIterator$Entry
-java.util.IdentityHashMap$EntrySet
-java.util.IdentityHashMap$IdentityHashMapIterator
-java.util.IdentityHashMap$KeyIterator
-java.util.IdentityHashMap$KeySet
-java.util.IdentityHashMap$ValueIterator
-java.util.IdentityHashMap$Values
-java.util.IllegalFormatException
-java.util.IllformedLocaleException
-java.util.Iterator
-java.util.JumboEnumSet
-java.util.JumboEnumSet$EnumSetIterator
-java.util.LinkedHashMap
-java.util.LinkedHashMap$LinkedEntryIterator
-java.util.LinkedHashMap$LinkedEntrySet
-java.util.LinkedHashMap$LinkedHashIterator
-java.util.LinkedHashMap$LinkedHashMapEntry
-java.util.LinkedHashMap$LinkedKeyIterator
-java.util.LinkedHashMap$LinkedKeySet
-java.util.LinkedHashMap$LinkedValueIterator
-java.util.LinkedHashMap$LinkedValues
-java.util.LinkedHashSet
-java.util.LinkedList
-java.util.LinkedList$DescendingIterator
-java.util.LinkedList$ListItr
-java.util.LinkedList$Node
-java.util.List
-java.util.ListIterator
-java.util.Locale
-java.util.Locale$Builder
-java.util.Locale$Cache
-java.util.Locale$Category
-java.util.Locale$FilteringMode
-java.util.Locale$LanguageRange
-java.util.Locale$LocaleKey
-java.util.Locale$NoImagePreloadHolder
-java.util.Map
-java.util.Map$Entry
-java.util.MissingFormatArgumentException
-java.util.MissingResourceException
-java.util.NavigableMap
-java.util.NavigableSet
-java.util.NoSuchElementException
-java.util.Objects
-java.util.Observable
-java.util.Observer
-java.util.Optional
-java.util.PrimitiveIterator
-java.util.PrimitiveIterator$OfInt
-java.util.PriorityQueue
-java.util.PriorityQueue$Itr
-java.util.Properties
-java.util.Properties$LineReader
-java.util.PropertyResourceBundle
-java.util.Queue
-java.util.Random
-java.util.RandomAccess
-java.util.RandomAccessSubList
-java.util.RegularEnumSet
-java.util.RegularEnumSet$EnumSetIterator
-java.util.ResourceBundle
-java.util.ResourceBundle$1
-java.util.ResourceBundle$BundleReference
-java.util.ResourceBundle$CacheKey
-java.util.ResourceBundle$CacheKeyReference
-java.util.ResourceBundle$Control
-java.util.ResourceBundle$Control$1
-java.util.ResourceBundle$Control$CandidateListCache
-java.util.ResourceBundle$LoaderReference
-java.util.Scanner
-java.util.Scanner$1
-java.util.ServiceConfigurationError
-java.util.ServiceLoader
-java.util.ServiceLoader$1
-java.util.ServiceLoader$LazyIterator
-java.util.Set
-java.util.SimpleTimeZone
-java.util.SortedMap
-java.util.SortedSet
-java.util.Spliterator
-java.util.Spliterator$OfDouble
-java.util.Spliterator$OfInt
-java.util.Spliterator$OfLong
-java.util.Spliterator$OfPrimitive
-java.util.Spliterators
-java.util.Spliterators$EmptySpliterator
-java.util.Spliterators$EmptySpliterator$OfDouble
-java.util.Spliterators$EmptySpliterator$OfInt
-java.util.Spliterators$EmptySpliterator$OfLong
-java.util.Spliterators$EmptySpliterator$OfRef
-java.util.Spliterators$IntArraySpliterator
-java.util.Spliterators$IteratorSpliterator
-java.util.Stack
-java.util.StringJoiner
-java.util.StringTokenizer
-java.util.SubList
-java.util.SubList$1
-java.util.TaskQueue
-java.util.TimSort
-java.util.TimeZone
-java.util.Timer
-java.util.Timer$1
-java.util.TimerTask
-java.util.TimerThread
-java.util.TreeMap
-java.util.TreeMap$AscendingSubMap
-java.util.TreeMap$AscendingSubMap$AscendingEntrySetView
-java.util.TreeMap$EntryIterator
-java.util.TreeMap$EntrySet
-java.util.TreeMap$KeyIterator
-java.util.TreeMap$KeySet
-java.util.TreeMap$NavigableSubMap
-java.util.TreeMap$NavigableSubMap$EntrySetView
-java.util.TreeMap$NavigableSubMap$SubMapEntryIterator
-java.util.TreeMap$NavigableSubMap$SubMapIterator
-java.util.TreeMap$NavigableSubMap$SubMapKeyIterator
-java.util.TreeMap$PrivateEntryIterator
-java.util.TreeMap$TreeMapEntry
-java.util.TreeMap$ValueIterator
-java.util.TreeMap$Values
-java.util.TreeSet
-java.util.UUID
-java.util.UUID$Holder
-java.util.UnknownFormatConversionException
-java.util.Vector
-java.util.Vector$1
-java.util.Vector$Itr
-java.util.WeakHashMap
-java.util.WeakHashMap$Entry
-java.util.WeakHashMap$EntrySet
-java.util.WeakHashMap$HashIterator
-java.util.WeakHashMap$KeyIterator
-java.util.WeakHashMap$KeySet
-java.util.WeakHashMap$ValueIterator
-java.util.WeakHashMap$Values
-java.util.concurrent.-$Lambda$xR9BLpu6SifNikvFgr4lEiECBsk
-java.util.concurrent.AbstractExecutorService
-java.util.concurrent.ArrayBlockingQueue
-java.util.concurrent.BlockingDeque
-java.util.concurrent.BlockingQueue
-java.util.concurrent.Callable
-java.util.concurrent.CancellationException
-java.util.concurrent.CompletableFuture
-java.util.concurrent.CompletableFuture$AltResult
-java.util.concurrent.CompletableFuture$AsynchronousCompletionTask
-java.util.concurrent.CompletableFuture$Completion
-java.util.concurrent.CompletionStage
-java.util.concurrent.ConcurrentHashMap
-java.util.concurrent.ConcurrentHashMap$BaseIterator
-java.util.concurrent.ConcurrentHashMap$BulkTask
-java.util.concurrent.ConcurrentHashMap$CollectionView
-java.util.concurrent.ConcurrentHashMap$CounterCell
-java.util.concurrent.ConcurrentHashMap$EntryIterator
-java.util.concurrent.ConcurrentHashMap$EntrySetView
-java.util.concurrent.ConcurrentHashMap$ForEachEntryTask
-java.util.concurrent.ConcurrentHashMap$ForEachKeyTask
-java.util.concurrent.ConcurrentHashMap$ForEachMappingTask
-java.util.concurrent.ConcurrentHashMap$ForEachTransformedEntryTask
-java.util.concurrent.ConcurrentHashMap$ForEachTransformedKeyTask
-java.util.concurrent.ConcurrentHashMap$ForEachTransformedMappingTask
-java.util.concurrent.ConcurrentHashMap$ForEachTransformedValueTask
-java.util.concurrent.ConcurrentHashMap$ForEachValueTask
-java.util.concurrent.ConcurrentHashMap$ForwardingNode
-java.util.concurrent.ConcurrentHashMap$KeyIterator
-java.util.concurrent.ConcurrentHashMap$KeySetView
-java.util.concurrent.ConcurrentHashMap$KeySpliterator
-java.util.concurrent.ConcurrentHashMap$MapEntry
-java.util.concurrent.ConcurrentHashMap$MapReduceEntriesTask
-java.util.concurrent.ConcurrentHashMap$MapReduceEntriesToDoubleTask
-java.util.concurrent.ConcurrentHashMap$MapReduceEntriesToIntTask
-java.util.concurrent.ConcurrentHashMap$MapReduceEntriesToLongTask
-java.util.concurrent.ConcurrentHashMap$MapReduceKeysTask
-java.util.concurrent.ConcurrentHashMap$MapReduceKeysToDoubleTask
-java.util.concurrent.ConcurrentHashMap$MapReduceKeysToIntTask
-java.util.concurrent.ConcurrentHashMap$MapReduceKeysToLongTask
-java.util.concurrent.ConcurrentHashMap$MapReduceMappingsTask
-java.util.concurrent.ConcurrentHashMap$MapReduceMappingsToDoubleTask
-java.util.concurrent.ConcurrentHashMap$MapReduceMappingsToIntTask
-java.util.concurrent.ConcurrentHashMap$MapReduceMappingsToLongTask
-java.util.concurrent.ConcurrentHashMap$MapReduceValuesTask
-java.util.concurrent.ConcurrentHashMap$MapReduceValuesToDoubleTask
-java.util.concurrent.ConcurrentHashMap$MapReduceValuesToIntTask
-java.util.concurrent.ConcurrentHashMap$MapReduceValuesToLongTask
-java.util.concurrent.ConcurrentHashMap$Node
-java.util.concurrent.ConcurrentHashMap$ReduceEntriesTask
-java.util.concurrent.ConcurrentHashMap$ReduceKeysTask
-java.util.concurrent.ConcurrentHashMap$ReduceValuesTask
-java.util.concurrent.ConcurrentHashMap$ReservationNode
-java.util.concurrent.ConcurrentHashMap$SearchEntriesTask
-java.util.concurrent.ConcurrentHashMap$SearchKeysTask
-java.util.concurrent.ConcurrentHashMap$SearchMappingsTask
-java.util.concurrent.ConcurrentHashMap$SearchValuesTask
-java.util.concurrent.ConcurrentHashMap$Segment
-java.util.concurrent.ConcurrentHashMap$Traverser
-java.util.concurrent.ConcurrentHashMap$TreeBin
-java.util.concurrent.ConcurrentHashMap$TreeNode
-java.util.concurrent.ConcurrentHashMap$ValueIterator
-java.util.concurrent.ConcurrentHashMap$ValuesView
-java.util.concurrent.ConcurrentLinkedDeque
-java.util.concurrent.ConcurrentLinkedDeque$Node
-java.util.concurrent.ConcurrentLinkedQueue
-java.util.concurrent.ConcurrentLinkedQueue$Itr
-java.util.concurrent.ConcurrentLinkedQueue$Node
-java.util.concurrent.ConcurrentMap
-java.util.concurrent.ConcurrentNavigableMap
-java.util.concurrent.ConcurrentSkipListMap
-java.util.concurrent.ConcurrentSkipListMap$HeadIndex
-java.util.concurrent.ConcurrentSkipListMap$Index
-java.util.concurrent.ConcurrentSkipListMap$Iter
-java.util.concurrent.ConcurrentSkipListMap$Node
-java.util.concurrent.ConcurrentSkipListMap$ValueIterator
-java.util.concurrent.ConcurrentSkipListMap$Values
-java.util.concurrent.CopyOnWriteArrayList
-java.util.concurrent.CopyOnWriteArrayList$COWIterator
-java.util.concurrent.CopyOnWriteArraySet
-java.util.concurrent.CountDownLatch
-java.util.concurrent.CountDownLatch$Sync
-java.util.concurrent.CountedCompleter
-java.util.concurrent.DelayQueue
-java.util.concurrent.Delayed
-java.util.concurrent.ExecutionException
-java.util.concurrent.Executor
-java.util.concurrent.ExecutorService
-java.util.concurrent.Executors
-java.util.concurrent.Executors$DefaultThreadFactory
-java.util.concurrent.Executors$DelegatedExecutorService
-java.util.concurrent.Executors$DelegatedScheduledExecutorService
-java.util.concurrent.Executors$FinalizableDelegatedExecutorService
-java.util.concurrent.Executors$RunnableAdapter
-java.util.concurrent.ForkJoinPool
-java.util.concurrent.ForkJoinPool$1
-java.util.concurrent.ForkJoinPool$DefaultForkJoinWorkerThreadFactory
-java.util.concurrent.ForkJoinPool$ForkJoinWorkerThreadFactory
-java.util.concurrent.ForkJoinTask
-java.util.concurrent.ForkJoinTask$ExceptionNode
-java.util.concurrent.Future
-java.util.concurrent.FutureTask
-java.util.concurrent.FutureTask$WaitNode
-java.util.concurrent.LinkedBlockingDeque
-java.util.concurrent.LinkedBlockingDeque$AbstractItr
-java.util.concurrent.LinkedBlockingDeque$Itr
-java.util.concurrent.LinkedBlockingDeque$Node
-java.util.concurrent.LinkedBlockingQueue
-java.util.concurrent.LinkedBlockingQueue$Itr
-java.util.concurrent.LinkedBlockingQueue$Node
-java.util.concurrent.PriorityBlockingQueue
-java.util.concurrent.RejectedExecutionException
-java.util.concurrent.RejectedExecutionHandler
-java.util.concurrent.RunnableFuture
-java.util.concurrent.RunnableScheduledFuture
-java.util.concurrent.ScheduledExecutorService
-java.util.concurrent.ScheduledFuture
-java.util.concurrent.ScheduledThreadPoolExecutor
-java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue
-java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask
-java.util.concurrent.Semaphore
-java.util.concurrent.Semaphore$NonfairSync
-java.util.concurrent.Semaphore$Sync
-java.util.concurrent.SynchronousQueue
-java.util.concurrent.SynchronousQueue$TransferQueue
-java.util.concurrent.SynchronousQueue$TransferStack
-java.util.concurrent.SynchronousQueue$TransferStack$SNode
-java.util.concurrent.SynchronousQueue$Transferer
-java.util.concurrent.ThreadFactory
-java.util.concurrent.ThreadLocalRandom
-java.util.concurrent.ThreadLocalRandom$1
-java.util.concurrent.ThreadPoolExecutor
-java.util.concurrent.ThreadPoolExecutor$AbortPolicy
-java.util.concurrent.ThreadPoolExecutor$DiscardOldestPolicy
-java.util.concurrent.ThreadPoolExecutor$DiscardPolicy
-java.util.concurrent.ThreadPoolExecutor$Worker
-java.util.concurrent.TimeUnit
-java.util.concurrent.TimeUnit$1
-java.util.concurrent.TimeUnit$2
-java.util.concurrent.TimeUnit$3
-java.util.concurrent.TimeUnit$4
-java.util.concurrent.TimeUnit$5
-java.util.concurrent.TimeUnit$6
-java.util.concurrent.TimeUnit$7
-java.util.concurrent.TimeoutException
-java.util.concurrent.atomic.AtomicBoolean
-java.util.concurrent.atomic.AtomicInteger
-java.util.concurrent.atomic.AtomicIntegerArray
-java.util.concurrent.atomic.AtomicIntegerFieldUpdater
-java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl
-java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl$1
-java.util.concurrent.atomic.AtomicLong
-java.util.concurrent.atomic.AtomicLongArray
-java.util.concurrent.atomic.AtomicReference
-java.util.concurrent.atomic.AtomicReferenceArray
-java.util.concurrent.atomic.AtomicReferenceFieldUpdater
-java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl
-java.util.concurrent.locks.AbstractOwnableSynchronizer
-java.util.concurrent.locks.AbstractQueuedSynchronizer
-java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject
-java.util.concurrent.locks.AbstractQueuedSynchronizer$Node
-java.util.concurrent.locks.Condition
-java.util.concurrent.locks.Lock
-java.util.concurrent.locks.LockSupport
-java.util.concurrent.locks.ReadWriteLock
-java.util.concurrent.locks.ReentrantLock
-java.util.concurrent.locks.ReentrantLock$FairSync
-java.util.concurrent.locks.ReentrantLock$NonfairSync
-java.util.concurrent.locks.ReentrantLock$Sync
-java.util.concurrent.locks.ReentrantReadWriteLock
-java.util.concurrent.locks.ReentrantReadWriteLock$FairSync
-java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync
-java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock
-java.util.concurrent.locks.ReentrantReadWriteLock$Sync
-java.util.concurrent.locks.ReentrantReadWriteLock$Sync$HoldCounter
-java.util.concurrent.locks.ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter
-java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock
-java.util.function.-$Lambda$1MZdIZ-DL_fjy9l0o8IMJk57T2g
-java.util.function.-$Lambda$VGDeaUHZQIZywZW2ttlyhwk3Cmk
-java.util.function.-$Lambda$VGDeaUHZQIZywZW2ttlyhwk3Cmk$1
-java.util.function.BiConsumer
-java.util.function.BiFunction
-java.util.function.BinaryOperator
-java.util.function.Consumer
-java.util.function.DoubleBinaryOperator
-java.util.function.DoubleUnaryOperator
-java.util.function.Function
-java.util.function.IntBinaryOperator
-java.util.function.IntConsumer
-java.util.function.IntFunction
-java.util.function.IntToDoubleFunction
-java.util.function.IntToLongFunction
-java.util.function.IntUnaryOperator
-java.util.function.LongBinaryOperator
-java.util.function.LongConsumer
-java.util.function.LongUnaryOperator
-java.util.function.Predicate
-java.util.function.Supplier
-java.util.function.ToDoubleBiFunction
-java.util.function.ToDoubleFunction
-java.util.function.ToIntBiFunction
-java.util.function.ToIntFunction
-java.util.function.ToLongBiFunction
-java.util.function.ToLongFunction
-java.util.function.UnaryOperator
-java.util.jar.Attributes
-java.util.jar.Attributes$Name
-java.util.jar.JarEntry
-java.util.jar.JarFile
-java.util.jar.JarFile$JarEntryIterator
-java.util.jar.JarFile$JarFileEntry
-java.util.jar.JarVerifier
-java.util.jar.JarVerifier$1
-java.util.jar.JarVerifier$VerifierStream
-java.util.jar.Manifest
-java.util.jar.Manifest$FastInputStream
-java.util.logging.ErrorManager
-java.util.logging.FileHandler
-java.util.logging.FileHandler$InitializationErrorManager
-java.util.logging.FileHandler$MeteredStream
-java.util.logging.Filter
-java.util.logging.Formatter
-java.util.logging.Handler
-java.util.logging.Level
-java.util.logging.Level$KnownLevel
-java.util.logging.LogManager
-java.util.logging.LogManager$1
-java.util.logging.LogManager$2
-java.util.logging.LogManager$3
-java.util.logging.LogManager$5
-java.util.logging.LogManager$Cleaner
-java.util.logging.LogManager$LogNode
-java.util.logging.LogManager$LoggerContext
-java.util.logging.LogManager$LoggerContext$1
-java.util.logging.LogManager$LoggerWeakRef
-java.util.logging.LogManager$RootLogger
-java.util.logging.LogManager$SystemLoggerContext
-java.util.logging.LogRecord
-java.util.logging.Logger
-java.util.logging.Logger$1
-java.util.logging.Logger$LoggerBundle
-java.util.logging.LoggingPermission
-java.util.logging.LoggingProxyImpl
-java.util.logging.SimpleFormatter
-java.util.logging.StreamHandler
-java.util.logging.XMLFormatter
-java.util.prefs.AbstractPreferences
-java.util.prefs.FileSystemPreferences
-java.util.prefs.Preferences
-java.util.regex.MatchResult
-java.util.regex.Matcher
-java.util.regex.Matcher$OffsetBasedMatchResult
-java.util.regex.Pattern
-java.util.regex.PatternSyntaxException
-java.util.stream.-$Lambda$DJvCeprCIGMk0JvfSkTmQUmEYKA$1
-java.util.stream.-$Lambda$QgGTJrv63_zzBbeGjswm_UMqEbo$12
-java.util.stream.-$Lambda$QgGTJrv63_zzBbeGjswm_UMqEbo$5
-java.util.stream.-$Lambda$QgGTJrv63_zzBbeGjswm_UMqEbo$6
-java.util.stream.-$Lambda$RYrQKhHyGc-mMxiERR98xxRAWkA$5
-java.util.stream.-$Lambda$ioGbka_-VkWTFjRjTt8T4zzsxgk$3
-java.util.stream.-$Lambda$ioGbka_-VkWTFjRjTt8T4zzsxgk$7
-java.util.stream.-$Lambda$qTstLJg88fs2C3g6LH-R51vCVP0$21
-java.util.stream.-$Lambda$qTstLJg88fs2C3g6LH-R51vCVP0$22
-java.util.stream.-$Lambda$qTstLJg88fs2C3g6LH-R51vCVP0$26
-java.util.stream.-$Lambda$qTstLJg88fs2C3g6LH-R51vCVP0$4
-java.util.stream.-$Lambda$qTstLJg88fs2C3g6LH-R51vCVP0$5
-java.util.stream.-$Lambda$qTstLJg88fs2C3g6LH-R51vCVP0$51
-java.util.stream.-$Lambda$qTstLJg88fs2C3g6LH-R51vCVP0$54
-java.util.stream.-$Lambda$qTstLJg88fs2C3g6LH-R51vCVP0$67
-java.util.stream.-$Lambda$qTstLJg88fs2C3g6LH-R51vCVP0$83
-java.util.stream.AbstractPipeline
-java.util.stream.AbstractSpinedBuffer
-java.util.stream.BaseStream
-java.util.stream.Collector
-java.util.stream.Collector$Characteristics
-java.util.stream.Collectors
-java.util.stream.Collectors$CollectorImpl
-java.util.stream.DistinctOps
-java.util.stream.DistinctOps$1
-java.util.stream.DistinctOps$1$2
-java.util.stream.DoubleStream
-java.util.stream.FindOps
-java.util.stream.FindOps$FindOp
-java.util.stream.FindOps$FindSink
-java.util.stream.FindOps$FindSink$OfRef
-java.util.stream.ForEachOps
-java.util.stream.ForEachOps$ForEachOp
-java.util.stream.ForEachOps$ForEachOp$OfRef
-java.util.stream.IntPipeline
-java.util.stream.IntPipeline$4
-java.util.stream.IntPipeline$4$1
-java.util.stream.IntPipeline$Head
-java.util.stream.IntPipeline$StatelessOp
-java.util.stream.IntStream
-java.util.stream.LongPipeline
-java.util.stream.LongPipeline$StatelessOp
-java.util.stream.LongStream
-java.util.stream.Node
-java.util.stream.Node$Builder
-java.util.stream.Node$Builder$OfInt
-java.util.stream.Node$OfDouble
-java.util.stream.Node$OfInt
-java.util.stream.Node$OfLong
-java.util.stream.Node$OfPrimitive
-java.util.stream.Nodes
-java.util.stream.Nodes$EmptyNode
-java.util.stream.Nodes$EmptyNode$OfDouble
-java.util.stream.Nodes$EmptyNode$OfInt
-java.util.stream.Nodes$EmptyNode$OfLong
-java.util.stream.Nodes$EmptyNode$OfRef
-java.util.stream.Nodes$IntSpinedNodeBuilder
-java.util.stream.PipelineHelper
-java.util.stream.ReduceOps
-java.util.stream.ReduceOps$3
-java.util.stream.ReduceOps$3ReducingSink
-java.util.stream.ReduceOps$8
-java.util.stream.ReduceOps$8ReducingSink
-java.util.stream.ReduceOps$AccumulatingSink
-java.util.stream.ReduceOps$Box
-java.util.stream.ReduceOps$ReduceOp
-java.util.stream.ReferencePipeline
-java.util.stream.ReferencePipeline$2
-java.util.stream.ReferencePipeline$2$1
-java.util.stream.ReferencePipeline$3
-java.util.stream.ReferencePipeline$3$1
-java.util.stream.ReferencePipeline$4
-java.util.stream.ReferencePipeline$4$1
-java.util.stream.ReferencePipeline$5
-java.util.stream.ReferencePipeline$5$1
-java.util.stream.ReferencePipeline$Head
-java.util.stream.ReferencePipeline$StatefulOp
-java.util.stream.ReferencePipeline$StatelessOp
-java.util.stream.Sink
-java.util.stream.Sink$ChainedInt
-java.util.stream.Sink$ChainedReference
-java.util.stream.Sink$OfInt
-java.util.stream.Sink$OfLong
-java.util.stream.SliceOps
-java.util.stream.SliceOps$1
-java.util.stream.SliceOps$1$1
-java.util.stream.SpinedBuffer$OfInt
-java.util.stream.SpinedBuffer$OfPrimitive
-java.util.stream.Stream
-java.util.stream.StreamOpFlag
-java.util.stream.StreamOpFlag$MaskBuilder
-java.util.stream.StreamOpFlag$Type
-java.util.stream.StreamShape
-java.util.stream.StreamSpliterators$InfiniteSupplyingSpliterator
-java.util.stream.StreamSpliterators$InfiniteSupplyingSpliterator$OfRef
-java.util.stream.StreamSupport
-java.util.stream.TerminalOp
-java.util.stream.TerminalSink
-java.util.zip.Adler32
-java.util.zip.CRC32
-java.util.zip.CheckedInputStream
-java.util.zip.Checksum
-java.util.zip.DataFormatException
-java.util.zip.Deflater
-java.util.zip.DeflaterOutputStream
-java.util.zip.GZIPInputStream
-java.util.zip.GZIPInputStream$1
-java.util.zip.GZIPOutputStream
-java.util.zip.Inflater
-java.util.zip.InflaterInputStream
-java.util.zip.ZStreamRef
-java.util.zip.ZipCoder
-java.util.zip.ZipConstants
-java.util.zip.ZipEntry
-java.util.zip.ZipException
-java.util.zip.ZipFile
-java.util.zip.ZipFile$ZipEntryIterator
-java.util.zip.ZipFile$ZipFileInflaterInputStream
-java.util.zip.ZipFile$ZipFileInputStream
-java.util.zip.ZipInputStream
-java.util.zip.ZipUtils
-javax.crypto.BadPaddingException
-javax.crypto.Cipher
-javax.crypto.Cipher$CipherSpiAndProvider
-javax.crypto.Cipher$InitParams
-javax.crypto.Cipher$InitType
-javax.crypto.Cipher$NeedToSet
-javax.crypto.Cipher$SpiAndProviderUpdater
-javax.crypto.Cipher$Transform
-javax.crypto.CipherSpi
-javax.crypto.IllegalBlockSizeException
-javax.crypto.JarVerifier
-javax.crypto.JceSecurity
-javax.crypto.JceSecurity$1
-javax.crypto.KeyGenerator
-javax.crypto.KeyGeneratorSpi
-javax.crypto.Mac
-javax.crypto.MacSpi
-javax.crypto.NoSuchPaddingException
-javax.crypto.NullCipher
-javax.crypto.SecretKey
-javax.crypto.ShortBufferException
-javax.crypto.spec.IvParameterSpec
-javax.crypto.spec.OAEPParameterSpec
-javax.crypto.spec.PBEParameterSpec
-javax.crypto.spec.PSource
-javax.crypto.spec.PSource$PSpecified
-javax.crypto.spec.SecretKeySpec
-javax.microedition.khronos.egl.EGL
-javax.microedition.khronos.egl.EGL10
-javax.microedition.khronos.egl.EGLConfig
-javax.microedition.khronos.egl.EGLContext
-javax.microedition.khronos.egl.EGLDisplay
-javax.microedition.khronos.egl.EGLSurface
-javax.microedition.khronos.opengles.GL
-javax.microedition.khronos.opengles.GL10
-javax.microedition.khronos.opengles.GL10Ext
-javax.microedition.khronos.opengles.GL11
-javax.microedition.khronos.opengles.GL11Ext
-javax.microedition.khronos.opengles.GL11ExtensionPack
-javax.net.DefaultSocketFactory
-javax.net.ServerSocketFactory
-javax.net.SocketFactory
-javax.net.ssl.ExtendedSSLSession
-javax.net.ssl.HandshakeCompletedListener
-javax.net.ssl.HostnameVerifier
-javax.net.ssl.HttpsURLConnection
-javax.net.ssl.KeyManager
-javax.net.ssl.KeyManagerFactory
-javax.net.ssl.KeyManagerFactory$1
-javax.net.ssl.KeyManagerFactorySpi
-javax.net.ssl.SNIHostName
-javax.net.ssl.SNIServerName
-javax.net.ssl.SSLContext
-javax.net.ssl.SSLContextSpi
-javax.net.ssl.SSLEngine
-javax.net.ssl.SSLException
-javax.net.ssl.SSLParameters
-javax.net.ssl.SSLPeerUnverifiedException
-javax.net.ssl.SSLProtocolException
-javax.net.ssl.SSLServerSocketFactory
-javax.net.ssl.SSLSession
-javax.net.ssl.SSLSessionContext
-javax.net.ssl.SSLSocket
-javax.net.ssl.SSLSocketFactory
-javax.net.ssl.SSLSocketFactory$1
-javax.net.ssl.TrustManager
-javax.net.ssl.TrustManagerFactory
-javax.net.ssl.TrustManagerFactory$1
-javax.net.ssl.TrustManagerFactorySpi
-javax.net.ssl.X509ExtendedKeyManager
-javax.net.ssl.X509ExtendedTrustManager
-javax.net.ssl.X509KeyManager
-javax.net.ssl.X509TrustManager
-javax.security.auth.Destroyable
-javax.security.auth.callback.UnsupportedCallbackException
-javax.security.auth.x500.X500Principal
-javax.security.cert.Certificate
-javax.security.cert.CertificateEncodingException
-javax.security.cert.CertificateException
-javax.security.cert.X509Certificate
-javax.sip.SipException
-javax.xml.parsers.ParserConfigurationException
-javax.xml.parsers.SAXParser
-javax.xml.parsers.SAXParserFactory
-javax.xml.transform.TransformerConfigurationException
-javax.xml.transform.TransformerException
-junit.framework.Assert
-libcore.icu.CollationKeyICU
-libcore.icu.DateIntervalFormat
-libcore.icu.DateUtilsBridge
-libcore.icu.ICU
-libcore.icu.LocaleData
-libcore.icu.NativeConverter
-libcore.icu.RelativeDateTimeFormatter
-libcore.icu.RelativeDateTimeFormatter$FormatterCache
-libcore.icu.TimeZoneNames
-libcore.icu.TimeZoneNames$1
-libcore.icu.TimeZoneNames$ZoneStringsCache
-libcore.internal.StringPool
-libcore.io.AsynchronousCloseMonitor
-libcore.io.BlockGuardOs
-libcore.io.BufferIterator
-libcore.io.ClassPathURLStreamHandler
-libcore.io.ClassPathURLStreamHandler$ClassPathURLConnection
-libcore.io.ClassPathURLStreamHandler$ClassPathURLConnection$1
-libcore.io.DropBox
-libcore.io.DropBox$DefaultReporter
-libcore.io.DropBox$Reporter
-libcore.io.EventLogger
-libcore.io.EventLogger$DefaultReporter
-libcore.io.EventLogger$Reporter
-libcore.io.ForwardingOs
-libcore.io.IoBridge
-libcore.io.IoTracker
-libcore.io.IoTracker$Mode
-libcore.io.IoUtils
-libcore.io.IoUtils$FileReader
-libcore.io.Libcore
-libcore.io.Linux
-libcore.io.Memory
-libcore.io.MemoryMappedFile
-libcore.io.NioBufferIterator
-libcore.io.Os
-libcore.io.Streams
-libcore.math.MathUtils
-libcore.net.MimeUtils
-libcore.net.NetworkSecurityPolicy
-libcore.net.NetworkSecurityPolicy$DefaultNetworkSecurityPolicy
-libcore.net.UriCodec
-libcore.net.event.NetworkEventDispatcher
-libcore.net.event.NetworkEventListener
-libcore.reflect.AnnotatedElements
-libcore.reflect.AnnotationFactory
-libcore.reflect.AnnotationMember
-libcore.reflect.AnnotationMember$DefaultValues
-libcore.reflect.GenericArrayTypeImpl
-libcore.reflect.GenericSignatureParser
-libcore.reflect.ListOfTypes
-libcore.reflect.ListOfVariables
-libcore.reflect.ParameterizedTypeImpl
-libcore.reflect.TypeVariableImpl
-libcore.reflect.Types
-libcore.util.BasicLruCache
-libcore.util.CharsetUtils
-libcore.util.CollectionUtils
-libcore.util.EmptyArray
-libcore.util.HexEncoding
-libcore.util.NativeAllocationRegistry
-libcore.util.NativeAllocationRegistry$CleanerRunner
-libcore.util.NativeAllocationRegistry$CleanerThunk
-libcore.util.Objects
-libcore.util.TimeZoneDataFiles
-libcore.util.ZoneInfo
-libcore.util.ZoneInfo$CheckedArithmeticException
-libcore.util.ZoneInfo$OffsetInterval
-libcore.util.ZoneInfo$WallTime
-libcore.util.ZoneInfoDB
-libcore.util.ZoneInfoDB$TzData
-libcore.util.ZoneInfoDB$TzData$1
-org.apache.commons.logging.Log
-org.apache.commons.logging.LogFactory
-org.apache.commons.logging.impl.Jdk14Logger
-org.apache.commons.logging.impl.WeakHashtable
-org.apache.harmony.dalvik.NativeTestTarget
-org.apache.harmony.dalvik.ddmc.Chunk
-org.apache.harmony.dalvik.ddmc.ChunkHandler
-org.apache.harmony.dalvik.ddmc.DdmServer
-org.apache.harmony.dalvik.ddmc.DdmVmInternal
-org.apache.harmony.luni.internal.util.TimezoneGetter
-org.apache.harmony.xml.ExpatAttributes
-org.apache.harmony.xml.ExpatException
-org.apache.harmony.xml.ExpatParser
-org.apache.harmony.xml.ExpatParser$CurrentAttributes
-org.apache.harmony.xml.ExpatParser$ExpatLocator
-org.apache.harmony.xml.ExpatReader
-org.apache.harmony.xml.parsers.SAXParserFactoryImpl
-org.apache.harmony.xml.parsers.SAXParserImpl
-org.apache.http.ConnectionReuseStrategy
-org.apache.http.FormattedHeader
-org.apache.http.Header
-org.apache.http.HeaderElement
-org.apache.http.HeaderElementIterator
-org.apache.http.HeaderIterator
-org.apache.http.HttpClientConnection
-org.apache.http.HttpConnection
-org.apache.http.HttpConnectionMetrics
-org.apache.http.HttpEntity
-org.apache.http.HttpEntityEnclosingRequest
-org.apache.http.HttpException
-org.apache.http.HttpHost
-org.apache.http.HttpInetConnection
-org.apache.http.HttpMessage
-org.apache.http.HttpRequest
-org.apache.http.HttpRequestFactory
-org.apache.http.HttpRequestInterceptor
-org.apache.http.HttpResponse
-org.apache.http.HttpResponseFactory
-org.apache.http.HttpResponseInterceptor
-org.apache.http.HttpServerConnection
-org.apache.http.HttpVersion
-org.apache.http.NameValuePair
-org.apache.http.ParseException
-org.apache.http.ProtocolException
-org.apache.http.ProtocolVersion
-org.apache.http.ReasonPhraseCatalog
-org.apache.http.RequestLine
-org.apache.http.StatusLine
-org.apache.http.auth.AuthSchemeFactory
-org.apache.http.auth.AuthSchemeRegistry
-org.apache.http.auth.AuthState
-org.apache.http.auth.AuthenticationException
-org.apache.http.client.AuthenticationHandler
-org.apache.http.client.ClientProtocolException
-org.apache.http.client.CookieStore
-org.apache.http.client.CredentialsProvider
-org.apache.http.client.HttpClient
-org.apache.http.client.HttpRequestRetryHandler
-org.apache.http.client.HttpResponseException
-org.apache.http.client.RedirectHandler
-org.apache.http.client.RequestDirector
-org.apache.http.client.ResponseHandler
-org.apache.http.client.UserTokenHandler
-org.apache.http.client.entity.UrlEncodedFormEntity
-org.apache.http.client.methods.AbortableHttpRequest
-org.apache.http.client.methods.HttpEntityEnclosingRequestBase
-org.apache.http.client.methods.HttpGet
-org.apache.http.client.methods.HttpPost
-org.apache.http.client.methods.HttpPut
-org.apache.http.client.methods.HttpRequestBase
-org.apache.http.client.methods.HttpUriRequest
-org.apache.http.client.params.HttpClientParams
-org.apache.http.client.protocol.RequestAddCookies
-org.apache.http.client.protocol.RequestDefaultHeaders
-org.apache.http.client.protocol.RequestProxyAuthentication
-org.apache.http.client.protocol.RequestTargetAuthentication
-org.apache.http.client.protocol.ResponseProcessCookies
-org.apache.http.client.utils.URIUtils
-org.apache.http.client.utils.URLEncodedUtils
-org.apache.http.conn.BasicManagedEntity
-org.apache.http.conn.ClientConnectionManager
-org.apache.http.conn.ClientConnectionOperator
-org.apache.http.conn.ClientConnectionRequest
-org.apache.http.conn.ConnectTimeoutException
-org.apache.http.conn.ConnectionKeepAliveStrategy
-org.apache.http.conn.ConnectionReleaseTrigger
-org.apache.http.conn.EofSensorInputStream
-org.apache.http.conn.EofSensorWatcher
-org.apache.http.conn.ManagedClientConnection
-org.apache.http.conn.OperatedClientConnection
-org.apache.http.conn.params.ConnManagerPNames
-org.apache.http.conn.params.ConnManagerParamBean
-org.apache.http.conn.params.ConnManagerParams
-org.apache.http.conn.params.ConnManagerParams$1
-org.apache.http.conn.params.ConnPerRoute
-org.apache.http.conn.params.ConnPerRouteBean
-org.apache.http.conn.params.ConnRoutePNames
-org.apache.http.conn.params.ConnRouteParams
-org.apache.http.conn.routing.BasicRouteDirector
-org.apache.http.conn.routing.HttpRoute
-org.apache.http.conn.routing.HttpRouteDirector
-org.apache.http.conn.routing.HttpRoutePlanner
-org.apache.http.conn.routing.RouteInfo
-org.apache.http.conn.routing.RouteInfo$LayerType
-org.apache.http.conn.routing.RouteInfo$TunnelType
-org.apache.http.conn.routing.RouteTracker
-org.apache.http.conn.scheme.LayeredSocketFactory
-org.apache.http.conn.scheme.PlainSocketFactory
-org.apache.http.conn.scheme.Scheme
-org.apache.http.conn.scheme.SchemeRegistry
-org.apache.http.conn.scheme.SocketFactory
-org.apache.http.conn.ssl.AbstractVerifier
-org.apache.http.conn.ssl.AllowAllHostnameVerifier
-org.apache.http.conn.ssl.AndroidDistinguishedNameParser
-org.apache.http.conn.ssl.BrowserCompatHostnameVerifier
-org.apache.http.conn.ssl.SSLSocketFactory
-org.apache.http.conn.ssl.StrictHostnameVerifier
-org.apache.http.conn.ssl.X509HostnameVerifier
-org.apache.http.cookie.ClientCookie
-org.apache.http.cookie.Cookie
-org.apache.http.cookie.CookieAttributeHandler
-org.apache.http.cookie.CookieIdentityComparator
-org.apache.http.cookie.CookieOrigin
-org.apache.http.cookie.CookiePathComparator
-org.apache.http.cookie.CookieSpec
-org.apache.http.cookie.CookieSpecFactory
-org.apache.http.cookie.CookieSpecRegistry
-org.apache.http.cookie.MalformedCookieException
-org.apache.http.cookie.SetCookie
-org.apache.http.entity.AbstractHttpEntity
-org.apache.http.entity.BasicHttpEntity
-org.apache.http.entity.ByteArrayEntity
-org.apache.http.entity.ContentLengthStrategy
-org.apache.http.entity.HttpEntityWrapper
-org.apache.http.entity.InputStreamEntity
-org.apache.http.entity.StringEntity
-org.apache.http.impl.AbstractHttpClientConnection
-org.apache.http.impl.AbstractHttpServerConnection
-org.apache.http.impl.DefaultConnectionReuseStrategy
-org.apache.http.impl.DefaultHttpRequestFactory
-org.apache.http.impl.DefaultHttpResponseFactory
-org.apache.http.impl.DefaultHttpServerConnection
-org.apache.http.impl.EnglishReasonPhraseCatalog
-org.apache.http.impl.HttpConnectionMetricsImpl
-org.apache.http.impl.SocketHttpClientConnection
-org.apache.http.impl.SocketHttpServerConnection
-org.apache.http.impl.auth.BasicSchemeFactory
-org.apache.http.impl.auth.DigestSchemeFactory
-org.apache.http.impl.client.AbstractAuthenticationHandler
-org.apache.http.impl.client.AbstractHttpClient
-org.apache.http.impl.client.BasicCookieStore
-org.apache.http.impl.client.BasicCredentialsProvider
-org.apache.http.impl.client.ClientParamsStack
-org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy
-org.apache.http.impl.client.DefaultHttpClient
-org.apache.http.impl.client.DefaultHttpRequestRetryHandler
-org.apache.http.impl.client.DefaultProxyAuthenticationHandler
-org.apache.http.impl.client.DefaultRedirectHandler
-org.apache.http.impl.client.DefaultRequestDirector
-org.apache.http.impl.client.DefaultTargetAuthenticationHandler
-org.apache.http.impl.client.DefaultUserTokenHandler
-org.apache.http.impl.client.EntityEnclosingRequestWrapper
-org.apache.http.impl.client.RequestWrapper
-org.apache.http.impl.client.RoutedRequest
-org.apache.http.impl.client.TunnelRefusedException
-org.apache.http.impl.conn.AbstractClientConnAdapter
-org.apache.http.impl.conn.AbstractPoolEntry
-org.apache.http.impl.conn.AbstractPooledConnAdapter
-org.apache.http.impl.conn.DefaultClientConnection
-org.apache.http.impl.conn.DefaultClientConnectionOperator
-org.apache.http.impl.conn.DefaultResponseParser
-org.apache.http.impl.conn.IdleConnectionHandler
-org.apache.http.impl.conn.IdleConnectionHandler$TimeValues
-org.apache.http.impl.conn.ProxySelectorRoutePlanner
-org.apache.http.impl.conn.tsccm.AbstractConnPool
-org.apache.http.impl.conn.tsccm.BasicPoolEntry
-org.apache.http.impl.conn.tsccm.BasicPoolEntryRef
-org.apache.http.impl.conn.tsccm.BasicPooledConnAdapter
-org.apache.http.impl.conn.tsccm.ConnPoolByRoute
-org.apache.http.impl.conn.tsccm.ConnPoolByRoute$1
-org.apache.http.impl.conn.tsccm.PoolEntryRequest
-org.apache.http.impl.conn.tsccm.RefQueueHandler
-org.apache.http.impl.conn.tsccm.RefQueueWorker
-org.apache.http.impl.conn.tsccm.RouteSpecificPool
-org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager
-org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager$1
-org.apache.http.impl.conn.tsccm.WaitingThreadAborter
-org.apache.http.impl.cookie.AbstractCookieAttributeHandler
-org.apache.http.impl.cookie.AbstractCookieSpec
-org.apache.http.impl.cookie.BasicClientCookie
-org.apache.http.impl.cookie.BasicCommentHandler
-org.apache.http.impl.cookie.BasicDomainHandler
-org.apache.http.impl.cookie.BasicExpiresHandler
-org.apache.http.impl.cookie.BasicMaxAgeHandler
-org.apache.http.impl.cookie.BasicPathHandler
-org.apache.http.impl.cookie.BasicSecureHandler
-org.apache.http.impl.cookie.BestMatchSpec
-org.apache.http.impl.cookie.BestMatchSpecFactory
-org.apache.http.impl.cookie.BrowserCompatSpec
-org.apache.http.impl.cookie.BrowserCompatSpecFactory
-org.apache.http.impl.cookie.CookieSpecBase
-org.apache.http.impl.cookie.DateParseException
-org.apache.http.impl.cookie.DateUtils
-org.apache.http.impl.cookie.DateUtils$DateFormatHolder
-org.apache.http.impl.cookie.DateUtils$DateFormatHolder$1
-org.apache.http.impl.cookie.NetscapeDomainHandler
-org.apache.http.impl.cookie.NetscapeDraftHeaderParser
-org.apache.http.impl.cookie.NetscapeDraftSpec
-org.apache.http.impl.cookie.NetscapeDraftSpecFactory
-org.apache.http.impl.cookie.RFC2109DomainHandler
-org.apache.http.impl.cookie.RFC2109Spec
-org.apache.http.impl.cookie.RFC2109SpecFactory
-org.apache.http.impl.cookie.RFC2109VersionHandler
-org.apache.http.impl.cookie.RFC2965CommentUrlAttributeHandler
-org.apache.http.impl.cookie.RFC2965DiscardAttributeHandler
-org.apache.http.impl.cookie.RFC2965DomainAttributeHandler
-org.apache.http.impl.cookie.RFC2965PortAttributeHandler
-org.apache.http.impl.cookie.RFC2965Spec
-org.apache.http.impl.cookie.RFC2965SpecFactory
-org.apache.http.impl.cookie.RFC2965VersionAttributeHandler
-org.apache.http.impl.entity.EntityDeserializer
-org.apache.http.impl.entity.EntitySerializer
-org.apache.http.impl.entity.LaxContentLengthStrategy
-org.apache.http.impl.entity.StrictContentLengthStrategy
-org.apache.http.impl.io.AbstractMessageParser
-org.apache.http.impl.io.AbstractMessageWriter
-org.apache.http.impl.io.AbstractSessionInputBuffer
-org.apache.http.impl.io.AbstractSessionOutputBuffer
-org.apache.http.impl.io.ChunkedInputStream
-org.apache.http.impl.io.ContentLengthInputStream
-org.apache.http.impl.io.ContentLengthOutputStream
-org.apache.http.impl.io.HttpRequestParser
-org.apache.http.impl.io.HttpRequestWriter
-org.apache.http.impl.io.HttpResponseWriter
-org.apache.http.impl.io.HttpTransportMetricsImpl
-org.apache.http.impl.io.IdentityOutputStream
-org.apache.http.impl.io.SocketInputBuffer
-org.apache.http.impl.io.SocketOutputBuffer
-org.apache.http.io.HttpMessageParser
-org.apache.http.io.HttpMessageWriter
-org.apache.http.io.HttpTransportMetrics
-org.apache.http.io.SessionInputBuffer
-org.apache.http.io.SessionOutputBuffer
-org.apache.http.message.AbstractHttpMessage
-org.apache.http.message.BasicHeader
-org.apache.http.message.BasicHeaderElement
-org.apache.http.message.BasicHeaderElementIterator
-org.apache.http.message.BasicHeaderValueParser
-org.apache.http.message.BasicHttpRequest
-org.apache.http.message.BasicHttpResponse
-org.apache.http.message.BasicLineFormatter
-org.apache.http.message.BasicLineParser
-org.apache.http.message.BasicListHeaderIterator
-org.apache.http.message.BasicNameValuePair
-org.apache.http.message.BasicRequestLine
-org.apache.http.message.BasicStatusLine
-org.apache.http.message.BufferedHeader
-org.apache.http.message.HeaderGroup
-org.apache.http.message.HeaderValueParser
-org.apache.http.message.LineFormatter
-org.apache.http.message.LineParser
-org.apache.http.message.ParserCursor
-org.apache.http.params.AbstractHttpParams
-org.apache.http.params.BasicHttpParams
-org.apache.http.params.CoreConnectionPNames
-org.apache.http.params.CoreProtocolPNames
-org.apache.http.params.DefaultedHttpParams
-org.apache.http.params.HttpAbstractParamBean
-org.apache.http.params.HttpConnectionParams
-org.apache.http.params.HttpParams
-org.apache.http.params.HttpProtocolParams
-org.apache.http.protocol.BasicHttpContext
-org.apache.http.protocol.BasicHttpProcessor
-org.apache.http.protocol.DefaultedHttpContext
-org.apache.http.protocol.HTTP
-org.apache.http.protocol.HttpContext
-org.apache.http.protocol.HttpProcessor
-org.apache.http.protocol.HttpRequestExecutor
-org.apache.http.protocol.HttpRequestHandler
-org.apache.http.protocol.HttpRequestHandlerRegistry
-org.apache.http.protocol.HttpRequestHandlerResolver
-org.apache.http.protocol.HttpRequestInterceptorList
-org.apache.http.protocol.HttpResponseInterceptorList
-org.apache.http.protocol.HttpService
-org.apache.http.protocol.RequestConnControl
-org.apache.http.protocol.RequestContent
-org.apache.http.protocol.RequestExpectContinue
-org.apache.http.protocol.RequestTargetHost
-org.apache.http.protocol.RequestUserAgent
-org.apache.http.protocol.ResponseConnControl
-org.apache.http.protocol.ResponseContent
-org.apache.http.protocol.UriPatternMatcher
-org.apache.http.util.ByteArrayBuffer
-org.apache.http.util.CharArrayBuffer
-org.apache.http.util.EntityUtils
-org.apache.http.util.LangUtils
-org.ccil.cowan.tagsoup.AttributesImpl
-org.ccil.cowan.tagsoup.AutoDetector
-org.ccil.cowan.tagsoup.Element
-org.ccil.cowan.tagsoup.ElementType
-org.ccil.cowan.tagsoup.HTMLModels
-org.ccil.cowan.tagsoup.HTMLScanner
-org.ccil.cowan.tagsoup.HTMLSchema
-org.ccil.cowan.tagsoup.Parser
-org.ccil.cowan.tagsoup.Parser$1
-org.ccil.cowan.tagsoup.ScanHandler
-org.ccil.cowan.tagsoup.Scanner
-org.ccil.cowan.tagsoup.Schema
-org.json.JSON
-org.json.JSONArray
-org.json.JSONException
-org.json.JSONObject
-org.json.JSONObject$1
-org.json.JSONStringer
-org.json.JSONStringer$Scope
-org.json.JSONTokener
-org.kxml2.io.KXmlParser
-org.kxml2.io.KXmlParser$ValueContext
-org.kxml2.io.KXmlSerializer
-org.xml.sax.Attributes
-org.xml.sax.ContentHandler
-org.xml.sax.DTDHandler
-org.xml.sax.EntityResolver
-org.xml.sax.ErrorHandler
-org.xml.sax.InputSource
-org.xml.sax.Locator
-org.xml.sax.SAXException
-org.xml.sax.SAXNotRecognizedException
-org.xml.sax.SAXNotSupportedException
-org.xml.sax.SAXParseException
-org.xml.sax.XMLReader
-org.xml.sax.ext.DeclHandler
-org.xml.sax.ext.DefaultHandler2
-org.xml.sax.ext.EntityResolver2
-org.xml.sax.ext.LexicalHandler
-org.xml.sax.helpers.AttributesImpl
-org.xml.sax.helpers.DefaultHandler
-org.xmlpull.v1.XmlPullParser
-org.xmlpull.v1.XmlPullParserException
-org.xmlpull.v1.XmlPullParserFactory
-org.xmlpull.v1.XmlSerializer
-sun.invoke.util.BytecodeDescriptor
-sun.invoke.util.VerifyAccess
-sun.invoke.util.Wrapper
-sun.invoke.util.Wrapper$Format
-sun.misc.ASCIICaseInsensitiveComparator
-sun.misc.Cleaner
-sun.misc.CompoundEnumeration
-sun.misc.FDBigInteger
-sun.misc.FloatingDecimal
-sun.misc.FloatingDecimal$1
-sun.misc.FloatingDecimal$ASCIIToBinaryBuffer
-sun.misc.FloatingDecimal$ASCIIToBinaryConverter
-sun.misc.FloatingDecimal$BinaryToASCIIBuffer
-sun.misc.FloatingDecimal$BinaryToASCIIConverter
-sun.misc.FloatingDecimal$ExceptionalBinaryToASCIIBuffer
-sun.misc.FloatingDecimal$PreparedASCIIToBinaryBuffer
-sun.misc.FormattedFloatingDecimal
-sun.misc.FormattedFloatingDecimal$1
-sun.misc.FormattedFloatingDecimal$Form
-sun.misc.IOUtils
-sun.misc.JavaIOFileDescriptorAccess
-sun.misc.LRUCache
-sun.misc.REException
-sun.misc.SharedSecrets
-sun.misc.Unsafe
-sun.misc.VM
-sun.misc.Version
-sun.net.ConnectionResetException
-sun.net.NetHooks
-sun.net.NetProperties
-sun.net.NetProperties$1
-sun.net.ResourceManager
-sun.net.spi.DefaultProxySelector
-sun.net.spi.DefaultProxySelector$1
-sun.net.spi.DefaultProxySelector$NonProxyInfo
-sun.net.spi.nameservice.NameService
-sun.net.util.IPAddressUtil
-sun.net.www.ParseUtil
-sun.net.www.protocol.file.Handler
-sun.net.www.protocol.jar.Handler
-sun.nio.ch.AbstractPollArrayWrapper
-sun.nio.ch.AbstractPollSelectorImpl
-sun.nio.ch.AllocatedNativeObject
-sun.nio.ch.ChannelInputStream
-sun.nio.ch.DatagramChannelImpl
-sun.nio.ch.DatagramDispatcher
-sun.nio.ch.DefaultSelectorProvider
-sun.nio.ch.DirectBuffer
-sun.nio.ch.FileChannelImpl
-sun.nio.ch.FileChannelImpl$Unmapper
-sun.nio.ch.FileDescriptorHolderSocketImpl
-sun.nio.ch.FileDispatcher
-sun.nio.ch.FileDispatcherImpl
-sun.nio.ch.FileKey
-sun.nio.ch.FileLockImpl
-sun.nio.ch.FileLockTable
-sun.nio.ch.IOStatus
-sun.nio.ch.IOUtil
-sun.nio.ch.Interruptible
-sun.nio.ch.NativeDispatcher
-sun.nio.ch.NativeObject
-sun.nio.ch.NativeThread
-sun.nio.ch.NativeThreadSet
-sun.nio.ch.Net
-sun.nio.ch.Net$1
-sun.nio.ch.Net$4
-sun.nio.ch.PollArrayWrapper
-sun.nio.ch.PollSelectorImpl
-sun.nio.ch.PollSelectorProvider
-sun.nio.ch.SelChImpl
-sun.nio.ch.SelectionKeyImpl
-sun.nio.ch.SelectorImpl
-sun.nio.ch.SelectorProviderImpl
-sun.nio.ch.ServerSocketChannelImpl
-sun.nio.ch.SharedFileLockTable
-sun.nio.ch.SharedFileLockTable$FileLockReference
-sun.nio.ch.SocketAdaptor
-sun.nio.ch.SocketAdaptor$1
-sun.nio.ch.SocketAdaptor$2
-sun.nio.ch.SocketAdaptor$SocketInputStream
-sun.nio.ch.SocketChannelImpl
-sun.nio.ch.SocketDispatcher
-sun.nio.ch.Util
-sun.nio.ch.Util$1
-sun.nio.ch.Util$2
-sun.nio.ch.Util$BufferCache
-sun.nio.cs.ArrayDecoder
-sun.nio.cs.ArrayEncoder
-sun.nio.cs.StreamDecoder
-sun.nio.cs.StreamEncoder
-sun.nio.cs.ThreadLocalCoders
-sun.nio.cs.ThreadLocalCoders$1
-sun.nio.cs.ThreadLocalCoders$2
-sun.nio.cs.ThreadLocalCoders$Cache
-sun.nio.fs.AbstractBasicFileAttributeView
-sun.nio.fs.AbstractFileSystemProvider
-sun.nio.fs.AbstractPath
-sun.nio.fs.DefaultFileSystemProvider
-sun.nio.fs.DynamicFileAttributeView
-sun.nio.fs.LinuxFileSystem
-sun.nio.fs.LinuxFileSystemProvider
-sun.nio.fs.NativeBuffer
-sun.nio.fs.NativeBuffer$Deallocator
-sun.nio.fs.NativeBuffers
-sun.nio.fs.UnixChannelFactory
-sun.nio.fs.UnixChannelFactory$Flags
-sun.nio.fs.UnixConstants
-sun.nio.fs.UnixException
-sun.nio.fs.UnixFileAttributeViews
-sun.nio.fs.UnixFileAttributeViews$Basic
-sun.nio.fs.UnixFileAttributes
-sun.nio.fs.UnixFileAttributes$UnixAsBasicFileAttributes
-sun.nio.fs.UnixFileModeAttribute
-sun.nio.fs.UnixFileStoreAttributes
-sun.nio.fs.UnixFileSystem
-sun.nio.fs.UnixFileSystemProvider
-sun.nio.fs.UnixMountEntry
-sun.nio.fs.UnixNativeDispatcher
-sun.nio.fs.UnixPath
-sun.nio.fs.Util
-sun.reflect.misc.ReflectUtil
-sun.security.action.GetBooleanAction
-sun.security.action.GetIntegerAction
-sun.security.action.GetPropertyAction
-sun.security.jca.GetInstance
-sun.security.jca.GetInstance$Instance
-sun.security.jca.JCAUtil
-sun.security.jca.ProviderConfig
-sun.security.jca.ProviderConfig$2
-sun.security.jca.ProviderList
-sun.security.jca.ProviderList$1
-sun.security.jca.ProviderList$2
-sun.security.jca.ProviderList$3
-sun.security.jca.ProviderList$ServiceList
-sun.security.jca.ProviderList$ServiceList$1
-sun.security.jca.Providers
-sun.security.jca.ServiceId
-sun.security.pkcs.ContentInfo
-sun.security.pkcs.PKCS7
-sun.security.pkcs.PKCS7$VerbatimX509Certificate
-sun.security.pkcs.PKCS7$WrappedX509Certificate
-sun.security.pkcs.PKCS9Attribute
-sun.security.pkcs.SignerInfo
-sun.security.provider.CertPathProvider
-sun.security.provider.X509Factory
-sun.security.provider.certpath.AdaptableX509CertSelector
-sun.security.provider.certpath.AlgorithmChecker
-sun.security.provider.certpath.BasicChecker
-sun.security.provider.certpath.CertId
-sun.security.provider.certpath.CertPathHelper
-sun.security.provider.certpath.ConstraintsChecker
-sun.security.provider.certpath.KeyChecker
-sun.security.provider.certpath.OCSP$RevocationStatus
-sun.security.provider.certpath.OCSP$RevocationStatus$CertStatus
-sun.security.provider.certpath.OCSPResponse
-sun.security.provider.certpath.OCSPResponse$ResponseStatus
-sun.security.provider.certpath.OCSPResponse$SingleResponse
-sun.security.provider.certpath.PKIX
-sun.security.provider.certpath.PKIX$ValidatorParams
-sun.security.provider.certpath.PKIXCertPathValidator
-sun.security.provider.certpath.PKIXMasterCertPathValidator
-sun.security.provider.certpath.PolicyChecker
-sun.security.provider.certpath.PolicyNodeImpl
-sun.security.provider.certpath.RevocationChecker
-sun.security.provider.certpath.RevocationChecker$1
-sun.security.provider.certpath.RevocationChecker$Mode
-sun.security.provider.certpath.RevocationChecker$RevocationProperties
-sun.security.util.AbstractAlgorithmConstraints
-sun.security.util.AbstractAlgorithmConstraints$1
-sun.security.util.AlgorithmDecomposer
-sun.security.util.BitArray
-sun.security.util.ByteArrayLexOrder
-sun.security.util.ByteArrayTagOrder
-sun.security.util.Cache
-sun.security.util.Cache$EqualByteArray
-sun.security.util.CertConstraintParameters
-sun.security.util.Debug
-sun.security.util.DerEncoder
-sun.security.util.DerIndefLenConverter
-sun.security.util.DerInputBuffer
-sun.security.util.DerInputStream
-sun.security.util.DerOutputStream
-sun.security.util.DerValue
-sun.security.util.DisabledAlgorithmConstraints
-sun.security.util.DisabledAlgorithmConstraints$Constraint
-sun.security.util.DisabledAlgorithmConstraints$Constraint$Operator
-sun.security.util.DisabledAlgorithmConstraints$Constraints
-sun.security.util.DisabledAlgorithmConstraints$KeySizeConstraint
-sun.security.util.KeyUtil
-sun.security.util.Length
-sun.security.util.ManifestDigester
-sun.security.util.ManifestDigester$Entry
-sun.security.util.ManifestDigester$Position
-sun.security.util.ManifestEntryVerifier
-sun.security.util.ManifestEntryVerifier$SunProviderHolder
-sun.security.util.MemoryCache
-sun.security.util.MemoryCache$CacheEntry
-sun.security.util.MemoryCache$SoftCacheEntry
-sun.security.util.ObjectIdentifier
-sun.security.util.SignatureFileVerifier
-sun.security.x509.AVA
-sun.security.x509.AVAKeyword
-sun.security.x509.AccessDescription
-sun.security.x509.AlgorithmId
-sun.security.x509.AuthorityInfoAccessExtension
-sun.security.x509.AuthorityKeyIdentifierExtension
-sun.security.x509.BasicConstraintsExtension
-sun.security.x509.CRLDistributionPointsExtension
-sun.security.x509.CRLNumberExtension
-sun.security.x509.CRLReasonCodeExtension
-sun.security.x509.CertAttrSet
-sun.security.x509.CertificateAlgorithmId
-sun.security.x509.CertificateExtensions
-sun.security.x509.CertificateIssuerExtension
-sun.security.x509.CertificatePoliciesExtension
-sun.security.x509.CertificatePolicyId
-sun.security.x509.CertificateSerialNumber
-sun.security.x509.CertificateValidity
-sun.security.x509.CertificateVersion
-sun.security.x509.CertificateX509Key
-sun.security.x509.DNSName
-sun.security.x509.DeltaCRLIndicatorExtension
-sun.security.x509.DistributionPoint
-sun.security.x509.ExtendedKeyUsageExtension
-sun.security.x509.Extension
-sun.security.x509.FreshestCRLExtension
-sun.security.x509.GeneralName
-sun.security.x509.GeneralNameInterface
-sun.security.x509.GeneralNames
-sun.security.x509.InhibitAnyPolicyExtension
-sun.security.x509.IssuerAlternativeNameExtension
-sun.security.x509.IssuingDistributionPointExtension
-sun.security.x509.KeyIdentifier
-sun.security.x509.KeyUsageExtension
-sun.security.x509.NameConstraintsExtension
-sun.security.x509.NetscapeCertTypeExtension
-sun.security.x509.OCSPNoCheckExtension
-sun.security.x509.OIDMap
-sun.security.x509.OIDMap$OIDInfo
-sun.security.x509.PKIXExtensions
-sun.security.x509.PolicyConstraintsExtension
-sun.security.x509.PolicyInformation
-sun.security.x509.PolicyMappingsExtension
-sun.security.x509.PrivateKeyUsageExtension
-sun.security.x509.RDN
-sun.security.x509.SerialNumber
-sun.security.x509.SubjectAlternativeNameExtension
-sun.security.x509.SubjectInfoAccessExtension
-sun.security.x509.SubjectKeyIdentifierExtension
-sun.security.x509.URIName
-sun.security.x509.X500Name
-sun.security.x509.X500Name$1
-sun.security.x509.X509AttributeName
-sun.security.x509.X509CertImpl
-sun.security.x509.X509CertInfo
-sun.security.x509.X509Key
-sun.util.calendar.AbstractCalendar
-sun.util.calendar.BaseCalendar
-sun.util.calendar.BaseCalendar$Date
-sun.util.calendar.CalendarDate
-sun.util.calendar.CalendarSystem
-sun.util.calendar.CalendarUtils
-sun.util.calendar.Gregorian
-sun.util.calendar.Gregorian$Date
-sun.util.calendar.JulianCalendar
-sun.util.calendar.LocalGregorianCalendar
-sun.util.locale.BaseLocale
-sun.util.locale.BaseLocale$Cache
-sun.util.locale.BaseLocale$Key
-sun.util.locale.Extension
-sun.util.locale.InternalLocaleBuilder
-sun.util.locale.InternalLocaleBuilder$CaseInsensitiveChar
-sun.util.locale.LanguageTag
-sun.util.locale.LocaleExtensions
-sun.util.locale.LocaleObjectCache
-sun.util.locale.LocaleObjectCache$CacheEntry
-sun.util.locale.LocaleSyntaxException
-sun.util.locale.LocaleUtils
-sun.util.locale.ParseStatus
-sun.util.locale.StringTokenIterator
-sun.util.locale.UnicodeLocaleExtension
-sun.util.logging.LoggingProxy
-sun.util.logging.LoggingSupport
-sun.util.logging.LoggingSupport$1
-sun.util.logging.LoggingSupport$2
-sun.util.logging.PlatformLogger
-sun.util.logging.PlatformLogger$1
-sun.util.logging.PlatformLogger$JavaLoggerProxy
-sun.util.logging.PlatformLogger$Level
-sun.util.logging.PlatformLogger$LoggerProxy
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 8824643..97dcb90 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -391,8 +391,12 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({SHOW_MODE_AUTO, SHOW_MODE_HIDDEN})
- public @interface SoftKeyboardShowMode {};
+ @IntDef(prefix = { "SHOW_MODE_" }, value = {
+ SHOW_MODE_AUTO,
+ SHOW_MODE_HIDDEN
+ })
+ public @interface SoftKeyboardShowMode {}
+
public static final int SHOW_MODE_AUTO = 0;
public static final int SHOW_MODE_HIDDEN = 1;
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index bd9c9fa..782733f 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -290,8 +290,13 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({VISIBILITY_UNDEFINED, VISIBILITY_VISIBLE, VISIBILITY_USER_MANAGED_VISIBLE,
- VISIBILITY_NOT_VISIBLE, VISIBILITY_USER_MANAGED_NOT_VISIBLE})
+ @IntDef(prefix = { "VISIBILITY_" }, value = {
+ VISIBILITY_UNDEFINED,
+ VISIBILITY_VISIBLE,
+ VISIBILITY_USER_MANAGED_VISIBLE,
+ VISIBILITY_NOT_VISIBLE,
+ VISIBILITY_USER_MANAGED_NOT_VISIBLE
+ })
public @interface AccountVisibility {
}
diff --git a/core/java/android/annotation/CallbackExecutor.java b/core/java/android/annotation/CallbackExecutor.java
new file mode 100644
index 0000000..5671a3d
--- /dev/null
+++ b/core/java/android/annotation/CallbackExecutor.java
@@ -0,0 +1,41 @@
+/*
+ * 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.annotation;
+
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.content.Context;
+import android.os.AsyncTask;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.concurrent.Executor;
+
+/**
+ * @paramDoc Callback and listener events are dispatched through this
+ * {@link Executor}, providing an easy way to control which thread is
+ * used. To dispatch events through the main thread of your
+ * application, you can use {@link Context#getMainExecutor()}. To
+ * dispatch events through a shared thread pool, you can use
+ * {@link AsyncTask#THREAD_POOL_EXECUTOR}.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target(PARAMETER)
+public @interface CallbackExecutor {
+}
diff --git a/core/java/android/annotation/Condemned.java b/core/java/android/annotation/Condemned.java
new file mode 100644
index 0000000..186409b
--- /dev/null
+++ b/core/java/android/annotation/Condemned.java
@@ -0,0 +1,44 @@
+/*
+ * 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.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PACKAGE;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * A program element annotated @Condemned is one that programmers are
+ * blocked from using, typically because it's about to be completely destroyed.
+ * <p>
+ * This is a stronger version of @Deprecated, and it's typically used to
+ * mark APIs that only existed temporarily in a preview SDK, and which only
+ * continue to exist temporarily to support binary compatibility.
+ *
+ * @hide
+ */
+@Retention(SOURCE)
+@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
+public @interface Condemned {
+}
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 0e8326d..04ff48c 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -95,7 +95,11 @@
public abstract class ActionBar {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
+ @IntDef(prefix = { "NAVIGATION_MODE_" }, value = {
+ NAVIGATION_MODE_STANDARD,
+ NAVIGATION_MODE_LIST,
+ NAVIGATION_MODE_TABS
+ })
public @interface NavigationMode {}
/**
@@ -139,15 +143,14 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- value = {
- DISPLAY_USE_LOGO,
- DISPLAY_SHOW_HOME,
- DISPLAY_HOME_AS_UP,
- DISPLAY_SHOW_TITLE,
- DISPLAY_SHOW_CUSTOM,
- DISPLAY_TITLE_MULTIPLE_LINES
- })
+ @IntDef(flag = true, prefix = { "DISPLAY_" }, value = {
+ DISPLAY_USE_LOGO,
+ DISPLAY_SHOW_HOME,
+ DISPLAY_HOME_AS_UP,
+ DISPLAY_SHOW_TITLE,
+ DISPLAY_SHOW_CUSTOM,
+ DISPLAY_TITLE_MULTIPLE_LINES
+ })
public @interface DisplayOptions {}
/**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 142630e..1adae7a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -175,7 +175,7 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({
+ @IntDef(prefix = { "BUGREPORT_OPTION_" }, value = {
BUGREPORT_OPTION_FULL,
BUGREPORT_OPTION_INTERACTIVE,
BUGREPORT_OPTION_REMOTE,
@@ -457,6 +457,20 @@
/** @hide User operation call: one of related users cannot be stopped. */
public static final int USER_OP_ERROR_RELATED_USERS_CANNOT_STOP = -4;
+ /**
+ * @hide
+ * Process states, describing the kind of state a particular process is in.
+ * When updating these, make sure to also check all related references to the
+ * constant in code, and update these arrays:
+ *
+ * @see com.android.internal.app.procstats.ProcessState#PROCESS_STATE_TO_STATE
+ * @see com.android.server.am.ProcessList#sProcStateToProcMem
+ * @see com.android.server.am.ProcessList#sFirstAwakePssTimes
+ * @see com.android.server.am.ProcessList#sSameAwakePssTimes
+ * @see com.android.server.am.ProcessList#sTestFirstPssTimes
+ * @see com.android.server.am.ProcessList#sTestSamePssTimes
+ */
+
/** @hide Not a real process state. */
public static final int PROCESS_STATE_UNKNOWN = -1;
@@ -476,35 +490,35 @@
/** @hide Process is hosting a foreground service. */
public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4;
- /** @hide Same as {@link #PROCESS_STATE_TOP} but while device is sleeping. */
- public static final int PROCESS_STATE_TOP_SLEEPING = 5;
-
/** @hide Process is important to the user, and something they are aware of. */
- public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 6;
+ public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 5;
/** @hide Process is important to the user, but not something they are aware of. */
- public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 7;
+ public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 6;
/** @hide Process is in the background transient so we will try to keep running. */
- public static final int PROCESS_STATE_TRANSIENT_BACKGROUND = 8;
+ public static final int PROCESS_STATE_TRANSIENT_BACKGROUND = 7;
/** @hide Process is in the background running a backup/restore operation. */
- public static final int PROCESS_STATE_BACKUP = 9;
-
- /** @hide Process is in the background, but it can't restore its state so we want
- * to try to avoid killing it. */
- public static final int PROCESS_STATE_HEAVY_WEIGHT = 10;
+ public static final int PROCESS_STATE_BACKUP = 8;
/** @hide Process is in the background running a service. Unlike oom_adj, this level
* is used for both the normal running in background state and the executing
* operations state. */
- public static final int PROCESS_STATE_SERVICE = 11;
+ public static final int PROCESS_STATE_SERVICE = 9;
/** @hide Process is in the background running a receiver. Note that from the
* perspective of oom_adj, receivers run at a higher foreground level, but for our
* prioritization here that is not necessary and putting them below services means
* many fewer changes in some process states as they receive broadcasts. */
- public static final int PROCESS_STATE_RECEIVER = 12;
+ public static final int PROCESS_STATE_RECEIVER = 10;
+
+ /** @hide Same as {@link #PROCESS_STATE_TOP} but while device is sleeping. */
+ public static final int PROCESS_STATE_TOP_SLEEPING = 11;
+
+ /** @hide Process is in the background, but it can't restore its state so we want
+ * to try to avoid killing it. */
+ public static final int PROCESS_STATE_HEAVY_WEIGHT = 12;
/** @hide Process is in the background but hosts the home activity. */
public static final int PROCESS_STATE_HOME = 13;
@@ -533,10 +547,10 @@
// to frameworks/base/core/proto/android/app/activitymanager.proto and the following method must
// be updated to correctly map between them.
/**
- * Maps ActivityManager.PROCESS_STATE_ values to ActivityManagerProto.ProcessState enum.
+ * Maps ActivityManager.PROCESS_STATE_ values to ProcessState enum.
*
* @param amInt a process state of the form ActivityManager.PROCESS_STATE_
- * @return the value of the corresponding android.app.ActivityManagerProto's ProcessState enum.
+ * @return the value of the corresponding ActivityManager's ProcessState enum.
* @hide
*/
public static final int processStateAmToProto(int amInt) {
@@ -810,7 +824,7 @@
* impose on your application to let the overall system work best. The
* returned value is in megabytes; the baseline Android memory class is
* 16 (which happens to be the Java heap limit of those devices); some
- * device with more memory may return 24 or even higher numbers.
+ * devices with more memory may return 24 or even higher numbers.
*/
public int getMemoryClass() {
return staticGetMemoryClass();
@@ -837,7 +851,7 @@
* constrained devices, or it may be significantly larger on devices with
* a large amount of available RAM.
*
- * <p>The is the size of the application's Dalvik heap if it has
+ * <p>This is the size of the application's Dalvik heap if it has
* specified <code>android:largeHeap="true"</code> in its manifest.
*/
public int getLargeMemoryClass() {
@@ -2613,7 +2627,7 @@
Manifest.permission.ACCESS_INSTANT_APPS})
public boolean clearApplicationUserData(String packageName, IPackageDataObserver observer) {
try {
- return getService().clearApplicationUserData(packageName,
+ return getService().clearApplicationUserData(packageName, false,
observer, UserHandle.myUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -2884,13 +2898,13 @@
public static final int IMPORTANCE_FOREGROUND_SERVICE = 125;
/**
- * Constant for {@link #importance}: This process is running the foreground
- * UI, but the device is asleep so it is not visible to the user. This means
- * the user is not really aware of the process, because they can not see or
- * interact with it, but it is quite important because it what they expect to
- * return to once unlocking the device.
+ * @deprecated Pre-{@link android.os.Build.VERSION_CODES#P} version of
+ * {@link #IMPORTANCE_TOP_SLEEPING}. As of Android
+ * {@link android.os.Build.VERSION_CODES#P}, this is considered much less
+ * important since we want to reduce what apps can do when the screen is off.
*/
- public static final int IMPORTANCE_TOP_SLEEPING = 150;
+ @Deprecated
+ public static final int IMPORTANCE_TOP_SLEEPING_PRE_28 = 150;
/**
* Constant for {@link #importance}: This process is running something
@@ -2942,14 +2956,6 @@
public static final int IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170;
/**
- * Constant for {@link #importance}: This process is running an
- * application that can not save its state, and thus can't be killed
- * while in the background. This will be used with apps that have
- * {@link android.R.attr#cantSaveState} set on their application tag.
- */
- public static final int IMPORTANCE_CANT_SAVE_STATE = 270;
-
- /**
* Constant for {@link #importance}: This process is contains services
* that should remain running. These are background services apps have
* started, not something the user is aware of, so they may be killed by
@@ -2959,6 +2965,23 @@
public static final int IMPORTANCE_SERVICE = 300;
/**
+ * Constant for {@link #importance}: This process is running the foreground
+ * UI, but the device is asleep so it is not visible to the user. Though the
+ * system will try hard to keep its process from being killed, in all other
+ * ways we consider it a kind of cached process, with the limitations that go
+ * along with that state: network access, running background services, etc.
+ */
+ public static final int IMPORTANCE_TOP_SLEEPING = 325;
+
+ /**
+ * Constant for {@link #importance}: This process is running an
+ * application that can not save its state, and thus can't be killed
+ * while in the background. This will be used with apps that have
+ * {@link android.R.attr#cantSaveState} set on their application tag.
+ */
+ public static final int IMPORTANCE_CANT_SAVE_STATE = 350;
+
+ /**
* Constant for {@link #importance}: This process process contains
* cached code that is expendable, not actively running any app components
* we care about.
@@ -2993,16 +3016,16 @@
return IMPORTANCE_GONE;
} else if (procState >= PROCESS_STATE_HOME) {
return IMPORTANCE_CACHED;
- } else if (procState >= PROCESS_STATE_SERVICE) {
- return IMPORTANCE_SERVICE;
} else if (procState == PROCESS_STATE_HEAVY_WEIGHT) {
return IMPORTANCE_CANT_SAVE_STATE;
+ } else if (procState >= PROCESS_STATE_TOP_SLEEPING) {
+ return IMPORTANCE_TOP_SLEEPING;
+ } else if (procState >= PROCESS_STATE_SERVICE) {
+ return IMPORTANCE_SERVICE;
} else if (procState >= PROCESS_STATE_TRANSIENT_BACKGROUND) {
return IMPORTANCE_PERCEPTIBLE;
} else if (procState >= PROCESS_STATE_IMPORTANT_FOREGROUND) {
return IMPORTANCE_VISIBLE;
- } else if (procState >= PROCESS_STATE_TOP_SLEEPING) {
- return IMPORTANCE_TOP_SLEEPING;
} else if (procState >= PROCESS_STATE_FOREGROUND_SERVICE) {
return IMPORTANCE_FOREGROUND_SERVICE;
} else {
@@ -3036,6 +3059,8 @@
switch (importance) {
case IMPORTANCE_PERCEPTIBLE:
return IMPORTANCE_PERCEPTIBLE_PRE_26;
+ case IMPORTANCE_TOP_SLEEPING:
+ return IMPORTANCE_TOP_SLEEPING_PRE_28;
case IMPORTANCE_CANT_SAVE_STATE:
return IMPORTANCE_CANT_SAVE_STATE_PRE_26;
}
@@ -3049,16 +3074,18 @@
return PROCESS_STATE_NONEXISTENT;
} else if (importance >= IMPORTANCE_CACHED) {
return PROCESS_STATE_HOME;
+ } else if (importance >= IMPORTANCE_CANT_SAVE_STATE) {
+ return PROCESS_STATE_HEAVY_WEIGHT;
+ } else if (importance >= IMPORTANCE_TOP_SLEEPING) {
+ return PROCESS_STATE_TOP_SLEEPING;
} else if (importance >= IMPORTANCE_SERVICE) {
return PROCESS_STATE_SERVICE;
- } else if (importance == IMPORTANCE_CANT_SAVE_STATE) {
- return PROCESS_STATE_HEAVY_WEIGHT;
} else if (importance >= IMPORTANCE_PERCEPTIBLE) {
return PROCESS_STATE_TRANSIENT_BACKGROUND;
} else if (importance >= IMPORTANCE_VISIBLE) {
return PROCESS_STATE_IMPORTANT_FOREGROUND;
- } else if (importance >= IMPORTANCE_TOP_SLEEPING) {
- return PROCESS_STATE_TOP_SLEEPING;
+ } else if (importance >= IMPORTANCE_TOP_SLEEPING_PRE_28) {
+ return PROCESS_STATE_FOREGROUND_SERVICE;
} else if (importance >= IMPORTANCE_FOREGROUND_SERVICE) {
return PROCESS_STATE_FOREGROUND_SERVICE;
} else {
@@ -3837,7 +3864,7 @@
pw.println();
dumpService(pw, fd, ProcessStats.SERVICE_NAME, new String[] { packageName });
pw.println();
- dumpService(pw, fd, "usagestats", new String[] { "--packages", packageName });
+ dumpService(pw, fd, "usagestats", new String[] { packageName });
pw.println();
dumpService(pw, fd, BatteryStats.SERVICE_NAME, new String[] { packageName });
pw.flush();
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index d7efa91..60a5a11 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -99,7 +99,10 @@
// Called by the power manager.
public abstract void onWakefulnessChanged(int wakefulness);
- public abstract int startIsolatedProcess(String entryPoint, String[] mainArgs,
+ /**
+ * @return {@code true} if process start is successful, {@code false} otherwise.
+ */
+ public abstract boolean startIsolatedProcess(String entryPoint, String[] mainArgs,
String processName, String abiOverride, int uid, Runnable crashHandler);
/**
@@ -261,6 +264,11 @@
public abstract void notifyNetworkPolicyRulesUpdated(int uid, long procStateSeq);
/**
+ * Called after the voice interaction service has changed.
+ */
+ public abstract void notifyActiveVoiceInteractionServiceChanged(ComponentName component);
+
+ /**
* Called after virtual display Id is updated by
* {@link com.android.server.vr.Vr2dDisplay} with a specific
* {@param vr2dDisplayId}.
@@ -299,4 +307,16 @@
* @return true if runtime was restarted, false if it's normal boot
*/
public abstract boolean isRuntimeRestarted();
+
+ /**
+ * Returns {@code true} if {@code uid} is running an activity from {@code packageName}.
+ */
+ public abstract boolean hasRunningActivity(int uid, @Nullable String packageName);
+
+ public interface ScreenObserver {
+ public void onAwakeStateChanged(boolean isAwake);
+ public void onKeyguardStateChanged(boolean isShowing);
+ }
+
+ public abstract void registerScreenObserver(ScreenObserver observer);
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5369adf..de346f3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -80,6 +80,7 @@
import android.os.Environment;
import android.os.GraphicsEnvironment;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.LocaleList;
import android.os.Looper;
@@ -172,6 +173,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;
+import java.util.concurrent.Executor;
final class RemoteServiceException extends AndroidRuntimeException {
public RemoteServiceException(String msg) {
@@ -223,6 +225,12 @@
*/
public static final long INVALID_PROC_STATE_SEQ = -1;
+ /**
+ * Identifier for the sequence no. associated with this process start. It will be provided
+ * as one of the arguments when the process starts.
+ */
+ public static final String PROC_START_SEQ_IDENT = "seq=";
+
private final Object mNetworkPolicyLock = new Object();
/**
@@ -242,6 +250,7 @@
final ApplicationThread mAppThread = new ApplicationThread();
final Looper mLooper = Looper.myLooper();
final H mH = new H();
+ final Executor mExecutor = new HandlerExecutor(mH);
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
// List of new activities (via ActivityRecord.nextIdle) that should
// be reported when next we idle.
@@ -374,7 +383,7 @@
ActivityInfo activityInfo;
CompatibilityInfo compatInfo;
- public LoadedApk packageInfo;
+ public LoadedApk loadedApk;
List<ResultInfo> pendingResults;
List<ReferrerIntent> pendingIntents;
@@ -417,7 +426,7 @@
this.isForward = isForward;
this.profilerInfo = profilerInfo;
this.overrideConfig = overrideConfig;
- this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo,
+ this.loadedApk = client.getLoadedApkNoCheck(activityInfo.applicationInfo,
compatInfo);
init();
}
@@ -599,7 +608,7 @@
}
static final class AppBindData {
- LoadedApk info;
+ LoadedApk loadedApk;
String processName;
ApplicationInfo appInfo;
List<ProviderInfo> providers;
@@ -1143,7 +1152,7 @@
int N = stats.dbStats.size();
if (N > 0) {
pw.println(" DATABASES");
- printRow(pw, " %8s %8s %14s %14s %s", "pgsz", "dbsz", "Lookaside(b)", "cache",
+ printRow(pw, DB_INFO_FORMAT, "pgsz", "dbsz", "Lookaside(b)", "cache",
"Dbname");
for (int i = 0; i < N; i++) {
DbStats dbStats = stats.dbStats.get(i);
@@ -1175,6 +1184,124 @@
}
@Override
+ public void dumpMemInfoProto(ParcelFileDescriptor pfd, Debug.MemoryInfo mem,
+ boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
+ boolean dumpUnreachable, String[] args) {
+ ProtoOutputStream proto = new ProtoOutputStream(pfd.getFileDescriptor());
+ try {
+ dumpMemInfo(proto, mem, dumpFullInfo, dumpDalvik, dumpSummaryOnly, dumpUnreachable);
+ } finally {
+ proto.flush();
+ IoUtils.closeQuietly(pfd);
+ }
+ }
+
+ private void dumpMemInfo(ProtoOutputStream proto, Debug.MemoryInfo memInfo,
+ boolean dumpFullInfo, boolean dumpDalvik,
+ boolean dumpSummaryOnly, boolean dumpUnreachable) {
+ long nativeMax = Debug.getNativeHeapSize() / 1024;
+ long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
+ long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
+
+ Runtime runtime = Runtime.getRuntime();
+ runtime.gc(); // Do GC since countInstancesOfClass counts unreachable objects.
+ long dalvikMax = runtime.totalMemory() / 1024;
+ long dalvikFree = runtime.freeMemory() / 1024;
+ long dalvikAllocated = dalvikMax - dalvikFree;
+
+ Class[] classesToCount = new Class[] {
+ ContextImpl.class,
+ Activity.class,
+ WebView.class,
+ OpenSSLSocketImpl.class
+ };
+ long[] instanceCounts = VMDebug.countInstancesOfClasses(classesToCount, true);
+ long appContextInstanceCount = instanceCounts[0];
+ long activityInstanceCount = instanceCounts[1];
+ long webviewInstanceCount = instanceCounts[2];
+ long openSslSocketCount = instanceCounts[3];
+
+ long viewInstanceCount = ViewDebug.getViewInstanceCount();
+ long viewRootInstanceCount = ViewDebug.getViewRootImplCount();
+ int globalAssetCount = AssetManager.getGlobalAssetCount();
+ int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount();
+ int binderLocalObjectCount = Debug.getBinderLocalObjectCount();
+ int binderProxyObjectCount = Debug.getBinderProxyObjectCount();
+ int binderDeathObjectCount = Debug.getBinderDeathObjectCount();
+ long parcelSize = Parcel.getGlobalAllocSize();
+ long parcelCount = Parcel.getGlobalAllocCount();
+ SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo();
+
+ final long mToken = proto.start(MemInfoProto.AppData.PROCESS_MEMORY);
+ proto.write(MemInfoProto.ProcessMemory.PID, Process.myPid());
+ proto.write(MemInfoProto.ProcessMemory.PROCESS_NAME,
+ (mBoundApplication != null) ? mBoundApplication.processName : "unknown");
+ dumpMemInfoTable(proto, memInfo, dumpDalvik, dumpSummaryOnly,
+ nativeMax, nativeAllocated, nativeFree,
+ dalvikMax, dalvikAllocated, dalvikFree);
+ proto.end(mToken);
+
+ final long oToken = proto.start(MemInfoProto.AppData.OBJECTS);
+ proto.write(MemInfoProto.AppData.ObjectStats.VIEW_INSTANCE_COUNT, viewInstanceCount);
+ proto.write(MemInfoProto.AppData.ObjectStats.VIEW_ROOT_INSTANCE_COUNT,
+ viewRootInstanceCount);
+ proto.write(MemInfoProto.AppData.ObjectStats.APP_CONTEXT_INSTANCE_COUNT,
+ appContextInstanceCount);
+ proto.write(MemInfoProto.AppData.ObjectStats.ACTIVITY_INSTANCE_COUNT,
+ activityInstanceCount);
+ proto.write(MemInfoProto.AppData.ObjectStats.GLOBAL_ASSET_COUNT, globalAssetCount);
+ proto.write(MemInfoProto.AppData.ObjectStats.GLOBAL_ASSET_MANAGER_COUNT,
+ globalAssetManagerCount);
+ proto.write(MemInfoProto.AppData.ObjectStats.LOCAL_BINDER_OBJECT_COUNT,
+ binderLocalObjectCount);
+ proto.write(MemInfoProto.AppData.ObjectStats.PROXY_BINDER_OBJECT_COUNT,
+ binderProxyObjectCount);
+ proto.write(MemInfoProto.AppData.ObjectStats.PARCEL_MEMORY_KB, parcelSize / 1024);
+ proto.write(MemInfoProto.AppData.ObjectStats.PARCEL_COUNT, parcelCount);
+ proto.write(MemInfoProto.AppData.ObjectStats.BINDER_OBJECT_DEATH_COUNT,
+ binderDeathObjectCount);
+ proto.write(MemInfoProto.AppData.ObjectStats.OPEN_SSL_SOCKET_COUNT, openSslSocketCount);
+ proto.write(MemInfoProto.AppData.ObjectStats.WEBVIEW_INSTANCE_COUNT,
+ webviewInstanceCount);
+ proto.end(oToken);
+
+ // SQLite mem info
+ final long sToken = proto.start(MemInfoProto.AppData.SQL);
+ proto.write(MemInfoProto.AppData.SqlStats.MEMORY_USED_KB, stats.memoryUsed / 1024);
+ proto.write(MemInfoProto.AppData.SqlStats.PAGECACHE_OVERFLOW_KB,
+ stats.pageCacheOverflow / 1024);
+ proto.write(MemInfoProto.AppData.SqlStats.MALLOC_SIZE_KB, stats.largestMemAlloc / 1024);
+ int n = stats.dbStats.size();
+ for (int i = 0; i < n; i++) {
+ DbStats dbStats = stats.dbStats.get(i);
+
+ final long dToken = proto.start(MemInfoProto.AppData.SqlStats.DATABASES);
+ proto.write(MemInfoProto.AppData.SqlStats.Database.NAME, dbStats.dbName);
+ proto.write(MemInfoProto.AppData.SqlStats.Database.PAGE_SIZE, dbStats.pageSize);
+ proto.write(MemInfoProto.AppData.SqlStats.Database.DB_SIZE, dbStats.dbSize);
+ proto.write(MemInfoProto.AppData.SqlStats.Database.LOOKASIDE_B, dbStats.lookaside);
+ proto.write(MemInfoProto.AppData.SqlStats.Database.CACHE, dbStats.cache);
+ proto.end(dToken);
+ }
+ proto.end(sToken);
+
+ // Asset details.
+ String assetAlloc = AssetManager.getAssetAllocations();
+ if (assetAlloc != null) {
+ proto.write(MemInfoProto.AppData.ASSET_ALLOCATIONS, assetAlloc);
+ }
+
+ // Unreachable native memory
+ if (dumpUnreachable) {
+ int flags = mBoundApplication == null ? 0 : mBoundApplication.appInfo.flags;
+ boolean showContents = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0
+ || android.os.Build.IS_DEBUGGABLE;
+ proto.write(MemInfoProto.AppData.UNREACHABLE_MEMORY,
+ Debug.getUnreachableMemory(100, showContents));
+ }
+ }
+
+ @Override
public void dumpGfxInfo(ParcelFileDescriptor pfd, String[] args) {
nDumpGraphicsInfo(pfd.getFileDescriptor());
WindowManagerGlobal.getInstance().dumpGfxInfo(pfd.getFileDescriptor(), args);
@@ -1771,13 +1898,13 @@
return mH;
}
- public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
- int flags) {
- return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId());
+ public final LoadedApk getLoadedApkForPackageName(String packageName,
+ CompatibilityInfo compatInfo, int flags) {
+ return getLoadedApkForPackageName(packageName, compatInfo, flags, UserHandle.myUserId());
}
- public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
- int flags, int userId) {
+ public final LoadedApk getLoadedApkForPackageName(String packageName,
+ CompatibilityInfo compatInfo, int flags, int userId) {
final boolean differentUser = (UserHandle.myUserId() != userId);
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
@@ -1790,13 +1917,13 @@
ref = mResourcePackages.get(packageName);
}
- LoadedApk packageInfo = ref != null ? ref.get() : null;
- //Slog.i(TAG, "getPackageInfo " + packageName + ": " + packageInfo);
- //if (packageInfo != null) Slog.i(TAG, "isUptoDate " + packageInfo.mResDir
- // + ": " + packageInfo.mResources.getAssets().isUpToDate());
- if (packageInfo != null && (packageInfo.mResources == null
- || packageInfo.mResources.getAssets().isUpToDate())) {
- if (packageInfo.isSecurityViolation()
+ LoadedApk loadedApk = ref != null ? ref.get() : null;
+ //Slog.i(TAG, "getLoadedApkForPackageName " + packageName + ": " + loadedApk);
+ //if (loadedApk != null) Slog.i(TAG, "isUptoDate " + loadedApk.mResDir
+ // + ": " + loadedApk.mResources.getAssets().isUpToDate());
+ if (loadedApk != null && (loadedApk.mResources == null
+ || loadedApk.mResources.getAssets().isUpToDate())) {
+ if (loadedApk.isSecurityViolation()
&& (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) {
throw new SecurityException(
"Requesting code from " + packageName
@@ -1804,7 +1931,7 @@
+ mBoundApplication.processName
+ "/" + mBoundApplication.appInfo.uid);
}
- return packageInfo;
+ return loadedApk;
}
}
@@ -1819,13 +1946,13 @@
}
if (ai != null) {
- return getPackageInfo(ai, compatInfo, flags);
+ return getLoadedApk(ai, compatInfo, flags);
}
return null;
}
- public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo,
+ public final LoadedApk getLoadedApk(ApplicationInfo ai, CompatibilityInfo compatInfo,
int flags) {
boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
boolean securityViolation = includeCode && ai.uid != 0
@@ -1847,17 +1974,17 @@
throw new SecurityException(msg);
}
}
- return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode,
+ return getLoadedApk(ai, compatInfo, null, securityViolation, includeCode,
registerPackage);
}
@Override
- public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
+ public final LoadedApk getLoadedApkNoCheck(ApplicationInfo ai,
CompatibilityInfo compatInfo) {
- return getPackageInfo(ai, compatInfo, null, false, true, false);
+ return getLoadedApk(ai, compatInfo, null, false, true, false);
}
- public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) {
+ public final LoadedApk peekLoadedApk(String packageName, boolean includeCode) {
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
if (includeCode) {
@@ -1869,7 +1996,7 @@
}
}
- private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
+ private LoadedApk getLoadedApk(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
boolean registerPackage) {
final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
@@ -1884,35 +2011,35 @@
ref = mResourcePackages.get(aInfo.packageName);
}
- LoadedApk packageInfo = ref != null ? ref.get() : null;
- if (packageInfo == null || (packageInfo.mResources != null
- && !packageInfo.mResources.getAssets().isUpToDate())) {
+ LoadedApk loadedApk = ref != null ? ref.get() : null;
+ if (loadedApk == null || (loadedApk.mResources != null
+ && !loadedApk.mResources.getAssets().isUpToDate())) {
if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
: "Loading resource-only package ") + aInfo.packageName
+ " (in " + (mBoundApplication != null
? mBoundApplication.processName : null)
+ ")");
- packageInfo =
+ loadedApk =
new LoadedApk(this, aInfo, compatInfo, baseLoader,
securityViolation, includeCode &&
(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
if (mSystemThread && "android".equals(aInfo.packageName)) {
- packageInfo.installSystemApplicationInfo(aInfo,
- getSystemContext().mPackageInfo.getClassLoader());
+ loadedApk.installSystemApplicationInfo(aInfo,
+ getSystemContext().mLoadedApk.getClassLoader());
}
if (differentUser) {
// Caching not supported across users
} else if (includeCode) {
mPackages.put(aInfo.packageName,
- new WeakReference<LoadedApk>(packageInfo));
+ new WeakReference<LoadedApk>(loadedApk));
} else {
mResourcePackages.put(aInfo.packageName,
- new WeakReference<LoadedApk>(packageInfo));
+ new WeakReference<LoadedApk>(loadedApk));
}
}
- return packageInfo;
+ return loadedApk;
}
}
@@ -1943,6 +2070,10 @@
return mLooper;
}
+ public Executor getExecutor() {
+ return mExecutor;
+ }
+
public Application getApplication() {
return mInitialApplication;
}
@@ -2322,23 +2453,23 @@
*
* @param hasSwappedOutPss determines whether to use dirtySwap or dirtySwapPss
*/
- private static void dumpHeap(ProtoOutputStream proto, long fieldId, String name,
+ private static void dumpMemoryInfo(ProtoOutputStream proto, long fieldId, String name,
int pss, int cleanPss, int sharedDirty, int privateDirty,
int sharedClean, int privateClean,
boolean hasSwappedOutPss, int dirtySwap, int dirtySwapPss) {
final long token = proto.start(fieldId);
- proto.write(MemInfoProto.NativeProcess.MemoryInfo.NAME, name);
- proto.write(MemInfoProto.NativeProcess.MemoryInfo.TOTAL_PSS_KB, pss);
- proto.write(MemInfoProto.NativeProcess.MemoryInfo.CLEAN_PSS_KB, cleanPss);
- proto.write(MemInfoProto.NativeProcess.MemoryInfo.SHARED_DIRTY_KB, sharedDirty);
- proto.write(MemInfoProto.NativeProcess.MemoryInfo.PRIVATE_DIRTY_KB, privateDirty);
- proto.write(MemInfoProto.NativeProcess.MemoryInfo.SHARED_CLEAN_KB, sharedClean);
- proto.write(MemInfoProto.NativeProcess.MemoryInfo.PRIVATE_CLEAN_KB, privateClean);
+ proto.write(MemInfoProto.ProcessMemory.MemoryInfo.NAME, name);
+ proto.write(MemInfoProto.ProcessMemory.MemoryInfo.TOTAL_PSS_KB, pss);
+ proto.write(MemInfoProto.ProcessMemory.MemoryInfo.CLEAN_PSS_KB, cleanPss);
+ proto.write(MemInfoProto.ProcessMemory.MemoryInfo.SHARED_DIRTY_KB, sharedDirty);
+ proto.write(MemInfoProto.ProcessMemory.MemoryInfo.PRIVATE_DIRTY_KB, privateDirty);
+ proto.write(MemInfoProto.ProcessMemory.MemoryInfo.SHARED_CLEAN_KB, sharedClean);
+ proto.write(MemInfoProto.ProcessMemory.MemoryInfo.PRIVATE_CLEAN_KB, privateClean);
if (hasSwappedOutPss) {
- proto.write(MemInfoProto.NativeProcess.MemoryInfo.DIRTY_SWAP_PSS_KB, dirtySwapPss);
+ proto.write(MemInfoProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_PSS_KB, dirtySwapPss);
} else {
- proto.write(MemInfoProto.NativeProcess.MemoryInfo.DIRTY_SWAP_KB, dirtySwap);
+ proto.write(MemInfoProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_KB, dirtySwap);
}
proto.end(token);
@@ -2353,26 +2484,26 @@
long dalvikMax, long dalvikAllocated, long dalvikFree) {
if (!dumpSummaryOnly) {
- final long nhToken = proto.start(MemInfoProto.NativeProcess.NATIVE_HEAP);
- dumpHeap(proto, MemInfoProto.NativeProcess.HeapInfo.MEM_INFO, "Native Heap",
+ final long nhToken = proto.start(MemInfoProto.ProcessMemory.NATIVE_HEAP);
+ dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.HeapInfo.MEM_INFO, "Native Heap",
memInfo.nativePss, memInfo.nativeSwappablePss, memInfo.nativeSharedDirty,
memInfo.nativePrivateDirty, memInfo.nativeSharedClean,
memInfo.nativePrivateClean, memInfo.hasSwappedOutPss,
memInfo.nativeSwappedOut, memInfo.nativeSwappedOutPss);
- proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_SIZE_KB, nativeMax);
- proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_ALLOC_KB, nativeAllocated);
- proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_FREE_KB, nativeFree);
+ proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, nativeMax);
+ proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, nativeAllocated);
+ proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, nativeFree);
proto.end(nhToken);
- final long dvToken = proto.start(MemInfoProto.NativeProcess.DALVIK_HEAP);
- dumpHeap(proto, MemInfoProto.NativeProcess.HeapInfo.MEM_INFO, "Dalvik Heap",
+ final long dvToken = proto.start(MemInfoProto.ProcessMemory.DALVIK_HEAP);
+ dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.HeapInfo.MEM_INFO, "Dalvik Heap",
memInfo.dalvikPss, memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty,
memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean,
memInfo.dalvikPrivateClean, memInfo.hasSwappedOutPss,
memInfo.dalvikSwappedOut, memInfo.dalvikSwappedOutPss);
- proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_SIZE_KB, dalvikMax);
- proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_ALLOC_KB, dalvikAllocated);
- proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_FREE_KB, dalvikFree);
+ proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, dalvikMax);
+ proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, dalvikAllocated);
+ proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, dalvikFree);
proto.end(dvToken);
int otherPss = memInfo.otherPss;
@@ -2396,7 +2527,7 @@
if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
|| mySharedClean != 0 || myPrivateClean != 0
|| (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
- dumpHeap(proto, MemInfoProto.NativeProcess.OTHER_HEAPS,
+ dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.OTHER_HEAPS,
Debug.MemoryInfo.getOtherLabel(i),
myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
mySharedClean, myPrivateClean,
@@ -2413,21 +2544,21 @@
}
}
- dumpHeap(proto, MemInfoProto.NativeProcess.UNKNOWN_HEAP, "Unknown",
+ dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.UNKNOWN_HEAP, "Unknown",
otherPss, otherSwappablePss,
otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean,
memInfo.hasSwappedOutPss, otherSwappedOut, otherSwappedOutPss);
- final long tToken = proto.start(MemInfoProto.NativeProcess.TOTAL_HEAP);
- dumpHeap(proto, MemInfoProto.NativeProcess.HeapInfo.MEM_INFO, "TOTAL",
+ final long tToken = proto.start(MemInfoProto.ProcessMemory.TOTAL_HEAP);
+ dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.HeapInfo.MEM_INFO, "TOTAL",
memInfo.getTotalPss(), memInfo.getTotalSwappablePss(),
memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(),
memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(),
memInfo.hasSwappedOutPss, memInfo.getTotalSwappedOut(),
memInfo.getTotalSwappedOutPss());
- proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_SIZE_KB, nativeMax + dalvikMax);
- proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_ALLOC_KB,
+ proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, nativeMax + dalvikMax);
+ proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB,
nativeAllocated + dalvikAllocated);
- proto.write(MemInfoProto.NativeProcess.HeapInfo.HEAP_FREE_KB, nativeFree + dalvikFree);
+ proto.write(MemInfoProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, nativeFree + dalvikFree);
proto.end(tToken);
if (dumpDalvik) {
@@ -2445,7 +2576,7 @@
if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
|| mySharedClean != 0 || myPrivateClean != 0
|| (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
- dumpHeap(proto, MemInfoProto.NativeProcess.DALVIK_DETAILS,
+ dumpMemoryInfo(proto, MemInfoProto.ProcessMemory.DALVIK_DETAILS,
Debug.MemoryInfo.getOtherLabel(i),
myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
mySharedClean, myPrivateClean,
@@ -2455,24 +2586,24 @@
}
}
- final long asToken = proto.start(MemInfoProto.NativeProcess.APP_SUMMARY);
- proto.write(MemInfoProto.NativeProcess.AppSummary.JAVA_HEAP_PSS_KB,
+ final long asToken = proto.start(MemInfoProto.ProcessMemory.APP_SUMMARY);
+ proto.write(MemInfoProto.ProcessMemory.AppSummary.JAVA_HEAP_PSS_KB,
memInfo.getSummaryJavaHeap());
- proto.write(MemInfoProto.NativeProcess.AppSummary.NATIVE_HEAP_PSS_KB,
+ proto.write(MemInfoProto.ProcessMemory.AppSummary.NATIVE_HEAP_PSS_KB,
memInfo.getSummaryNativeHeap());
- proto.write(MemInfoProto.NativeProcess.AppSummary.CODE_PSS_KB, memInfo.getSummaryCode());
- proto.write(MemInfoProto.NativeProcess.AppSummary.STACK_PSS_KB, memInfo.getSummaryStack());
- proto.write(MemInfoProto.NativeProcess.AppSummary.GRAPHICS_PSS_KB,
+ proto.write(MemInfoProto.ProcessMemory.AppSummary.CODE_PSS_KB, memInfo.getSummaryCode());
+ proto.write(MemInfoProto.ProcessMemory.AppSummary.STACK_PSS_KB, memInfo.getSummaryStack());
+ proto.write(MemInfoProto.ProcessMemory.AppSummary.GRAPHICS_PSS_KB,
memInfo.getSummaryGraphics());
- proto.write(MemInfoProto.NativeProcess.AppSummary.PRIVATE_OTHER_PSS_KB,
+ proto.write(MemInfoProto.ProcessMemory.AppSummary.PRIVATE_OTHER_PSS_KB,
memInfo.getSummaryPrivateOther());
- proto.write(MemInfoProto.NativeProcess.AppSummary.SYSTEM_PSS_KB,
+ proto.write(MemInfoProto.ProcessMemory.AppSummary.SYSTEM_PSS_KB,
memInfo.getSummarySystem());
if (memInfo.hasSwappedOutPss) {
- proto.write(MemInfoProto.NativeProcess.AppSummary.TOTAL_SWAP_PSS,
+ proto.write(MemInfoProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS,
memInfo.getSummaryTotalSwapPss());
} else {
- proto.write(MemInfoProto.NativeProcess.AppSummary.TOTAL_SWAP_PSS,
+ proto.write(MemInfoProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS,
memInfo.getSummaryTotalSwap());
}
proto.end(asToken);
@@ -2620,8 +2751,8 @@
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r) {
ActivityInfo aInfo = r.activityInfo;
- if (r.packageInfo == null) {
- r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
+ if (r.loadedApk == null) {
+ r.loadedApk = getLoadedApk(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
@@ -2658,15 +2789,15 @@
}
try {
- Application app = r.packageInfo.makeApplication(false, mInstrumentation);
+ Application app = r.loadedApk.makeApplication(false, mInstrumentation);
if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
- + ", pkg=" + r.packageInfo.getPackageName()
+ + ", pkg=" + r.loadedApk.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
- + ", dir=" + r.packageInfo.getAppDir());
+ + ", dir=" + r.loadedApk.getAppDir());
if (activity != null) {
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
@@ -2806,7 +2937,7 @@
}
ContextImpl appContext = ContextImpl.createActivityContext(
- this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
+ this, r.loadedApk, r.activityInfo, r.token, displayId, r.overrideConfig);
final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
// For debugging purposes, if the activity's package name contains the value of
@@ -2814,7 +2945,7 @@
// its content on a secondary display if there is one.
String pkgName = SystemProperties.get("debug.second-display.pkg");
if (pkgName != null && !pkgName.isEmpty()
- && r.packageInfo.mPackageName.contains(pkgName)) {
+ && r.loadedApk.mPackageName.contains(pkgName)) {
for (int id : dm.getDisplayIds()) {
if (id != Display.DEFAULT_DISPLAY) {
Display display =
@@ -3134,7 +3265,7 @@
String component = data.intent.getComponent().getClassName();
- LoadedApk packageInfo = getPackageInfoNoCheck(
+ LoadedApk loadedApk = getLoadedApkNoCheck(
data.info.applicationInfo, data.compatInfo);
IActivityManager mgr = ActivityManager.getService();
@@ -3143,7 +3274,7 @@
BroadcastReceiver receiver;
ContextImpl context;
try {
- app = packageInfo.makeApplication(false, mInstrumentation);
+ app = loadedApk.makeApplication(false, mInstrumentation);
context = (ContextImpl) app.getBaseContext();
if (data.info.splitName != null) {
context = (ContextImpl) context.createContextForSplit(data.info.splitName);
@@ -3152,7 +3283,8 @@
data.intent.setExtrasClassLoader(cl);
data.intent.prepareToEnterProcess();
data.setExtrasClassLoader(cl);
- receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
+ receiver = loadedApk.getAppFactory()
+ .instantiateReceiver(cl, data.info.name, data.intent);
} catch (Exception e) {
if (DEBUG_BROADCAST) Slog.i(TAG,
"Finishing failed broadcast to " + data.intent.getComponent());
@@ -3167,9 +3299,9 @@
TAG, "Performing receive of " + data.intent
+ ": app=" + app
+ ", appName=" + app.getPackageName()
- + ", pkg=" + packageInfo.getPackageName()
+ + ", pkg=" + loadedApk.getPackageName()
+ ", comp=" + data.intent.getComponent().toShortString()
- + ", dir=" + packageInfo.getAppDir());
+ + ", dir=" + loadedApk.getAppDir());
sCurrentBroadcastIntent.set(data.intent);
receiver.setPendingResult(data);
@@ -3214,8 +3346,8 @@
unscheduleGcIdler();
// instantiate the BackupAgent class named in the manifest
- LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
- String packageName = packageInfo.mPackageName;
+ LoadedApk loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
+ String packageName = loadedApk.mPackageName;
if (packageName == null) {
Slog.d(TAG, "Asked to create backup agent for nonexistent package");
return;
@@ -3241,11 +3373,11 @@
try {
if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
- java.lang.ClassLoader cl = packageInfo.getClassLoader();
+ java.lang.ClassLoader cl = loadedApk.getClassLoader();
agent = (BackupAgent) cl.loadClass(classname).newInstance();
// set up the agent's context
- ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
+ ContextImpl context = ContextImpl.createAppContext(this, loadedApk);
context.setOuterContext(agent);
agent.attach(context);
@@ -3281,8 +3413,8 @@
private void handleDestroyBackupAgent(CreateBackupAgentData data) {
if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data);
- LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
- String packageName = packageInfo.mPackageName;
+ LoadedApk loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
+ String packageName = loadedApk.mPackageName;
BackupAgent agent = mBackupAgents.get(packageName);
if (agent != null) {
try {
@@ -3302,12 +3434,13 @@
// we are back active so skip it.
unscheduleGcIdler();
- LoadedApk packageInfo = getPackageInfoNoCheck(
+ LoadedApk loadedApk = getLoadedApkNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
- java.lang.ClassLoader cl = packageInfo.getClassLoader();
- service = (Service) cl.loadClass(data.info.name).newInstance();
+ java.lang.ClassLoader cl = loadedApk.getClassLoader();
+ service = loadedApk.getAppFactory()
+ .instantiateService(cl, data.info.name, data.intent);
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
@@ -3319,10 +3452,10 @@
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
- ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
+ ContextImpl context = ContextImpl.createAppContext(this, loadedApk);
context.setOuterContext(service);
- Application app = packageInfo.makeApplication(false, mInstrumentation);
+ Application app = loadedApk.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
service.onCreate();
@@ -4186,11 +4319,11 @@
}
private void handleUpdatePackageCompatibilityInfo(UpdateCompatibilityData data) {
- LoadedApk apk = peekPackageInfo(data.pkg, false);
+ LoadedApk apk = peekLoadedApk(data.pkg, false);
if (apk != null) {
apk.setCompatibilityInfo(data.info);
}
- apk = peekPackageInfo(data.pkg, true);
+ apk = peekLoadedApk(data.pkg, true);
if (apk != null) {
apk.setCompatibilityInfo(data.info);
}
@@ -4684,7 +4817,7 @@
if (a != null) {
Configuration thisConfig = applyConfigCompatMainThread(
mCurDefaultDisplayDpi, newConfig,
- ar.packageInfo.getCompatibilityInfo());
+ ar.loadedApk.getCompatibilityInfo());
if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
// If the activity is currently resumed, its configuration
// needs to change right now.
@@ -5170,7 +5303,7 @@
}
final void handleDispatchPackageBroadcast(int cmd, String[] packages) {
- boolean hasPkgInfo = false;
+ boolean hasLoadedApk = false;
switch (cmd) {
case ApplicationThreadConstants.PACKAGE_REMOVED:
case ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL:
@@ -5181,14 +5314,14 @@
}
synchronized (mResourcesManager) {
for (int i = packages.length - 1; i >= 0; i--) {
- if (!hasPkgInfo) {
+ if (!hasLoadedApk) {
WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
if (ref != null && ref.get() != null) {
- hasPkgInfo = true;
+ hasLoadedApk = true;
} else {
ref = mResourcePackages.get(packages[i]);
if (ref != null && ref.get() != null) {
- hasPkgInfo = true;
+ hasLoadedApk = true;
}
}
}
@@ -5208,21 +5341,21 @@
synchronized (mResourcesManager) {
for (int i = packages.length - 1; i >= 0; i--) {
WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
- LoadedApk pkgInfo = ref != null ? ref.get() : null;
- if (pkgInfo != null) {
- hasPkgInfo = true;
+ LoadedApk loadedApk = ref != null ? ref.get() : null;
+ if (loadedApk != null) {
+ hasLoadedApk = true;
} else {
ref = mResourcePackages.get(packages[i]);
- pkgInfo = ref != null ? ref.get() : null;
- if (pkgInfo != null) {
- hasPkgInfo = true;
+ loadedApk = ref != null ? ref.get() : null;
+ if (loadedApk != null) {
+ hasLoadedApk = true;
}
}
// If the package is being replaced, yet it still has a valid
// LoadedApk object, the package was updated with _DONT_KILL.
// Adjust it's internal references to the application info and
// resources.
- if (pkgInfo != null) {
+ if (loadedApk != null) {
try {
final String packageName = packages[i];
final ApplicationInfo aInfo =
@@ -5236,13 +5369,13 @@
if (ar.activityInfo.applicationInfo.packageName
.equals(packageName)) {
ar.activityInfo.applicationInfo = aInfo;
- ar.packageInfo = pkgInfo;
+ ar.loadedApk = loadedApk;
}
}
}
final List<String> oldPaths =
sPackageManager.getPreviousCodePaths(packageName);
- pkgInfo.updateApplicationInfo(aInfo, oldPaths);
+ loadedApk.updateApplicationInfo(aInfo, oldPaths);
} catch (RemoteException e) {
}
}
@@ -5251,7 +5384,7 @@
break;
}
}
- ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo);
+ ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasLoadedApk);
}
final void handleLowMemory() {
@@ -5455,7 +5588,7 @@
applyCompatConfiguration(mCurDefaultDisplayDpi);
}
- data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
+ data.loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
/**
* Switch this process to density compatibility mode if needed.
@@ -5499,7 +5632,7 @@
// XXX should have option to change the port.
Debug.changeDebugPort(8100);
if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) {
- Slog.w(TAG, "Application " + data.info.getPackageName()
+ Slog.w(TAG, "Application " + data.loadedApk.getPackageName()
+ " is waiting for the debugger on port 8100...");
IActivityManager mgr = ActivityManager.getService();
@@ -5518,7 +5651,7 @@
}
} else {
- Slog.w(TAG, "Application " + data.info.getPackageName()
+ Slog.w(TAG, "Application " + data.loadedApk.getPackageName()
+ " can be debugged on port 8100...");
}
}
@@ -5566,14 +5699,14 @@
mInstrumentationAppDir = ii.sourceDir;
mInstrumentationSplitAppDirs = ii.splitSourceDirs;
mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii);
- mInstrumentedAppDir = data.info.getAppDir();
- mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
- mInstrumentedLibDir = data.info.getLibDir();
+ mInstrumentedAppDir = data.loadedApk.getAppDir();
+ mInstrumentedSplitAppDirs = data.loadedApk.getSplitAppDirs();
+ mInstrumentedLibDir = data.loadedApk.getLibDir();
} else {
ii = null;
}
- final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
+ final ContextImpl appContext = ContextImpl.createAppContext(this, data.loadedApk);
updateLocaleListFromAppContext(appContext,
mResourcesManager.getConfiguration().getLocales());
@@ -5617,9 +5750,9 @@
}
ii.copyTo(instrApp);
instrApp.initForUser(UserHandle.myUserId());
- final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
+ final LoadedApk loadedApk = getLoadedApk(instrApp, data.compatInfo,
appContext.getClassLoader(), false, true, false);
- final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
+ final ContextImpl instrContext = ContextImpl.createAppContext(this, loadedApk);
try {
final ClassLoader cl = instrContext.getClassLoader();
@@ -5644,6 +5777,7 @@
}
} else {
mInstrumentation = new Instrumentation();
+ mInstrumentation.basicInit(this);
}
if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) {
@@ -5663,7 +5797,7 @@
try {
// 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);
+ app = data.loadedApk.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
// don't bring up providers in restricted mode; they may depend on the
@@ -5717,7 +5851,7 @@
final int preloadedFontsResource = info.metaData.getInt(
ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
if (preloadedFontsResource != 0) {
- data.info.getResources().preloadFonts(preloadedFontsResource);
+ data.loadedApk.getResources().preloadFonts(preloadedFontsResource);
}
}
} catch (RemoteException e) {
@@ -6175,8 +6309,13 @@
try {
final java.lang.ClassLoader cl = c.getClassLoader();
- localProvider = (ContentProvider)cl.
- loadClass(info.name).newInstance();
+ LoadedApk loadedApk = peekLoadedApk(ai.packageName, true);
+ if (loadedApk == null) {
+ // System startup case.
+ loadedApk = getSystemContext().mLoadedApk;
+ }
+ localProvider = loadedApk.getAppFactory()
+ .instantiateProvider(cl, info.name);
provider = localProvider.getIContentProvider();
if (provider == null) {
Slog.e(TAG, "Failed to instantiate class " +
@@ -6274,7 +6413,7 @@
System.exit(0);
}
- private void attach(boolean system) {
+ private void attach(boolean system, long startSeq) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
@@ -6289,7 +6428,7 @@
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManager.getService();
try {
- mgr.attachApplication(mAppThread);
+ mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -6322,9 +6461,10 @@
UserHandle.myUserId());
try {
mInstrumentation = new Instrumentation();
+ mInstrumentation.basicInit(this);
ContextImpl context = ContextImpl.createAppContext(
- this, getSystemContext().mPackageInfo);
- mInitialApplication = context.mPackageInfo.makeApplication(true, null);
+ this, getSystemContext().mLoadedApk);
+ mInitialApplication = context.mLoadedApk.makeApplication(true, null);
mInitialApplication.onCreate();
} catch (Exception e) {
throw new RuntimeException(
@@ -6367,7 +6507,7 @@
ThreadedRenderer.enableForegroundTrimming();
}
ActivityThread thread = new ActivityThread();
- thread.attach(true);
+ thread.attach(true, 0);
return thread;
}
@@ -6439,8 +6579,19 @@
Looper.prepareMainLooper();
+ // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
+ // It will be in the format "seq=114"
+ long startSeq = 0;
+ if (args != null) {
+ for (int i = args.length - 1; i >= 0; --i) {
+ if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
+ startSeq = Long.parseLong(
+ args[i].substring(PROC_START_SEQ_IDENT.length()));
+ }
+ }
+ }
ActivityThread thread = new ActivityThread();
- thread.attach(false);
+ thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
diff --git a/core/java/android/app/AppComponentFactory.java b/core/java/android/app/AppComponentFactory.java
new file mode 100644
index 0000000..4df7379
--- /dev/null
+++ b/core/java/android/app/AppComponentFactory.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.ContentProvider;
+import android.content.Intent;
+
+/**
+ * Interface used to control the instantiation of manifest elements.
+ *
+ * @see #instantiateApplication
+ * @see #instantiateActivity
+ * @see #instantiateService
+ * @see #instantiateReceiver
+ * @see #instantiateProvider
+ */
+public class AppComponentFactory {
+
+ /**
+ * Allows application to override the creation of the application object. This can be used to
+ * perform things such as dependency injection or class loader changes to these
+ * classes.
+ *
+ * @param cl The default classloader to use for instantiation.
+ * @param className The class to be instantiated.
+ */
+ public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
+ @NonNull String className)
+ throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ return (Application) cl.loadClass(className).newInstance();
+ }
+
+ /**
+ * Allows application to override the creation of activities. This can be used to
+ * perform things such as dependency injection or class loader changes to these
+ * classes.
+ *
+ * @param cl The default classloader to use for instantiation.
+ * @param className The class to be instantiated.
+ * @param intent Intent creating the class.
+ */
+ public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className,
+ @Nullable Intent intent)
+ throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ return (Activity) cl.loadClass(className).newInstance();
+ }
+
+ /**
+ * Allows application to override the creation of receivers. This can be used to
+ * perform things such as dependency injection or class loader changes to these
+ * classes.
+ *
+ * @param cl The default classloader to use for instantiation.
+ * @param className The class to be instantiated.
+ * @param intent Intent creating the class.
+ */
+ public @NonNull BroadcastReceiver instantiateReceiver(@NonNull ClassLoader cl,
+ @NonNull String className, @Nullable Intent intent)
+ throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ return (BroadcastReceiver) cl.loadClass(className).newInstance();
+ }
+
+ /**
+ * Allows application to override the creation of services. This can be used to
+ * perform things such as dependency injection or class loader changes to these
+ * classes.
+ *
+ * @param cl The default classloader to use for instantiation.
+ * @param className The class to be instantiated.
+ * @param intent Intent creating the class.
+ */
+ public @NonNull Service instantiateService(@NonNull ClassLoader cl,
+ @NonNull String className, @Nullable Intent intent)
+ throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ return (Service) cl.loadClass(className).newInstance();
+ }
+
+ /**
+ * Allows application to override the creation of providers. This can be used to
+ * perform things such as dependency injection or class loader changes to these
+ * classes.
+ *
+ * @param cl The default classloader to use for instantiation.
+ * @param className The class to be instantiated.
+ */
+ public @NonNull ContentProvider instantiateProvider(@NonNull ClassLoader cl,
+ @NonNull String className)
+ throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ return (ContentProvider) cl.loadClass(className).newInstance();
+ }
+
+ /**
+ * @hide
+ */
+ public static AppComponentFactory DEFAULT = new AppComponentFactory();
+}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index b6fb120..ea22d33 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -160,7 +160,7 @@
public static final int OP_WRITE_ICC_SMS = 22;
/** @hide */
public static final int OP_WRITE_SETTINGS = 23;
- /** @hide */
+ /** @hide Required to draw on top of other apps. */
public static final int OP_SYSTEM_ALERT_WINDOW = 24;
/** @hide */
public static final int OP_ACCESS_NOTIFICATIONS = 25;
@@ -256,8 +256,12 @@
public static final int OP_RUN_ANY_IN_BACKGROUND = 70;
/** @hide Change Wi-Fi connectivity state */
public static final int OP_CHANGE_WIFI_STATE = 71;
+ /** @hide Request package deletion through package installer */
+ public static final int OP_REQUEST_DELETE_PACKAGES = 72;
+ /** @hide Bind an accessibility service. */
+ public static final int OP_BIND_ACCESSIBILITY_SERVICE = 73;
/** @hide */
- public static final int _NUM_OP = 72;
+ public static final int _NUM_OP = 74;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -410,6 +414,7 @@
OP_CAMERA,
// Body sensors
OP_BODY_SENSORS,
+ OP_REQUEST_DELETE_PACKAGES,
// APPOP PERMISSIONS
OP_ACCESS_NOTIFICATIONS,
@@ -499,6 +504,8 @@
OP_ANSWER_PHONE_CALLS,
OP_RUN_ANY_IN_BACKGROUND,
OP_CHANGE_WIFI_STATE,
+ OP_REQUEST_DELETE_PACKAGES,
+ OP_BIND_ACCESSIBILITY_SERVICE,
};
/**
@@ -578,6 +585,8 @@
OPSTR_ANSWER_PHONE_CALLS,
null, // OP_RUN_ANY_IN_BACKGROUND
null, // OP_CHANGE_WIFI_STATE
+ null, // OP_REQUEST_DELETE_PACKAGES
+ null, // OP_BIND_ACCESSIBILITY_SERVICE
};
/**
@@ -657,6 +666,8 @@
"ANSWER_PHONE_CALLS",
"RUN_ANY_IN_BACKGROUND",
"CHANGE_WIFI_STATE",
+ "REQUEST_DELETE_PACKAGES",
+ "BIND_ACCESSIBILITY_SERVICE",
};
/**
@@ -736,6 +747,8 @@
Manifest.permission.ANSWER_PHONE_CALLS,
null, // no permission for OP_RUN_ANY_IN_BACKGROUND
Manifest.permission.CHANGE_WIFI_STATE,
+ Manifest.permission.REQUEST_DELETE_PACKAGES,
+ Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
};
/**
@@ -816,6 +829,8 @@
null, // ANSWER_PHONE_CALLS
null, // OP_RUN_ANY_IN_BACKGROUND
null, // OP_CHANGE_WIFI_STATE
+ null, // REQUEST_DELETE_PACKAGES
+ null, // OP_BIND_ACCESSIBILITY_SERVICE
};
/**
@@ -895,6 +910,8 @@
false, // ANSWER_PHONE_CALLS
false, // OP_RUN_ANY_IN_BACKGROUND
false, // OP_CHANGE_WIFI_STATE
+ false, // OP_REQUEST_DELETE_PACKAGES
+ false, // OP_BIND_ACCESSIBILITY_SERVICE
};
/**
@@ -973,6 +990,8 @@
AppOpsManager.MODE_ALLOWED, // ANSWER_PHONE_CALLS
AppOpsManager.MODE_ALLOWED, // OP_RUN_ANY_IN_BACKGROUND
AppOpsManager.MODE_ALLOWED, // OP_CHANGE_WIFI_STATE
+ AppOpsManager.MODE_ALLOWED, // REQUEST_DELETE_PACKAGES
+ AppOpsManager.MODE_ALLOWED, // OP_BIND_ACCESSIBILITY_SERVICE
};
/**
@@ -1055,6 +1074,8 @@
false, // ANSWER_PHONE_CALLS
false, // OP_RUN_ANY_IN_BACKGROUND
false, // OP_CHANGE_WIFI_STATE
+ false, // OP_REQUEST_DELETE_PACKAGES
+ false, // OP_BIND_ACCESSIBILITY_SERVICE
};
/**
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 156df36..5822f5c 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -187,7 +187,7 @@
*/
/* package */ final void attach(Context context) {
attachBaseContext(context);
- mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
+ mLoadedApk = ContextImpl.getImpl(context).mLoadedApk;
}
/* package */ void dispatchActivityCreated(Activity activity, Bundle savedInstanceState) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 1dbdb59..8641a21 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1381,7 +1381,7 @@
sameUid ? app.sourceDir : app.publicSourceDir,
sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
- mContext.mPackageInfo);
+ mContext.mLoadedApk);
if (r != null) {
return r;
}
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index ef66af0..45c0e0c 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -110,7 +110,7 @@
PendingTransactionActions pendingActions);
/** Get package info. */
- public abstract LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
+ public abstract LoadedApk getLoadedApkNoCheck(ApplicationInfo ai,
CompatibilityInfo compatInfo);
/** Deliver app configuration change notification. */
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index b0d020a..1653430 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -90,6 +90,7 @@
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Objects;
+import java.util.concurrent.Executor;
class ReceiverRestrictedContext extends ContextWrapper {
ReceiverRestrictedContext(Context base) {
@@ -158,7 +159,7 @@
private ArrayMap<String, File> mSharedPrefsPaths;
final @NonNull ActivityThread mMainThread;
- final @NonNull LoadedApk mPackageInfo;
+ final @NonNull LoadedApk mLoadedApk;
private @Nullable ClassLoader mClassLoader;
private final @Nullable IBinder mActivityToken;
@@ -250,9 +251,14 @@
}
@Override
+ public Executor getMainExecutor() {
+ return mMainThread.getExecutor();
+ }
+
+ @Override
public Context getApplicationContext() {
- return (mPackageInfo != null) ?
- mPackageInfo.getApplication() : mMainThread.getApplication();
+ return (mLoadedApk != null) ?
+ mLoadedApk.getApplication() : mMainThread.getApplication();
}
@Override
@@ -296,15 +302,15 @@
@Override
public ClassLoader getClassLoader() {
- return mClassLoader != null ? mClassLoader : (mPackageInfo != null ? mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader());
+ return mClassLoader != null ? mClassLoader : (mLoadedApk != null ? mLoadedApk.getClassLoader() : ClassLoader.getSystemClassLoader());
}
@Override
public String getPackageName() {
- if (mPackageInfo != null) {
- return mPackageInfo.getPackageName();
+ if (mLoadedApk != null) {
+ return mLoadedApk.getPackageName();
}
- // No mPackageInfo means this is a Context for the system itself,
+ // No mLoadedApk means this is a Context for the system itself,
// and this here is its name.
return "android";
}
@@ -323,24 +329,24 @@
@Override
public ApplicationInfo getApplicationInfo() {
- if (mPackageInfo != null) {
- return mPackageInfo.getApplicationInfo();
+ if (mLoadedApk != null) {
+ return mLoadedApk.getApplicationInfo();
}
throw new RuntimeException("Not supported in system context");
}
@Override
public String getPackageResourcePath() {
- if (mPackageInfo != null) {
- return mPackageInfo.getResDir();
+ if (mLoadedApk != null) {
+ return mLoadedApk.getResDir();
}
throw new RuntimeException("Not supported in system context");
}
@Override
public String getPackageCodePath() {
- if (mPackageInfo != null) {
- return mPackageInfo.getAppDir();
+ if (mLoadedApk != null) {
+ return mLoadedApk.getAppDir();
}
throw new RuntimeException("Not supported in system context");
}
@@ -350,7 +356,7 @@
// At least one application in the world actually passes in a null
// name. This happened to work because when we generated the file name
// we would stringify it to "null.xml". Nice.
- if (mPackageInfo.getApplicationInfo().targetSdkVersion <
+ if (mLoadedApk.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.KITKAT) {
if (name == null) {
name = "null";
@@ -1092,11 +1098,11 @@
warnIfCallingFromSystemProcess();
IIntentReceiver rd = null;
if (resultReceiver != null) {
- if (mPackageInfo != null) {
+ if (mLoadedApk != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
- rd = mPackageInfo.getReceiverDispatcher(
+ rd = mLoadedApk.getReceiverDispatcher(
resultReceiver, getOuterContext(), scheduler,
mMainThread.getInstrumentation(), false);
} else {
@@ -1196,11 +1202,11 @@
Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
IIntentReceiver rd = null;
if (resultReceiver != null) {
- if (mPackageInfo != null) {
+ if (mLoadedApk != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
- rd = mPackageInfo.getReceiverDispatcher(
+ rd = mLoadedApk.getReceiverDispatcher(
resultReceiver, getOuterContext(), scheduler,
mMainThread.getInstrumentation(), false);
} else {
@@ -1250,11 +1256,11 @@
warnIfCallingFromSystemProcess();
IIntentReceiver rd = null;
if (resultReceiver != null) {
- if (mPackageInfo != null) {
+ if (mLoadedApk != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
- rd = mPackageInfo.getReceiverDispatcher(
+ rd = mLoadedApk.getReceiverDispatcher(
resultReceiver, getOuterContext(), scheduler,
mMainThread.getInstrumentation(), false);
} else {
@@ -1332,11 +1338,11 @@
Bundle initialExtras) {
IIntentReceiver rd = null;
if (resultReceiver != null) {
- if (mPackageInfo != null) {
+ if (mLoadedApk != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
- rd = mPackageInfo.getReceiverDispatcher(
+ rd = mLoadedApk.getReceiverDispatcher(
resultReceiver, getOuterContext(), scheduler,
mMainThread.getInstrumentation(), false);
} else {
@@ -1413,11 +1419,11 @@
Handler scheduler, Context context, int flags) {
IIntentReceiver rd = null;
if (receiver != null) {
- if (mPackageInfo != null && context != null) {
+ if (mLoadedApk != null && context != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
- rd = mPackageInfo.getReceiverDispatcher(
+ rd = mLoadedApk.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
@@ -1444,8 +1450,8 @@
@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
- if (mPackageInfo != null) {
- IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher(
+ if (mLoadedApk != null) {
+ IIntentReceiver rd = mLoadedApk.forgetReceiverDispatcher(
getOuterContext(), receiver);
try {
ActivityManager.getService().unregisterReceiver(rd);
@@ -1578,7 +1584,7 @@
@Override
public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler,
int flags) {
- return mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
+ return mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags);
}
/** @hide */
@@ -1600,16 +1606,16 @@
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
- if (mPackageInfo != null) {
- sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
+ if (mLoadedApk != null) {
+ sd = mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags);
} else {
throw new RuntimeException("Not supported in system context");
}
validateServiceIntent(service);
try {
IBinder token = getActivityToken();
- if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
- && mPackageInfo.getApplicationInfo().targetSdkVersion
+ if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mLoadedApk != null
+ && mLoadedApk.getApplicationInfo().targetSdkVersion
< android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
flags |= BIND_WAIVE_PRIORITY;
}
@@ -1633,8 +1639,8 @@
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
- if (mPackageInfo != null) {
- IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
+ if (mLoadedApk != null) {
+ IServiceConnection sd = mLoadedApk.forgetServiceDispatcher(
getOuterContext(), conn);
try {
ActivityManager.getService().unbindService(sd);
@@ -1979,40 +1985,20 @@
}
}
- private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
- int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
- final String[] splitResDirs;
- final ClassLoader classLoader;
- try {
- splitResDirs = pi.getSplitPaths(splitName);
- classLoader = pi.getSplitClassLoader(splitName);
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- return ResourcesManager.getInstance().getResources(activityToken,
- pi.getResDir(),
- splitResDirs,
- pi.getOverlayDirs(),
- pi.getApplicationInfo().sharedLibraryFiles,
- displayId,
- overrideConfig,
- compatInfo,
- classLoader);
- }
-
@Override
public Context createApplicationContext(ApplicationInfo application, int flags)
throws NameNotFoundException {
- LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
+ LoadedApk loadedApk = mMainThread.getLoadedApk(application,
+ mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE);
- if (pi != null) {
- ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken,
+ if (loadedApk != null) {
+ ContextImpl c = new ContextImpl(this, mMainThread, loadedApk, null, mActivityToken,
new UserHandle(UserHandle.getUserId(application.uid)), flags, null);
final int displayId = mDisplay != null
? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
- c.setResources(createResources(mActivityToken, pi, null, displayId, null,
+ c.setResources(loadedApk.createResources(mActivityToken, null, displayId, null,
getDisplayAdjustments(displayId).getCompatibilityInfo()));
if (c.mResources != null) {
return c;
@@ -2036,20 +2022,21 @@
if (packageName.equals("system") || packageName.equals("android")) {
// The system resources are loaded in every application, so we can safely copy
// the context without reloading Resources.
- return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user,
+ return new ContextImpl(this, mMainThread, mLoadedApk, null, mActivityToken, user,
flags, null);
}
- LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
+ LoadedApk loadedApk = mMainThread.getLoadedApkForPackageName(packageName,
+ mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
- if (pi != null) {
- ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user,
+ if (loadedApk != null) {
+ ContextImpl c = new ContextImpl(this, mMainThread, loadedApk, null, mActivityToken, user,
flags, null);
final int displayId = mDisplay != null
? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
- c.setResources(createResources(mActivityToken, pi, null, displayId, null,
+ c.setResources(loadedApk.createResources(mActivityToken, null, displayId, null,
getDisplayAdjustments(displayId).getCompatibilityInfo()));
if (c.mResources != null) {
return c;
@@ -2063,30 +2050,21 @@
@Override
public Context createContextForSplit(String splitName) throws NameNotFoundException {
- if (!mPackageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
+ if (!mLoadedApk.getApplicationInfo().requestsIsolatedSplitLoading()) {
// All Splits are always loaded.
return this;
}
- final ClassLoader classLoader = mPackageInfo.getSplitClassLoader(splitName);
- final String[] paths = mPackageInfo.getSplitPaths(splitName);
+ final ClassLoader classLoader = mLoadedApk.getSplitClassLoader(splitName);
- final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, splitName,
+ final ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, splitName,
mActivityToken, mUser, mFlags, classLoader);
final int displayId = mDisplay != null
? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
- context.setResources(ResourcesManager.getInstance().getResources(
- mActivityToken,
- mPackageInfo.getResDir(),
- paths,
- mPackageInfo.getOverlayDirs(),
- mPackageInfo.getApplicationInfo().sharedLibraryFiles,
- displayId,
- null,
- mPackageInfo.getCompatibilityInfo(),
- classLoader));
+ context.setResources(mLoadedApk.getOrCreateResourcesForSplit(splitName,
+ mActivityToken, displayId));
return context;
}
@@ -2096,11 +2074,11 @@
throw new IllegalArgumentException("overrideConfiguration must not be null");
}
- ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
+ ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, mSplitName,
mActivityToken, mUser, mFlags, mClassLoader);
final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
- context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
+ context.setResources(mLoadedApk.createResources(mActivityToken, mSplitName, displayId,
overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
return context;
}
@@ -2111,11 +2089,11 @@
throw new IllegalArgumentException("display must not be null");
}
- ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
+ ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, mSplitName,
mActivityToken, mUser, mFlags, mClassLoader);
final int displayId = display.getDisplayId();
- context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
+ context.setResources(mLoadedApk.createResources(mActivityToken, mSplitName, displayId,
null, getDisplayAdjustments(displayId).getCompatibilityInfo()));
context.mDisplay = display;
return context;
@@ -2125,7 +2103,7 @@
public Context createDeviceProtectedStorageContext() {
final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
| Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
- return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
+ return new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser,
flags, mClassLoader);
}
@@ -2133,7 +2111,7 @@
public Context createCredentialProtectedStorageContext() {
final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
| Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
- return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
+ return new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser,
flags, mClassLoader);
}
@@ -2182,14 +2160,14 @@
@Override
public File getDataDir() {
- if (mPackageInfo != null) {
+ if (mLoadedApk != null) {
File res = null;
if (isCredentialProtectedStorage()) {
- res = mPackageInfo.getCredentialProtectedDataDirFile();
+ res = mLoadedApk.getCredentialProtectedDataDirFile();
} else if (isDeviceProtectedStorage()) {
- res = mPackageInfo.getDeviceProtectedDataDirFile();
+ res = mLoadedApk.getDeviceProtectedDataDirFile();
} else {
- res = mPackageInfo.getDataDirFile();
+ res = mLoadedApk.getDataDirFile();
}
if (res != null) {
@@ -2240,10 +2218,10 @@
}
static ContextImpl createSystemContext(ActivityThread mainThread) {
- LoadedApk packageInfo = new LoadedApk(mainThread);
- ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
+ LoadedApk loadedApk = new LoadedApk(mainThread);
+ ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0,
null);
- context.setResources(packageInfo.getResources());
+ context.setResources(loadedApk.getResources());
context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
context.mResourcesManager.getDisplayMetrics());
return context;
@@ -2254,35 +2232,35 @@
* Make sure that the created system UI context shares the same LoadedApk as the system context.
*/
static ContextImpl createSystemUiContext(ContextImpl systemContext) {
- final LoadedApk packageInfo = systemContext.mPackageInfo;
- ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null,
+ final LoadedApk loadedApk = systemContext.mLoadedApk;
+ ContextImpl context = new ContextImpl(null, systemContext.mMainThread, loadedApk, null,
null, null, 0, null);
- context.setResources(createResources(null, packageInfo, null, Display.DEFAULT_DISPLAY, null,
- packageInfo.getCompatibilityInfo()));
+ context.setResources(loadedApk.createResources(null, null, Display.DEFAULT_DISPLAY, null,
+ loadedApk.getCompatibilityInfo()));
return context;
}
- static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
- if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
- ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
+ static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk loadedApk) {
+ if (loadedApk == null) throw new IllegalArgumentException("loadedApk");
+ ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0,
null);
- context.setResources(packageInfo.getResources());
+ context.setResources(loadedApk.getResources());
return context;
}
static ContextImpl createActivityContext(ActivityThread mainThread,
- LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
+ LoadedApk loadedApk, ActivityInfo activityInfo, IBinder activityToken, int displayId,
Configuration overrideConfiguration) {
- if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
+ if (loadedApk == null) throw new IllegalArgumentException("loadedApk");
- String[] splitDirs = packageInfo.getSplitResDirs();
- ClassLoader classLoader = packageInfo.getClassLoader();
+ String[] splitDirs = loadedApk.getSplitResDirs();
+ ClassLoader classLoader = loadedApk.getClassLoader();
- if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
+ if (loadedApk.getApplicationInfo().requestsIsolatedSplitLoading()) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies");
try {
- classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName);
- splitDirs = packageInfo.getSplitPaths(activityInfo.splitName);
+ classLoader = loadedApk.getSplitClassLoader(activityInfo.splitName);
+ splitDirs = loadedApk.getSplitPaths(activityInfo.splitName);
} catch (NameNotFoundException e) {
// Nothing above us can handle a NameNotFoundException, better crash.
throw new RuntimeException(e);
@@ -2291,14 +2269,14 @@
}
}
- ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
+ ContextImpl context = new ContextImpl(null, mainThread, loadedApk, activityInfo.splitName,
activityToken, null, 0, classLoader);
// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
- ? packageInfo.getCompatibilityInfo()
+ ? loadedApk.getCompatibilityInfo()
: CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
final ResourcesManager resourcesManager = ResourcesManager.getInstance();
@@ -2306,10 +2284,10 @@
// Create the base resources for which all configuration contexts for this Activity
// will be rebased upon.
context.setResources(resourcesManager.createBaseActivityResources(activityToken,
- packageInfo.getResDir(),
+ loadedApk.getResDir(),
splitDirs,
- packageInfo.getOverlayDirs(),
- packageInfo.getApplicationInfo().sharedLibraryFiles,
+ loadedApk.getOverlayDirs(),
+ loadedApk.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfiguration,
compatInfo,
@@ -2320,7 +2298,7 @@
}
private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
- @NonNull LoadedApk packageInfo, @Nullable String splitName,
+ @NonNull LoadedApk loadedApk, @Nullable String splitName,
@Nullable IBinder activityToken, @Nullable UserHandle user, int flags,
@Nullable ClassLoader classLoader) {
mOuterContext = this;
@@ -2329,10 +2307,10 @@
// location for application.
if ((flags & (Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE
| Context.CONTEXT_DEVICE_PROTECTED_STORAGE)) == 0) {
- final File dataDir = packageInfo.getDataDirFile();
- if (Objects.equals(dataDir, packageInfo.getCredentialProtectedDataDirFile())) {
+ final File dataDir = loadedApk.getDataDirFile();
+ if (Objects.equals(dataDir, loadedApk.getCredentialProtectedDataDirFile())) {
flags |= Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
- } else if (Objects.equals(dataDir, packageInfo.getDeviceProtectedDataDirFile())) {
+ } else if (Objects.equals(dataDir, loadedApk.getDeviceProtectedDataDirFile())) {
flags |= Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
}
}
@@ -2346,7 +2324,7 @@
}
mUser = user;
- mPackageInfo = packageInfo;
+ mLoadedApk = loadedApk;
mSplitName = splitName;
mClassLoader = classLoader;
mResourcesManager = ResourcesManager.getInstance();
@@ -2357,8 +2335,8 @@
setResources(container.mResources);
mDisplay = container.mDisplay;
} else {
- mBasePackageName = packageInfo.mPackageName;
- ApplicationInfo ainfo = packageInfo.getApplicationInfo();
+ mBasePackageName = loadedApk.mPackageName;
+ ApplicationInfo ainfo = loadedApk.getApplicationInfo();
if (ainfo.uid == Process.SYSTEM_UID && ainfo.uid != Process.myUid()) {
// Special case: system components allow themselves to be loaded in to other
// processes. For purposes of app ops, we must then consider the context as
@@ -2381,7 +2359,7 @@
}
void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
- mPackageInfo.installSystemApplicationInfo(info, classLoader);
+ mLoadedApk.installSystemApplicationInfo(info, classLoader);
}
final void scheduleFinalCleanup(String who, String what) {
@@ -2390,7 +2368,7 @@
final void performFinalCleanup(String who, String what) {
//Log.i(TAG, "Cleanup up context: " + this);
- mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
+ mLoadedApk.removeContextRegistrations(getOuterContext(), who, what);
}
final Context getReceiverRestrictedContext() {
diff --git a/core/java/android/app/DialogFragment.java b/core/java/android/app/DialogFragment.java
index a0fb6ee..3a355d9 100644
--- a/core/java/android/app/DialogFragment.java
+++ b/core/java/android/app/DialogFragment.java
@@ -137,7 +137,9 @@
* {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java
* embed}
*
- * @deprecated Use {@link android.support.v4.app.DialogFragment}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link android.support.v4.app.DialogFragment} for consistent behavior across all devices
+ * and access to <a href="{@docRoot}topic/libraries/architecture/lifecycle.html">Lifecycle</a>.
*/
@Deprecated
public class DialogFragment extends Fragment
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index a92684b..4ff07f2 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -257,7 +257,9 @@
* pressing back will pop it to return the user to whatever previous state
* the activity UI was in.
*
- * @deprecated Use {@link android.support.v4.app.Fragment}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link android.support.v4.app.Fragment} for consistent behavior across all devices
+ * and access to <a href="{@docRoot}topic/libraries/architecture/lifecycle.html">Lifecycle</a>.
*/
@Deprecated
public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListener {
diff --git a/core/java/android/app/FragmentContainer.java b/core/java/android/app/FragmentContainer.java
index a1dd32f..536c866 100644
--- a/core/java/android/app/FragmentContainer.java
+++ b/core/java/android/app/FragmentContainer.java
@@ -25,7 +25,8 @@
/**
* Callbacks to a {@link Fragment}'s container.
*
- * @deprecated Use {@link android.support.v4.app.FragmentContainer}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link android.support.v4.app.FragmentContainer}.
*/
@Deprecated
public abstract class FragmentContainer {
diff --git a/core/java/android/app/FragmentController.java b/core/java/android/app/FragmentController.java
index cbb58d4..40bc248 100644
--- a/core/java/android/app/FragmentController.java
+++ b/core/java/android/app/FragmentController.java
@@ -38,7 +38,8 @@
* It is the responsibility of the host to take care of the Fragment's lifecycle.
* The methods provided by {@link FragmentController} are for that purpose.
*
- * @deprecated Use {@link android.support.v4.app.FragmentController}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link android.support.v4.app.FragmentController}
*/
@Deprecated
public class FragmentController {
diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java
index 1edc68e..b48817b 100644
--- a/core/java/android/app/FragmentHostCallback.java
+++ b/core/java/android/app/FragmentHostCallback.java
@@ -38,7 +38,8 @@
* host fragments, implement {@link FragmentHostCallback}, overriding the methods
* applicable to the host.
*
- * @deprecated Use {@link android.support.v4.app.FragmentHostCallback}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link android.support.v4.app.FragmentHostCallback}
*/
@Deprecated
public abstract class FragmentHostCallback<E> extends FragmentContainer {
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 12e60b8..708450f 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -75,7 +75,9 @@
* <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html">
* Fragments For All</a> for more details.
*
- * @deprecated Use {@link android.support.v4.app.FragmentManager}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link android.support.v4.app.FragmentManager} for consistent behavior across all devices
+ * and access to <a href="{@docRoot}topic/libraries/architecture/lifecycle.html">Lifecycle</a>.
*/
@Deprecated
public abstract class FragmentManager {
@@ -90,7 +92,8 @@
* the identifier as returned by {@link #getId} is the only thing that
* will be persisted across activity instances.
*
- * @deprecated Use {@link android.support.v4.app.FragmentManager.BackStackEntry}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">
+ * Support Library</a> {@link android.support.v4.app.FragmentManager.BackStackEntry}
*/
@Deprecated
public interface BackStackEntry {
@@ -136,7 +139,9 @@
/**
* Interface to watch for changes to the back stack.
*
- * @deprecated Use {@link android.support.v4.app.FragmentManager.OnBackStackChangedListener}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">
+ * Support Library</a>
+ * {@link android.support.v4.app.FragmentManager.OnBackStackChangedListener}
*/
@Deprecated
public interface OnBackStackChangedListener {
@@ -438,7 +443,9 @@
* Callback interface for listening to fragment state changes that happen
* within a given FragmentManager.
*
- * @deprecated Use {@link android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">
+ * Support Library</a>
+ * {@link android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks}
*/
@Deprecated
public abstract static class FragmentLifecycleCallbacks {
diff --git a/core/java/android/app/FragmentManagerNonConfig.java b/core/java/android/app/FragmentManagerNonConfig.java
index beb1a15..326438a 100644
--- a/core/java/android/app/FragmentManagerNonConfig.java
+++ b/core/java/android/app/FragmentManagerNonConfig.java
@@ -28,7 +28,8 @@
* {@link FragmentController#retainNonConfig()} and
* {@link FragmentController#restoreAllState(Parcelable, FragmentManagerNonConfig)}.</p>
*
- * @deprecated Use {@link android.support.v4.app.FragmentManagerNonConfig}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link android.support.v4.app.FragmentManagerNonConfig}
*/
@Deprecated
public class FragmentManagerNonConfig {
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 0f4a7fb..713a559 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -22,7 +22,8 @@
* guide.</p>
* </div>
*
- * @deprecated Use {@link android.support.v4.app.FragmentTransaction}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link android.support.v4.app.FragmentTransaction}
*/
@Deprecated
public abstract class FragmentTransaction {
@@ -182,7 +183,12 @@
public static final int TRANSIT_FRAGMENT_FADE = 3 | TRANSIT_ENTER_MASK;
/** @hide */
- @IntDef({TRANSIT_NONE, TRANSIT_FRAGMENT_OPEN, TRANSIT_FRAGMENT_CLOSE, TRANSIT_FRAGMENT_FADE})
+ @IntDef(prefix = { "TRANSIT_" }, value = {
+ TRANSIT_NONE,
+ TRANSIT_FRAGMENT_OPEN,
+ TRANSIT_FRAGMENT_CLOSE,
+ TRANSIT_FRAGMENT_FADE
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface Transit {}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 84c07ec..a4e221a 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -108,7 +108,7 @@
void unbroadcastIntent(in IApplicationThread caller, in Intent intent, int userId);
oneway void finishReceiver(in IBinder who, int resultCode, in String resultData, in Bundle map,
boolean abortBroadcast, int flags);
- void attachApplication(in IApplicationThread app);
+ void attachApplication(in IApplicationThread app, long startSeq);
oneway void activityIdle(in IBinder token, in Configuration config,
in boolean stopProfiling);
void activityPaused(in IBinder token);
@@ -207,7 +207,7 @@
boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot);
void getMemoryInfo(out ActivityManager.MemoryInfo outInfo);
List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState();
- boolean clearApplicationUserData(in String packageName,
+ boolean clearApplicationUserData(in String packageName, boolean keepState,
in IPackageDataObserver observer, int userId);
void forceStopPackage(in String packageName, int userId);
boolean killPids(in int[] pids, in String reason, boolean secure);
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index b25d778..893a41c 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -110,6 +110,9 @@
void dumpMemInfo(in ParcelFileDescriptor fd, in Debug.MemoryInfo mem, boolean checkin,
boolean dumpInfo, boolean dumpDalvik, boolean dumpSummaryOnly, boolean dumpUnreachable,
in String[] args);
+ void dumpMemInfoProto(in ParcelFileDescriptor fd, in Debug.MemoryInfo mem,
+ boolean dumpInfo, boolean dumpDalvik, boolean dumpSummaryOnly, boolean dumpUnreachable,
+ in String[] args);
void dumpGfxInfo(in ParcelFileDescriptor fd, in String[] args);
void dumpProvider(in ParcelFileDescriptor fd, IBinder servicetoken,
in String[] args);
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 49d58eb..80782e3 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -155,4 +155,9 @@
* Unregister a callback that was receiving color updates
*/
void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId);
+
+ /**
+ * Called from SystemUI when it shows the AoD UI.
+ */
+ void setInAmbientMode(boolean inAmbienMode);
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index d49e11f..41324d0 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1114,7 +1114,10 @@
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
- return newApplication(cl.loadClass(className), context);
+ Application app = getFactory(context.getPackageName())
+ .instantiateApplication(cl, className);
+ app.attach(context);
+ return app;
}
/**
@@ -1201,7 +1204,15 @@
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
- return (Activity)cl.loadClass(className).newInstance();
+ String pkg = intent.getComponent().getPackageName();
+ return getFactory(pkg).instantiateActivity(cl, className, intent);
+ }
+
+ private AppComponentFactory getFactory(String pkg) {
+ LoadedApk loadedApk = mThread.peekLoadedApk(pkg, true);
+ // This is in the case of starting up "android".
+ if (loadedApk == null) loadedApk = mThread.getSystemContext().mLoadedApk;
+ return loadedApk.getAppFactory();
}
private void prePerformCreate(Activity activity) {
@@ -1950,6 +1961,14 @@
mUiAutomationConnection = uiAutomationConnection;
}
+ /**
+ * Only sets the ActivityThread up, keeps everything else null because app is not being
+ * instrumented.
+ */
+ final void basicInit(ActivityThread thread) {
+ mThread = thread;
+ }
+
/** @hide */
public static void checkStartActivityResult(int res, Object intent) {
if (!ActivityManager.isStartResultFatalError(res)) {
diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java
index 9ec7f41..88e2356 100644
--- a/core/java/android/app/LauncherActivity.java
+++ b/core/java/android/app/LauncherActivity.java
@@ -166,7 +166,7 @@
if (item.icon == null) {
item.icon = mIconResizer.createIconThumbnail(item.resolveInfo.loadIcon(getPackageManager()));
}
- text.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null);
+ text.setCompoundDrawablesRelativeWithIntrinsicBounds(item.icon, null, null, null);
}
}
diff --git a/core/java/android/app/ListFragment.java b/core/java/android/app/ListFragment.java
index 90b77b3..7790f70 100644
--- a/core/java/android/app/ListFragment.java
+++ b/core/java/android/app/ListFragment.java
@@ -145,7 +145,9 @@
* @see #setListAdapter
* @see android.widget.ListView
*
- * @deprecated Use {@link android.support.v4.app.ListFragment}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link android.support.v4.app.ListFragment} for consistent behavior across all devices
+ * and access to <a href="{@docRoot}topic/libraries/architecture/lifecycle.html">Lifecycle</a>.
*/
@Deprecated
public class ListFragment extends Fragment {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index ebd1014..26f4980 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -31,6 +31,7 @@
import android.content.pm.split.SplitDependencyLoader;
import android.content.res.AssetManager;
import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
@@ -48,15 +49,13 @@
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.LogPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayAdjustments;
-
import com.android.internal.util.ArrayUtils;
-
import dalvik.system.VMRuntime;
-
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -125,6 +124,7 @@
= new ArrayMap<>();
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
= new ArrayMap<>();
+ private AppComponentFactory mAppComponentFactory;
Application getApplication() {
return mApplication;
@@ -148,6 +148,7 @@
mIncludeCode = includeCode;
mRegisterPackage = registerPackage;
mDisplayAdjustments.setCompatibilityInfo(compatInfo);
+ mAppComponentFactory = createAppFactory(mApplicationInfo, mBaseClassLoader);
}
private static ApplicationInfo adjustNativeLibraryPaths(ApplicationInfo info) {
@@ -203,6 +204,7 @@
mRegisterPackage = false;
mClassLoader = ClassLoader.getSystemClassLoader();
mResources = Resources.getSystem();
+ mAppComponentFactory = createAppFactory(mApplicationInfo, mClassLoader);
}
/**
@@ -212,6 +214,23 @@
assert info.packageName.equals("android");
mApplicationInfo = info;
mClassLoader = classLoader;
+ mAppComponentFactory = createAppFactory(info, classLoader);
+ }
+
+ private AppComponentFactory createAppFactory(ApplicationInfo appInfo, ClassLoader cl) {
+ if (appInfo.appComponentFactory != null) {
+ try {
+ return (AppComponentFactory) cl.loadClass(appInfo.appComponentFactory)
+ .newInstance();
+ } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
+ Slog.e(TAG, "Unable to instantiate appComponentFactory", e);
+ }
+ }
+ return AppComponentFactory.DEFAULT;
+ }
+
+ public AppComponentFactory getAppFactory() {
+ return mAppComponentFactory;
}
public String getPackageName() {
@@ -313,6 +332,7 @@
getClassLoader());
}
}
+ mAppComponentFactory = createAppFactory(aInfo, mClassLoader);
}
private void setApplicationInfo(ApplicationInfo aInfo) {
@@ -947,14 +967,78 @@
throw new AssertionError("null split not found");
}
- mResources = ResourcesManager.getInstance().getResources(null, mResDir,
- splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
- Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
+ mResources = ResourcesManager.getInstance().getResources(
+ null,
+ mResDir,
+ splitPaths,
+ mOverlayDirs,
+ mApplicationInfo.sharedLibraryFiles,
+ Display.DEFAULT_DISPLAY,
+ null,
+ getCompatibilityInfo(),
getClassLoader());
}
return mResources;
}
+ public Resources getOrCreateResourcesForSplit(@NonNull String splitName,
+ @Nullable IBinder activityToken, int displayId) throws NameNotFoundException {
+ return ResourcesManager.getInstance().getResources(
+ activityToken,
+ mResDir,
+ getSplitPaths(splitName),
+ mOverlayDirs,
+ mApplicationInfo.sharedLibraryFiles,
+ displayId,
+ null,
+ getCompatibilityInfo(),
+ getSplitClassLoader(splitName));
+ }
+
+ /**
+ * Creates the top level resources for the given package. Will return an existing
+ * Resources if one has already been created.
+ */
+ public Resources getOrCreateTopLevelResources(@NonNull ApplicationInfo appInfo) {
+ // Request for this app, short circuit
+ if (appInfo.uid == Process.myUid()) {
+ return getResources();
+ }
+
+ // Get resources for a different package
+ return ResourcesManager.getInstance().getResources(
+ null,
+ appInfo.publicSourceDir,
+ appInfo.splitPublicSourceDirs,
+ appInfo.resourceDirs,
+ appInfo.sharedLibraryFiles,
+ Display.DEFAULT_DISPLAY,
+ null,
+ getCompatibilityInfo(),
+ getClassLoader());
+ }
+
+ public Resources createResources(IBinder activityToken, String splitName,
+ int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
+ final String[] splitResDirs;
+ final ClassLoader classLoader;
+ try {
+ splitResDirs = getSplitPaths(splitName);
+ classLoader = getSplitClassLoader(splitName);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ return ResourcesManager.getInstance().getResources(activityToken,
+ mResDir,
+ splitResDirs,
+ mOverlayDirs,
+ mApplicationInfo.sharedLibraryFiles,
+ displayId,
+ overrideConfig,
+ compatInfo,
+ classLoader);
+ }
+
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index 7969684..86d0fd6 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -55,14 +55,16 @@
* <a href="{@docRoot}guide/topics/fundamentals/loaders.html">Loaders</a> developer guide.</p>
* </div>
*
- * @deprecated Use {@link android.support.v4.app.LoaderManager}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link android.support.v4.app.LoaderManager}
*/
@Deprecated
public abstract class LoaderManager {
/**
* Callback interface for a client to interact with the manager.
*
- * @deprecated Use {@link android.support.v4.app.LoaderManager.LoaderCallbacks}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">
+ * Support Library</a> {@link android.support.v4.app.LoaderManager.LoaderCallbacks}
*/
@Deprecated
public interface LoaderCallbacks<D> {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index fb9efe6..85c3be8 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -577,7 +577,13 @@
public int flags;
/** @hide */
- @IntDef({PRIORITY_DEFAULT,PRIORITY_LOW,PRIORITY_MIN,PRIORITY_HIGH,PRIORITY_MAX})
+ @IntDef(prefix = { "PRIORITY_" }, value = {
+ PRIORITY_DEFAULT,
+ PRIORITY_LOW,
+ PRIORITY_MIN,
+ PRIORITY_HIGH,
+ PRIORITY_MAX
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface Priority {}
@@ -1084,6 +1090,12 @@
public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic";
/**
+ * {@link #extras} key: whether the {@link android.app.Notification.MessagingStyle} notification
+ * represents a group conversation.
+ */
+ public static final String EXTRA_IS_GROUP_CONVERSATION = "android.isGroupConversation";
+
+ /**
* {@link #extras} key: whether the notification should be colorized as
* supplied to {@link Builder#setColorized(boolean)}}.
*/
@@ -5954,9 +5966,10 @@
public static final int MAXIMUM_RETAINED_MESSAGES = 25;
CharSequence mUserDisplayName;
- CharSequence mConversationTitle;
+ @Nullable CharSequence mConversationTitle;
List<Message> mMessages = new ArrayList<>();
List<Message> mHistoricMessages = new ArrayList<>();
+ boolean mIsGroupConversation;
MessagingStyle() {
}
@@ -5979,20 +5992,20 @@
}
/**
- * Sets the title to be displayed on this conversation. This should only be used for
- * group messaging and left unset for one-on-one conversations.
- * @param conversationTitle
+ * Sets the title to be displayed on this conversation. May be set to {@code null}.
+ *
+ * @param conversationTitle A name for the conversation, or {@code null}
* @return this object for method chaining.
*/
- public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
+ public MessagingStyle setConversationTitle(@Nullable CharSequence conversationTitle) {
mConversationTitle = conversationTitle;
return this;
}
/**
- * Return the title to be displayed on this conversation. Can be <code>null</code> and
- * should be for one-on-one conversations
+ * Return the title to be displayed on this conversation. May return {@code null}.
*/
+ @Nullable
public CharSequence getConversationTitle() {
return mConversationTitle;
}
@@ -6069,6 +6082,24 @@
}
/**
+ * Sets whether this conversation notification represents a group.
+ * @param isGroupConversation {@code true} if the conversation represents a group,
+ * {@code false} otherwise.
+ * @return this object for method chaining
+ */
+ public MessagingStyle setGroupConversation(boolean isGroupConversation) {
+ mIsGroupConversation = isGroupConversation;
+ return this;
+ }
+
+ /**
+ * Returns {@code true} if this notification represents a group conversation.
+ */
+ public boolean isGroupConversation() {
+ return mIsGroupConversation;
+ }
+
+ /**
* @hide
*/
@Override
@@ -6088,6 +6119,7 @@
}
fixTitleAndTextExtras(extras);
+ extras.putBoolean(EXTRA_IS_GROUP_CONVERSATION, mIsGroupConversation);
}
private void fixTitleAndTextExtras(Bundle extras) {
@@ -6130,6 +6162,7 @@
mMessages = Message.getMessagesFromBundleArray(messages);
Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
+ mIsGroupConversation = extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION);
}
/**
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 23c4166..85a9be3 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -80,9 +80,14 @@
public static final int DISABLE2_MASK = DISABLE2_QUICK_SETTINGS | DISABLE2_SYSTEM_ICONS
| DISABLE2_NOTIFICATION_SHADE | DISABLE2_GLOBAL_ACTIONS;
- @IntDef(flag = true,
- value = {DISABLE2_NONE, DISABLE2_MASK, DISABLE2_QUICK_SETTINGS, DISABLE2_SYSTEM_ICONS,
- DISABLE2_NOTIFICATION_SHADE, DISABLE2_GLOBAL_ACTIONS})
+ @IntDef(flag = true, prefix = { "DISABLE2_" }, value = {
+ DISABLE2_NONE,
+ DISABLE2_MASK,
+ DISABLE2_QUICK_SETTINGS,
+ DISABLE2_SYSTEM_ICONS,
+ DISABLE2_NOTIFICATION_SHADE,
+ DISABLE2_GLOBAL_ACTIONS
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface Disable2Flags {}
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index bc61668..0da5e24 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -98,7 +98,11 @@
public static String ACTION_EXIT_DESK_MODE = "android.app.action.EXIT_DESK_MODE";
/** @hide */
- @IntDef({MODE_NIGHT_AUTO, MODE_NIGHT_NO, MODE_NIGHT_YES})
+ @IntDef(prefix = { "MODE_" }, value = {
+ MODE_NIGHT_AUTO,
+ MODE_NIGHT_NO,
+ MODE_NIGHT_YES
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface NightMode {}
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 9d40381f..35a1789 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -16,18 +16,15 @@
package android.app;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources.NotFoundException;
import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
@@ -39,6 +36,9 @@
import android.util.Printer;
import android.util.Xml;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
/**
@@ -76,6 +76,7 @@
final int mContextUriResource;
final int mContextDescriptionResource;
final boolean mShowMetadataInPreview;
+ final boolean mSupportsAmbientMode;
/**
* Constructor.
@@ -89,15 +90,7 @@
mService = service;
ServiceInfo si = service.serviceInfo;
- PackageManager pm = context.getPackageManager();
- String settingsActivityComponent = null;
- int thumbnailRes = -1;
- int authorRes = -1;
- int descriptionRes = -1;
- int contextUriRes = -1;
- int contextDescriptionRes = -1;
- boolean showMetadataInPreview = false;
-
+ final PackageManager pm = context.getPackageManager();
XmlResourceParser parser = null;
try {
parser = si.loadXmlMetaData(pm, WallpaperService.SERVICE_META_DATA);
@@ -123,27 +116,30 @@
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.Wallpaper);
- settingsActivityComponent = sa.getString(
+ mSettingsActivityName = sa.getString(
com.android.internal.R.styleable.Wallpaper_settingsActivity);
-
- thumbnailRes = sa.getResourceId(
+
+ mThumbnailResource = sa.getResourceId(
com.android.internal.R.styleable.Wallpaper_thumbnail,
-1);
- authorRes = sa.getResourceId(
+ mAuthorResource = sa.getResourceId(
com.android.internal.R.styleable.Wallpaper_author,
-1);
- descriptionRes = sa.getResourceId(
+ mDescriptionResource = sa.getResourceId(
com.android.internal.R.styleable.Wallpaper_description,
-1);
- contextUriRes = sa.getResourceId(
+ mContextUriResource = sa.getResourceId(
com.android.internal.R.styleable.Wallpaper_contextUri,
-1);
- contextDescriptionRes = sa.getResourceId(
+ mContextDescriptionResource = sa.getResourceId(
com.android.internal.R.styleable.Wallpaper_contextDescription,
-1);
- showMetadataInPreview = sa.getBoolean(
+ mShowMetadataInPreview = sa.getBoolean(
com.android.internal.R.styleable.Wallpaper_showMetadataInPreview,
false);
+ mSupportsAmbientMode = sa.getBoolean(
+ com.android.internal.R.styleable.Wallpaper_supportsAmbientMode,
+ false);
sa.recycle();
} catch (NameNotFoundException e) {
@@ -152,14 +148,6 @@
} finally {
if (parser != null) parser.close();
}
-
- mSettingsActivityName = settingsActivityComponent;
- mThumbnailResource = thumbnailRes;
- mAuthorResource = authorRes;
- mDescriptionResource = descriptionRes;
- mContextUriResource = contextUriRes;
- mContextDescriptionResource = contextDescriptionRes;
- mShowMetadataInPreview = showMetadataInPreview;
}
WallpaperInfo(Parcel source) {
@@ -170,6 +158,7 @@
mContextUriResource = source.readInt();
mContextDescriptionResource = source.readInt();
mShowMetadataInPreview = source.readInt() != 0;
+ mSupportsAmbientMode = source.readInt() != 0;
mService = ResolveInfo.CREATOR.createFromParcel(source);
}
@@ -326,6 +315,16 @@
}
/**
+ * Returns whether a wallpaper was optimized or not for ambient mode.
+ *
+ * @return {@code true} if wallpaper can draw in ambient mode.
+ * @hide
+ */
+ public boolean getSupportsAmbientMode() {
+ return mSupportsAmbientMode;
+ }
+
+ /**
* Return the class name of an activity that provides a settings UI for
* the wallpaper. You can launch this activity be starting it with
* an {@link android.content.Intent} whose action is MAIN and with an
@@ -366,6 +365,7 @@
dest.writeInt(mContextUriResource);
dest.writeInt(mContextDescriptionResource);
dest.writeInt(mShowMetadataInPreview ? 1 : 0);
+ dest.writeInt(mSupportsAmbientMode ? 1 : 0);
mService.writeToParcel(dest, flags);
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 081bd81..3829afb 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -176,7 +176,7 @@
// flags for which kind of wallpaper to act on
/** @hide */
- @IntDef(flag = true, value = {
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_SYSTEM,
FLAG_LOCK
})
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 80399ae..085fc79 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -89,7 +89,7 @@
public static final int WINDOWING_MODE_FREEFORM = 5;
/** @hide */
- @IntDef({
+ @IntDef(prefix = { "WINDOWING_MODE_" }, value = {
WINDOWING_MODE_UNDEFINED,
WINDOWING_MODE_FULLSCREEN,
WINDOWING_MODE_PINNED,
@@ -115,7 +115,7 @@
public static final int ACTIVITY_TYPE_ASSISTANT = 4;
/** @hide */
- @IntDef({
+ @IntDef(prefix = { "ACTIVITY_TYPE_" }, value = {
ACTIVITY_TYPE_UNDEFINED,
ACTIVITY_TYPE_STANDARD,
ACTIVITY_TYPE_HOME,
@@ -138,13 +138,12 @@
public static final int WINDOW_CONFIG_ACTIVITY_TYPE = 1 << 3;
/** @hide */
- @IntDef(flag = true,
- value = {
- WINDOW_CONFIG_BOUNDS,
- WINDOW_CONFIG_APP_BOUNDS,
- WINDOW_CONFIG_WINDOWING_MODE,
- WINDOW_CONFIG_ACTIVITY_TYPE
- })
+ @IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = {
+ WINDOW_CONFIG_BOUNDS,
+ WINDOW_CONFIG_APP_BOUNDS,
+ WINDOW_CONFIG_WINDOWING_MODE,
+ WINDOW_CONFIG_ACTIVITY_TYPE
+ })
public @interface WindowConfig {}
public WindowConfiguration() {
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index d0d98c9..2e697ac 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -368,9 +368,9 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({
- BUGREPORT_FAILURE_FAILED_COMPLETING,
- BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE
+ @IntDef(prefix = { "BUGREPORT_FAILURE_" }, value = {
+ BUGREPORT_FAILURE_FAILED_COMPLETING,
+ BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE
})
public @interface BugreportFailureCode {}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ad21983..9816297 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,7 +16,9 @@
package android.app.admin;
+import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
+import android.annotation.Condemned;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -49,6 +51,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.Process;
@@ -61,6 +64,9 @@
import android.security.Credentials;
import android.security.KeyChain;
import android.security.KeyChainException;
+import android.security.keymaster.KeymasterCertificateChain;
+import android.security.keystore.AttestationUtils;
+import android.security.keystore.KeyAttestationException;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.ParcelableKeyGenParameterSpec;
import android.service.restrictions.RestrictionsReceiver;
@@ -94,6 +100,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
/**
* Public interface for managing policies enforced on a device. Most clients of this class must be
@@ -1321,9 +1328,15 @@
public static final String DELEGATION_ENABLE_SYSTEM_APP = "delegation-enable-system-app";
/**
+ * Delegation for installing existing packages. This scope grants access to the
+ * {@link #installExistingPackage} API.
+ */
+ public static final String DELEGATION_INSTALL_EXISTING_PACKAGE =
+ "delegation-install-existing-package";
+
+ /**
* Delegation of management of uninstalled packages. This scope grants access to the
* {@code #setKeepUninstalledPackages} and {@code #getKeepUninstalledPackages} APIs.
- * @hide
*/
public static final String DELEGATION_KEEP_UNINSTALLED_PACKAGES =
"delegation-keep-uninstalled-packages";
@@ -1366,8 +1379,13 @@
/**
* @hide
*/
- @IntDef({STATE_USER_UNMANAGED, STATE_USER_SETUP_INCOMPLETE, STATE_USER_SETUP_COMPLETE,
- STATE_USER_SETUP_FINALIZED, STATE_USER_PROFILE_COMPLETE})
+ @IntDef(prefix = { "STATE_USER_" }, value = {
+ STATE_USER_UNMANAGED,
+ STATE_USER_SETUP_INCOMPLETE,
+ STATE_USER_SETUP_COMPLETE,
+ STATE_USER_SETUP_FINALIZED,
+ STATE_USER_PROFILE_COMPLETE
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface UserProvisioningState {}
@@ -1540,11 +1558,13 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({CODE_OK, CODE_HAS_DEVICE_OWNER, CODE_USER_HAS_PROFILE_OWNER, CODE_USER_NOT_RUNNING,
+ @IntDef(prefix = { "CODE_" }, value = {
+ CODE_OK, CODE_HAS_DEVICE_OWNER, CODE_USER_HAS_PROFILE_OWNER, CODE_USER_NOT_RUNNING,
CODE_USER_SETUP_COMPLETED, CODE_NOT_SYSTEM_USER, CODE_HAS_PAIRED,
CODE_MANAGED_USERS_NOT_SUPPORTED, CODE_SYSTEM_USER, CODE_CANNOT_ADD_MANAGED_PROFILE,
CODE_NOT_SYSTEM_USER_SPLIT, CODE_DEVICE_ADMIN_NOT_SUPPORTED,
- CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER, CODE_ADD_MANAGED_PROFILE_DISALLOWED})
+ CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER, CODE_ADD_MANAGED_PROFILE_DISALLOWED
+ })
public @interface ProvisioningPreCondition {}
/**
@@ -1626,11 +1646,15 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- value = {LOCK_TASK_FEATURE_NONE, LOCK_TASK_FEATURE_SYSTEM_INFO,
- LOCK_TASK_FEATURE_NOTIFICATIONS, LOCK_TASK_FEATURE_HOME,
- LOCK_TASK_FEATURE_RECENTS, LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
- LOCK_TASK_FEATURE_KEYGUARD})
+ @IntDef(flag = true, prefix = { "LOCK_TASK_FEATURE_" }, value = {
+ LOCK_TASK_FEATURE_NONE,
+ LOCK_TASK_FEATURE_SYSTEM_INFO,
+ LOCK_TASK_FEATURE_NOTIFICATIONS,
+ LOCK_TASK_FEATURE_HOME,
+ LOCK_TASK_FEATURE_RECENTS,
+ LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
+ LOCK_TASK_FEATURE_KEYGUARD
+ })
public @interface LockTaskFeature {}
/**
@@ -2611,10 +2635,121 @@
}
/**
+ * The maximum number of characters allowed in the password blacklist.
+ */
+ private static final int PASSWORD_BLACKLIST_CHARACTER_LIMIT = 128 * 1000;
+
+ /**
+ * Throws an exception if the password blacklist is too large.
+ *
+ * @hide
+ */
+ public static void enforcePasswordBlacklistSize(List<String> blacklist) {
+ if (blacklist == null) {
+ return;
+ }
+ long characterCount = 0;
+ for (final String item : blacklist) {
+ characterCount += item.length();
+ }
+ if (characterCount > PASSWORD_BLACKLIST_CHARACTER_LIMIT) {
+ throw new IllegalArgumentException("128 thousand blacklist character limit exceeded by "
+ + (characterCount - PASSWORD_BLACKLIST_CHARACTER_LIMIT) + " characters");
+ }
+ }
+
+ /**
+ * Called by an application that is administering the device to blacklist passwords.
+ * <p>
+ * Any blacklisted password or PIN is prevented from being enrolled by the user or the admin.
+ * Note that the match against the blacklist is case insensitive. The blacklist applies for all
+ * password qualities requested by {@link #setPasswordQuality} however it is not taken into
+ * consideration by {@link #isActivePasswordSufficient}.
+ * <p>
+ * The blacklist can be cleared by passing {@code null} or an empty list. The blacklist is
+ * given a name that is used to track which blacklist is currently set by calling {@link
+ * #getPasswordBlacklistName}. If the blacklist is being cleared, the name is ignored and {@link
+ * #getPasswordBlacklistName} will return {@code null}. The name can only be {@code null} when
+ * the blacklist is being cleared.
+ * <p>
+ * The blacklist is limited to a total of 128 thousand characters rather than limiting to a
+ * number of entries.
+ * <p>
+ * This method can be called on the {@link DevicePolicyManager} instance returned by
+ * {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent
+ * profile.
+ *
+ * @param admin the {@link DeviceAdminReceiver} this request is associated with
+ * @param name name to associate with the blacklist
+ * @param blacklist list of passwords to blacklist or {@code null} to clear the blacklist
+ * @return whether the new blacklist was successfully installed
+ * @throws SecurityException if {@code admin} is not a device or profile owner
+ * @throws IllegalArgumentException if the blacklist surpasses the character limit
+ * @throws NullPointerException if {@code name} is {@code null} when setting a non-empty list
+ *
+ * @see #getPasswordBlacklistName
+ * @see #isActivePasswordSufficient
+ * @see #resetPasswordWithToken
+ *
+ * TODO(63578054): unhide for P
+ * @hide
+ */
+ public boolean setPasswordBlacklist(@NonNull ComponentName admin, @Nullable String name,
+ @Nullable List<String> blacklist) {
+ enforcePasswordBlacklistSize(blacklist);
+
+ try {
+ return mService.setPasswordBlacklist(admin, name, blacklist, mParentInstance);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the name of the password blacklist set by the given admin.
+ *
+ * @param admin the {@link DeviceAdminReceiver} this request is associated with
+ * @return the name of the blacklist or {@code null} if no blacklist is set
+ *
+ * @see #setPasswordBlacklist
+ *
+ * TODO(63578054): unhide for P
+ * @hide
+ */
+ public @Nullable String getPasswordBlacklistName(@NonNull ComponentName admin) {
+ try {
+ return mService.getPasswordBlacklistName(admin, myUserId(), mParentInstance);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Test if a given password is blacklisted.
+ *
+ * @param userId the user to valiate for
+ * @param password the password to check against the blacklist
+ * @return whether the password is blacklisted
+ *
+ * @see #setPasswordBlacklist
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.TEST_BLACKLISTED_PASSWORD)
+ public boolean isPasswordBlacklisted(@UserIdInt int userId, @NonNull String password) {
+ try {
+ return mService.isPasswordBlacklisted(userId, password);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Determine whether the current password the user has set is sufficient to meet the policy
* requirements (e.g. quality, minimum length) that have been requested by the admins of this
* user and its participating profiles. Restrictions on profiles that have a separate challenge
- * are not taken into account. The user must be unlocked in order to perform the check.
+ * are not taken into account. The user must be unlocked in order to perform the check. The
+ * password blacklist is not considered when checking sufficiency.
* <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
@@ -2641,6 +2776,29 @@
}
/**
+ * When called by a profile owner of a managed profile returns true if the profile uses unified
+ * challenge with its parent user.
+ *
+ * <strong>Note: This method is not concerned with password quality and will return false if
+ * the profile has empty password as a separate challenge.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @throws SecurityException if {@code admin} is not a profile owner of a managed profile.
+ * @see UserManager#DISALLOW_UNIFIED_PASSWORD
+ */
+ public boolean isUsingUnifiedPassword(@NonNull ComponentName admin) {
+ throwIfParentInstance("isUsingUnifiedPassword");
+ if (mService != null) {
+ try {
+ return mService.isUsingUnifiedPassword(admin);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return true;
+ }
+
+ /**
* Determine whether the current profile password the user has set is sufficient
* to meet the policy requirements (e.g. quality, minimum length) that have been
* requested by the admins of the parent user and its profiles.
@@ -3159,7 +3317,9 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag=true, value={FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY})
+ @IntDef(flag = true, prefix = { "FLAG_EVICT_" }, value = {
+ FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY
+ })
public @interface LockNowFlag {}
/**
@@ -3474,6 +3634,16 @@
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_START_ENCRYPTION
= "android.app.action.START_ENCRYPTION";
+
+ /**
+ * Broadcast action: notify managed provisioning that new managed user is created.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_MANAGED_USER_CREATED =
+ "android.app.action.MANAGED_USER_CREATED";
+
/**
* Widgets are enabled in keyguard
*/
@@ -3972,15 +4142,27 @@
try {
final ParcelableKeyGenParameterSpec parcelableSpec =
new ParcelableKeyGenParameterSpec(keySpec);
+ KeymasterCertificateChain attestationChain = new KeymasterCertificateChain();
final boolean success = mService.generateKeyPair(
- admin, mContext.getPackageName(), algorithm, parcelableSpec);
+ admin, mContext.getPackageName(), algorithm, parcelableSpec, attestationChain);
if (!success) {
Log.e(TAG, "Error generating key via DevicePolicyManagerService.");
return null;
}
- final KeyPair keyPair = KeyChain.getKeyPair(mContext, keySpec.getKeystoreAlias());
- return new AttestedKeyPair(keyPair, null);
+ final String alias = keySpec.getKeystoreAlias();
+ final KeyPair keyPair = KeyChain.getKeyPair(mContext, alias);
+ Certificate[] outputChain = null;
+ try {
+ if (AttestationUtils.isChainValid(attestationChain)) {
+ outputChain = AttestationUtils.parseCertificateChain(attestationChain);
+ }
+ } catch (KeyAttestationException e) {
+ Log.e(TAG, "Error parsing attestation chain for alias " + alias, e);
+ mService.removeKeyPair(admin, mContext.getPackageName(), alias);
+ return null;
+ }
+ return new AttestedKeyPair(keyPair, outputChain);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (KeyChainException e) {
@@ -3992,6 +4174,52 @@
return null;
}
+
+ /**
+ * Called by a device or profile owner, or delegated certificate installer, to associate
+ * certificates with a key pair that was generated using {@link #generateKeyPair}, and
+ * set whether the key is available for the user to choose in the certificate selection
+ * prompt.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+ * {@code null} if calling from a delegated certificate installer.
+ * @param alias The private key alias under which to install the certificate. The {@code alias}
+ * should denote an existing private key. If a certificate with that alias already
+ * exists, it will be overwritten.
+ * @param certs The certificate chain to install. The chain should start with the leaf
+ * certificate and include the chain of trust in order. This will be returned by
+ * {@link android.security.KeyChain#getCertificateChain}.
+ * @param isUserSelectable {@code true} to indicate that a user can select this key via the
+ * certificate selection prompt, {@code false} to indicate that this key can only be
+ * granted access by implementing
+ * {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias}.
+ * @return {@code true} if the provided {@code alias} exists and the certificates has been
+ * successfully associated with it, {@code false} otherwise.
+ * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
+ * owner, or {@code admin} is null but the calling application is not a delegated
+ * certificate installer.
+ */
+ public boolean setKeyPairCertificate(@Nullable ComponentName admin,
+ @NonNull String alias, @NonNull List<Certificate> certs, boolean isUserSelectable) {
+ throwIfParentInstance("setKeyPairCertificate");
+ try {
+ final byte[] pemCert = Credentials.convertToPem(certs.get(0));
+ byte[] pemChain = null;
+ if (certs.size() > 1) {
+ pemChain = Credentials.convertToPem(
+ certs.subList(1, certs.size()).toArray(new Certificate[0]));
+ }
+ return mService.setKeyPairCertificate(admin, mContext.getPackageName(), alias, pemCert,
+ pemChain, isUserSelectable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (CertificateException | IOException e) {
+ Log.w(TAG, "Could not pem-encode certificate", e);
+ }
+ return false;
+ }
+
+
/**
* @return the alias of a given CA certificate in the certificate store, or {@code null} if it
* doesn't exist.
@@ -4262,16 +4490,16 @@
/**
* Called by a device owner to request a bugreport.
* <p>
- * If the device contains secondary users or profiles, they must be affiliated with the device
- * owner user. Otherwise a {@link SecurityException} will be thrown. See
- * {@link #setAffiliationIds}.
+ * If the device contains secondary users or profiles, they must be affiliated with the device.
+ * Otherwise a {@link SecurityException} will be thrown. See {@link #isAffiliatedUser}.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @return {@code true} if the bugreport collection started successfully, or {@code false} if it
* wasn't triggered because a previous bugreport operation is still active (either the
* bugreport is still running or waiting for the user to share or decline)
* @throws SecurityException if {@code admin} is not a device owner, or there is at least one
- * profile or secondary user that is not affiliated with the device owner user.
+ * profile or secondary user that is not affiliated with the device.
+ * @see #isAffiliatedUser
*/
public boolean requestBugreport(@NonNull ComponentName admin) {
throwIfParentInstance("requestBugreport");
@@ -6085,7 +6313,6 @@
* @return List of package names to keep cached.
* @see #setDelegatedScopes
* @see #DELEGATION_KEEP_UNINSTALLED_PACKAGES
- * @hide
*/
public @Nullable List<String> getKeepUninstalledPackages(@Nullable ComponentName admin) {
throwIfParentInstance("getKeepUninstalledPackages");
@@ -6113,7 +6340,6 @@
* @throws SecurityException if {@code admin} is not a device owner.
* @see #setDelegatedScopes
* @see #DELEGATION_KEEP_UNINSTALLED_PACKAGES
- * @hide
*/
public void setKeepUninstalledPackages(@Nullable ComponentName admin,
@NonNull List<String> packageNames) {
@@ -6201,21 +6427,27 @@
public static final int MAKE_USER_DEMO = 0x0004;
/**
- * Flag used by {@link #createAndManageUser} to specificy that the newly created user should be
+ * Flag used by {@link #createAndManageUser} to specify that the newly created user should be
* started in the background as part of the user creation.
*/
- // TODO: Investigate solutions for the case where reboot happens before setup is completed.
public static final int START_USER_IN_BACKGROUND = 0x0008;
/**
+ * Flag used by {@link #createAndManageUser} to specify that the newly created user should skip
+ * the disabling of system apps during provisioning.
+ */
+ public static final int LEAVE_ALL_SYSTEM_APPS_ENABLED = 0x0010;
+
+ /**
* @hide
*/
- @IntDef(
- flag = true,
- prefix = {"SKIP_", "MAKE_USER_", "START_"},
- value = {SKIP_SETUP_WIZARD, MAKE_USER_EPHEMERAL, MAKE_USER_DEMO,
- START_USER_IN_BACKGROUND}
- )
+ @IntDef(flag = true, prefix = { "SKIP_", "MAKE_USER_", "START_", "LEAVE_" }, value = {
+ SKIP_SETUP_WIZARD,
+ MAKE_USER_EPHEMERAL,
+ MAKE_USER_DEMO,
+ START_USER_IN_BACKGROUND,
+ LEAVE_ALL_SYSTEM_APPS_ENABLED
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface CreateAndManageUserFlags {}
@@ -6316,14 +6548,14 @@
}
/**
- * Called by a profile owner that is affiliated with the device owner to stop the calling user
+ * Called by a profile owner that is affiliated with the device to stop the calling user
* and switch back to primary.
* <p> This has no effect when called on a managed profile.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @return {@code true} if the exit was successful, {@code false} otherwise.
- * @throws SecurityException if {@code admin} is not a profile owner affiliated with the device
- * owner.
+ * @throws SecurityException if {@code admin} is not a profile owner affiliated with the device.
+ * @see #isAffiliatedUser
*/
public boolean logoutUser(@NonNull ComponentName admin) {
throwIfParentInstance("logoutUser");
@@ -6357,6 +6589,21 @@
}
/**
+ * Checks if the profile owner is running in an ephemeral user.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @return whether the profile owner is running in an ephemeral user.
+ */
+ public boolean isEphemeralUser(@NonNull ComponentName admin) {
+ throwIfParentInstance("isEphemeralUser");
+ try {
+ return mService.isEphemeralUser(admin);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Retrieves the application restrictions for a given target application running in the calling
* user.
* <p>
@@ -6591,6 +6838,37 @@
}
/**
+ * Install an existing package that has been installed in another user, or has been kept after
+ * removal via {@link #setKeepUninstalledPackages}.
+ * This function can be called by a device owner, profile owner or a delegate given
+ * the {@link #DELEGATION_INSTALL_EXISTING_PACKAGE} scope via {@link #setDelegatedScopes}.
+ * When called in a secondary user or managed profile, the user/profile must be affiliated with
+ * the device. See {@link #isAffiliatedUser}.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param packageName The package to be installed in the calling profile.
+ * @return {@code true} if the app is installed; {@code false} otherwise.
+ * @throws SecurityException if {@code admin} is not the device owner, or the profile owner of
+ * an affiliated user or profile.
+ * @see #setKeepUninstalledPackages
+ * @see #setDelegatedScopes
+ * @see #isAffiliatedUser
+ * @see #DELEGATION_PACKAGE_ACCESS
+ */
+ public boolean installExistingPackage(@NonNull ComponentName admin, String packageName) {
+ throwIfParentInstance("installExistingPackage");
+ if (mService != null) {
+ try {
+ return mService.installExistingPackage(admin, mContext.getPackageName(),
+ packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
* Called by a device owner or profile owner to disable account management for a specific type
* of account.
* <p>
@@ -6661,13 +6939,14 @@
* package list results in locked tasks belonging to those packages to be finished.
* <p>
* This function can only be called by the device owner or by a profile owner of a user/profile
- * that is affiliated with the device owner user. See {@link #setAffiliationIds}. Any packages
+ * that is affiliated with the device. See {@link #isAffiliatedUser}. Any packages
* set via this method will be cleared if the user becomes unaffiliated.
*
* @param packages The list of packages allowed to enter lock task mode
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @throws SecurityException if {@code admin} is not the device owner, or the profile owner of
* an affiliated user or profile.
+ * @see #isAffiliatedUser
* @see Activity#startLockTask()
* @see DeviceAdminReceiver#onLockTaskModeEntering(Context, Intent, String)
* @see DeviceAdminReceiver#onLockTaskModeExiting(Context, Intent)
@@ -6690,6 +6969,7 @@
*
* @throws SecurityException if {@code admin} is not the device owner, or the profile owner of
* an affiliated user or profile.
+ * @see #isAffiliatedUser
* @see #setLockTaskPackages
*/
public @NonNull String[] getLockTaskPackages(@NonNull ComponentName admin) {
@@ -6729,7 +7009,7 @@
* enabled.
* <p>
* This function can only be called by the device owner or by a profile owner of a user/profile
- * that is affiliated with the device owner user. See {@link #setAffiliationIds}. Any features
+ * that is affiliated with the device. See {@link #isAffiliatedUser}. Any features
* set via this method will be cleared if the user becomes unaffiliated.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
@@ -6743,6 +7023,7 @@
* {@link #LOCK_TASK_FEATURE_KEYGUARD}
* @throws SecurityException if {@code admin} is not the device owner, or the profile owner of
* an affiliated user or profile.
+ * @see #isAffiliatedUser
*/
public void setLockTaskFeatures(@NonNull ComponentName admin, @LockTaskFeature int flags) {
throwIfParentInstance("setLockTaskFeatures");
@@ -6762,7 +7043,8 @@
* @return bitfield of flags. See {@link #setLockTaskFeatures(ComponentName, int)} for a list.
* @throws SecurityException if {@code admin} is not the device owner, or the profile owner of
* an affiliated user or profile.
- * @see #setLockTaskFeatures(ComponentName, int)
+ * @see #isAffiliatedUser
+ * @see #setLockTaskFeatures
*/
public @LockTaskFeature int getLockTaskFeatures(@NonNull ComponentName admin) {
throwIfParentInstance("getLockTaskFeatures");
@@ -7760,6 +8042,7 @@
* @param admin Which device owner this request is associated with.
* @param enabled whether security logging should be enabled or not.
* @throws SecurityException if {@code admin} is not a device owner.
+ * @see #setAffiliationIds
* @see #retrieveSecurityLogs
*/
public void setSecurityLoggingEnabled(@NonNull ComponentName admin, boolean enabled) {
@@ -7798,14 +8081,14 @@
* owner has been notified via {@link DeviceAdminReceiver#onSecurityLogsAvailable}.
*
* <p>If there is any other user or profile on the device, it must be affiliated with the
- * device owner. Otherwise a {@link SecurityException} will be thrown. See
- * {@link #setAffiliationIds}
+ * device. Otherwise a {@link SecurityException} will be thrown. See {@link #isAffiliatedUser}.
*
* @param admin Which device owner this request is associated with.
* @return the new batch of security logs which is a list of {@link SecurityEvent},
* or {@code null} if rate limitation is exceeded or if logging is currently disabled.
* @throws SecurityException if {@code admin} is not a device owner, or there is at least one
- * profile or secondary user that is not affiliated with the device owner user.
+ * profile or secondary user that is not affiliated with the device.
+ * @see #isAffiliatedUser
* @see DeviceAdminReceiver#onSecurityLogsAvailable
*/
public @Nullable List<SecurityEvent> retrieveSecurityLogs(@NonNull ComponentName admin) {
@@ -7848,14 +8131,14 @@
* about data corruption when parsing. </strong>
*
* <p>If there is any other user or profile on the device, it must be affiliated with the
- * device owner. Otherwise a {@link SecurityException} will be thrown. See
- * {@link #setAffiliationIds}
+ * device. Otherwise a {@link SecurityException} will be thrown. See {@link #isAffiliatedUser}.
*
* @param admin Which device owner this request is associated with.
* @return Device logs from before the latest reboot of the system, or {@code null} if this API
* is not supported on the device.
* @throws SecurityException if {@code admin} is not a device owner, or there is at least one
- * profile or secondary user that is not affiliated with the device owner user.
+ * profile or secondary user that is not affiliated with the device.
+ * @see #isAffiliatedUser
* @see #retrieveSecurityLogs
*/
public @Nullable List<SecurityEvent> retrievePreRebootSecurityLogs(
@@ -8063,6 +8346,9 @@
* Indicates the entity that controls the device or profile owner. Two users/profiles are
* affiliated if the set of ids set by their device or profile owners intersect.
*
+ * <p>A user/profile that is affiliated with the device owner user is considered to be
+ * affiliated with the device.
+ *
* <p><strong>Note:</strong> Features that depend on user affiliation (such as security logging
* or {@link #bindDeviceAdminServiceAsUser}) won't be available when a secondary user or profile
* is created, until it becomes affiliated. Therefore it is recommended that the appropriate
@@ -8073,6 +8359,7 @@
* @param ids A set of opaque non-empty affiliation ids.
*
* @throws IllegalArgumentException if {@code ids} is null or contains an empty string.
+ * @see #isAffiliatedUser
*/
public void setAffiliationIds(@NonNull ComponentName admin, @NonNull Set<String> ids) {
throwIfParentInstance("setAffiliationIds");
@@ -8100,13 +8387,12 @@
}
/**
- * @hide
* Returns whether this user/profile is affiliated with the device.
* <p>
* By definition, the user that the device owner runs on is always affiliated with the device.
* Any other user/profile is considered affiliated with the device if the set specified by its
* profile owner via {@link #setAffiliationIds} intersects with the device owner's.
- *
+ * @see #setAffiliationIds
*/
public boolean isAffiliatedUser() {
throwIfParentInstance("isAffiliatedUser");
@@ -8319,6 +8605,7 @@
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param enabled whether network logging should be enabled or not.
* @throws SecurityException if {@code admin} is not a device owner.
+ * @see #setAffiliationIds
* @see #retrieveNetworkLogs
*/
public void setNetworkLoggingEnabled(@NonNull ComponentName admin, boolean enabled) {
@@ -8374,7 +8661,8 @@
* {@code null} if the batch represented by batchToken is no longer available or if
* logging is disabled.
* @throws SecurityException if {@code admin} is not a device owner, or there is at least one
- * profile or secondary user that is not affiliated with the device owner user.
+ * profile or secondary user that is not affiliated with the device.
+ * @see #setAffiliationIds
* @see DeviceAdminReceiver#onNetworkLogsAvailable
*/
public @Nullable List<NetworkEvent> retrieveNetworkLogs(@NonNull ComponentName admin,
@@ -8552,6 +8840,15 @@
}
}
+ /** {@hide} */
+ @Condemned
+ @Deprecated
+ public boolean clearApplicationUserData(@NonNull ComponentName admin,
+ @NonNull String packageName, @NonNull OnClearApplicationUserDataListener listener,
+ @NonNull Handler handler) {
+ return clearApplicationUserData(admin, packageName, listener, new HandlerExecutor(handler));
+ }
+
/**
* Called by the device owner or profile owner to clear application user data of a given
* package. The behaviour of this is equivalent to the target application calling
@@ -8563,19 +8860,20 @@
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param packageName The name of the package which will have its user data wiped.
* @param listener A callback object that will inform the caller when the clearing is done.
- * @param handler The handler indicating the thread on which the listener should be invoked.
+ * @param executor The executor through which the listener should be invoked.
* @throws SecurityException if the caller is not the device owner/profile owner.
* @return whether the clearing succeeded.
*/
public boolean clearApplicationUserData(@NonNull ComponentName admin,
@NonNull String packageName, @NonNull OnClearApplicationUserDataListener listener,
- @NonNull Handler handler) {
+ @NonNull @CallbackExecutor Executor executor) {
throwIfParentInstance("clearAppData");
+ Preconditions.checkNotNull(executor);
try {
return mService.clearApplicationUserData(admin, packageName,
new IPackageDataObserver.Stub() {
public void onRemoveCompleted(String pkg, boolean succeeded) {
- handler.post(() ->
+ executor.execute(() ->
listener.onApplicationUserDataCleared(pkg, succeeded));
}
});
@@ -8629,4 +8927,65 @@
*/
void onApplicationUserDataCleared(String packageName, boolean succeeded);
}
+
+ /**
+ * Returns set of system apps that should be removed during provisioning.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param userId ID of the user to be provisioned.
+ * @param provisioningAction action indicating type of provisioning, should be one of
+ * {@link #ACTION_PROVISION_MANAGED_DEVICE}, {@link #ACTION_PROVISION_MANAGED_PROFILE} or
+ * {@link #ACTION_PROVISION_MANAGED_USER}.
+ *
+ * @hide
+ */
+ public Set<String> getDisallowedSystemApps(ComponentName admin, int userId,
+ String provisioningAction) {
+ try {
+ return new ArraySet<>(
+ mService.getDisallowedSystemApps(admin, userId, provisioningAction));
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ //TODO STOPSHIP Add link to onTransferComplete callback when implemented.
+ /**
+ * Transfers the current administrator. All policies from the current administrator are
+ * migrated to the new administrator. The whole operation is atomic - the transfer is either
+ * complete or not done at all.
+ *
+ * Depending on the current administrator (device owner, profile owner, corporate owned
+ * profile owner), you have the following expected behaviour:
+ * <ul>
+ * <li>A device owner can only be transferred to a new device owner</li>
+ * <li>A profile owner can only be transferred to a new profile owner</li>
+ * <li>A corporate owned managed profile can have two cases:
+ * <ul>
+ * <li>If the device owner and profile owner are the same package,
+ * both will be transferred.</li>
+ * <li>If the device owner and profile owner are different packages,
+ * and if this method is called from the profile owner, only the profile owner
+ * is transferred. Similarly, if it is called from the device owner, only
+ * the device owner is transferred.</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param target Which {@link DeviceAdminReceiver} we want the new administrator to be.
+ * @param bundle Parameters - This bundle allows the current administrator to pass data to the
+ * new administrator. The parameters will be received in the
+ * onTransferComplete callback.
+ * @hide
+ */
+ public void transferOwner(@NonNull ComponentName admin, @NonNull ComponentName target,
+ PersistableBundle bundle) {
+ throwIfParentInstance("transferOwner");
+ try {
+ mService.transferOwner(admin, target, bundle);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index ff869d2..5b02c22 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -36,6 +36,7 @@
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.UserHandle;
+import android.security.keymaster.KeymasterCertificateChain;
import android.security.keystore.ParcelableKeyGenParameterSpec;
import java.util.List;
@@ -77,8 +78,13 @@
long getPasswordExpiration(in ComponentName who, int userHandle, boolean parent);
+ boolean setPasswordBlacklist(in ComponentName who, String name, in List<String> blacklist, boolean parent);
+ String getPasswordBlacklistName(in ComponentName who, int userId, boolean parent);
+ boolean isPasswordBlacklisted(int userId, String password);
+
boolean isActivePasswordSufficient(int userHandle, boolean parent);
boolean isProfileActivePasswordSufficientForParent(int userHandle);
+ boolean isUsingUnifiedPassword(in ComponentName admin);
int getCurrentFailedPasswordAttempts(int userHandle, boolean parent);
int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent);
@@ -166,7 +172,11 @@
in byte[] certBuffer, in byte[] certChainBuffer, String alias, boolean requestAccess,
boolean isUserSelectable);
boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias);
- boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, in ParcelableKeyGenParameterSpec keySpec);
+ boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm,
+ in ParcelableKeyGenParameterSpec keySpec,
+ out KeymasterCertificateChain attestationChain);
+ boolean setKeyPairCertificate(in ComponentName who, in String callerPackage, in String alias,
+ in byte[] certBuffer, in byte[] certChainBuffer, boolean isUserSelectable);
void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback);
void setDelegatedScopes(in ComponentName who, in String delegatePackage, in List<String> scopes);
@@ -223,6 +233,7 @@
void enableSystemApp(in ComponentName admin, in String callerPackage, in String packageName);
int enableSystemAppWithIntent(in ComponentName admin, in String callerPackage, in Intent intent);
+ boolean installExistingPackage(in ComponentName admin, in String callerPackage, in String packageName);
void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
String[] getAccountTypesWithManagementDisabled();
@@ -357,6 +368,7 @@
IApplicationThread caller, IBinder token, in Intent service,
IServiceConnection connection, int flags, int targetUserId);
List<UserHandle> getBindDeviceAdminTargetUsers(in ComponentName admin);
+ boolean isEphemeralUser(in ComponentName admin);
long getLastSecurityLogRetrievalTime();
long getLastBugReportRequestTime();
@@ -374,4 +386,7 @@
void setLogoutEnabled(in ComponentName admin, boolean enabled);
boolean isLogoutEnabled();
+
+ List<String> getDisallowedSystemApps(in ComponentName admin, int userId, String provisioningAction);
+ void transferOwner(in ComponentName admin, in ComponentName target, in PersistableBundle bundle);
}
diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java
index 4658a47..5fee853 100644
--- a/core/java/android/app/admin/PasswordMetrics.java
+++ b/core/java/android/app/admin/PasswordMetrics.java
@@ -223,7 +223,12 @@
}
@Retention(RetentionPolicy.SOURCE)
- @IntDef({CHAR_UPPER_CASE, CHAR_LOWER_CASE, CHAR_DIGIT, CHAR_SYMBOL})
+ @IntDef(prefix = { "CHAR_" }, value = {
+ CHAR_UPPER_CASE,
+ CHAR_LOWER_CASE,
+ CHAR_DIGIT,
+ CHAR_SYMBOL
+ })
private @interface CharacterCatagory {}
private static final int CHAR_LOWER_CASE = 0;
private static final int CHAR_UPPER_CASE = 1;
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 2b590e0..0f93c59 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -43,10 +43,17 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({TAG_ADB_SHELL_INTERACTIVE, TAG_ADB_SHELL_CMD, TAG_SYNC_RECV_FILE, TAG_SYNC_SEND_FILE,
- TAG_APP_PROCESS_START, TAG_KEYGUARD_DISMISSED, TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT,
- TAG_KEYGUARD_SECURED})
- public @interface SECURITY_LOG_TAG {}
+ @IntDef(prefix = { "TAG_" }, value = {
+ TAG_ADB_SHELL_INTERACTIVE,
+ TAG_ADB_SHELL_CMD,
+ TAG_SYNC_RECV_FILE,
+ TAG_SYNC_SEND_FILE,
+ TAG_APP_PROCESS_START,
+ TAG_KEYGUARD_DISMISSED,
+ TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT,
+ TAG_KEYGUARD_SECURED
+ })
+ public @interface SecurityLogTag {}
/**
* Indicate that an ADB interactive shell was opened via "adb shell".
@@ -143,13 +150,8 @@
/**
* Returns the tag of this log entry, which specifies entry's semantics.
- * Could be one of {@link SecurityLog#TAG_SYNC_RECV_FILE},
- * {@link SecurityLog#TAG_SYNC_SEND_FILE}, {@link SecurityLog#TAG_ADB_SHELL_CMD},
- * {@link SecurityLog#TAG_ADB_SHELL_INTERACTIVE}, {@link SecurityLog#TAG_APP_PROCESS_START},
- * {@link SecurityLog#TAG_KEYGUARD_DISMISSED}, {@link SecurityLog#TAG_KEYGUARD_SECURED},
- * {@link SecurityLog#TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT}.
*/
- public @SECURITY_LOG_TAG int getTag() {
+ public @SecurityLogTag int getTag() {
return mEvent.getTag();
}
diff --git a/core/java/android/app/admin/SystemUpdateInfo.java b/core/java/android/app/admin/SystemUpdateInfo.java
index fa31273..b0376b5 100644
--- a/core/java/android/app/admin/SystemUpdateInfo.java
+++ b/core/java/android/app/admin/SystemUpdateInfo.java
@@ -52,7 +52,11 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({SECURITY_PATCH_STATE_FALSE, SECURITY_PATCH_STATE_TRUE, SECURITY_PATCH_STATE_UNKNOWN})
+ @IntDef(prefix = { "SECURITY_PATCH_STATE_" }, value = {
+ SECURITY_PATCH_STATE_FALSE,
+ SECURITY_PATCH_STATE_TRUE,
+ SECURITY_PATCH_STATE_UNKNOWN
+ })
public @interface SecurityPatchState {}
private static final String ATTR_RECEIVED_TIME = "received-time";
diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java
index 995d98a..232a688 100644
--- a/core/java/android/app/admin/SystemUpdatePolicy.java
+++ b/core/java/android/app/admin/SystemUpdatePolicy.java
@@ -36,10 +36,11 @@
public class SystemUpdatePolicy implements Parcelable {
/** @hide */
- @IntDef({
- TYPE_INSTALL_AUTOMATIC,
- TYPE_INSTALL_WINDOWED,
- TYPE_POSTPONE})
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_INSTALL_AUTOMATIC,
+ TYPE_INSTALL_WINDOWED,
+ TYPE_POSTPONE
+ })
@Retention(RetentionPolicy.SOURCE)
@interface SystemUpdatePolicyType {}
diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
index 24141e5..0fdc7c5 100644
--- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java
+++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
@@ -27,8 +27,17 @@
*/
public abstract class ActivityLifecycleItem extends ClientTransactionItem {
- @IntDef({UNDEFINED, PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP,
- ON_DESTROY, ON_RESTART})
+ @IntDef(prefix = { "UNDEFINED", "PRE_", "ON_" }, value = {
+ UNDEFINED,
+ PRE_ON_CREATE,
+ ON_CREATE,
+ ON_START,
+ ON_RESUME,
+ ON_PAUSE,
+ ON_STOP,
+ ON_DESTROY,
+ ON_RESTART
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface LifecycleState{}
public static final int UNDEFINED = -1;
diff --git a/core/java/android/app/servertransaction/PendingTransactionActions.java b/core/java/android/app/servertransaction/PendingTransactionActions.java
index 073d28c..8304c1c 100644
--- a/core/java/android/app/servertransaction/PendingTransactionActions.java
+++ b/core/java/android/app/servertransaction/PendingTransactionActions.java
@@ -134,7 +134,7 @@
Bundle.dumpStats(pw, mPersistentState);
if (ex instanceof TransactionTooLargeException
- && mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
+ && mActivity.loadedApk.getTargetSdkVersion() < Build.VERSION_CODES.N) {
Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
return;
}
diff --git a/core/java/android/app/slice/ISliceListener.aidl b/core/java/android/app/slice/ISliceListener.aidl
new file mode 100644
index 0000000..d293fd4
--- /dev/null
+++ b/core/java/android/app/slice/ISliceListener.aidl
@@ -0,0 +1,25 @@
+/**
+ * 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.app.slice;
+
+import android.app.slice.ISliceManager;
+import android.app.slice.Slice;
+
+/** @hide */
+oneway interface ISliceListener {
+ void onSliceUpdated(in Slice s);
+}
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index 6e52f38..5f0e542 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -16,6 +16,17 @@
package android.app.slice;
+import android.app.slice.ISliceListener;
+import android.app.slice.SliceSpec;
+import android.net.Uri;
+
/** @hide */
interface ISliceManager {
+ void addSliceListener(in Uri uri, String pkg, in ISliceListener listener,
+ in SliceSpec[] specs);
+ void removeSliceListener(in Uri uri, String pkg, in ISliceListener listener);
+ void pinSlice(String pkg, in Uri uri, in SliceSpec[] specs);
+ void unpinSlice(String pkg, in Uri uri);
+ boolean hasSliceAccess(String pkg);
+ SliceSpec[] getPinnedSpecs(in Uri uri, String pkg);
}
diff --git a/core/java/android/app/slice/Slice.aidl b/core/java/android/app/slice/Slice.aidl
new file mode 100644
index 0000000..e097f9d
--- /dev/null
+++ b/core/java/android/app/slice/Slice.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.slice;
+
+parcelable Slice;
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 9b3b462..5c7f674 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -63,7 +63,7 @@
HINT_ACTIONS,
HINT_SELECTED,
HINT_NO_TINT,
- HINT_HIDDEN,
+ HINT_SHORTCUT,
HINT_TOGGLE,
HINT_HORIZONTAL,
HINT_PARTIAL,
@@ -118,12 +118,15 @@
*/
public static final String HINT_NO_TINT = "no_tint";
/**
- * Hint to indicate that this content should not be shown in larger renderings
- * of Slices. This content may be used to populate the shortcut/icon
- * format of the slice.
- * @hide
+ * Hint to indicate that this content should only be displayed if the slice is presented
+ * as a shortcut.
*/
- public static final String HINT_HIDDEN = "hidden";
+ public static final String HINT_SHORTCUT = "shortcut";
+ /**
+ * Hint indicating this content should be shown instead of the normal content when the slice
+ * is in small format.
+ */
+ public static final String HINT_SUMMARY = "summary";
/**
* Hint to indicate that this content has a toggle action associated with it. To indicate that
* the toggle is on, use {@link #HINT_SELECTED}. When the toggle state changes, the intent
@@ -151,7 +154,6 @@
/**
* Key to retrieve an extra added to an intent when a control is changed.
- * @hide
*/
public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
/**
@@ -171,6 +173,17 @@
* Subtype to tag an item represents a slider.
*/
public static final String SUBTYPE_SLIDER = "slider";
+ /**
+ * Subtype to indicate that this content has a toggle action associated with it. To indicate
+ * that the toggle is on, use {@link #HINT_SELECTED}. When the toggle state changes, the
+ * intent associated with it will be sent along with an extra {@link #EXTRA_TOGGLE_STATE}
+ * which can be retrieved to see the new state of the toggle.
+ */
+ public static final String SUBTYPE_TOGGLE = "toggle";
+ /**
+ * Subtype to tag an item representing priority.
+ */
+ public static final String SUBTYPE_PRIORITY = "priority";
private final SliceItem[] mItems;
private final @SliceHint String[] mHints;
@@ -463,6 +476,32 @@
}
/**
+ * Add a bundle to the slice being constructed.
+ * <p>Expected to be used for support library extension, should not be used for general
+ * development
+ * @param subType Optional template-specific type information
+ * @see {@link SliceItem#getSubType()}
+ */
+ public Slice.Builder addBundle(Bundle bundle, @Nullable String subType,
+ @SliceHint String... hints) {
+ mItems.add(new SliceItem(bundle, SliceItem.FORMAT_BUNDLE, subType,
+ hints));
+ return this;
+ }
+
+ /**
+ * Add a bundle to the slice being constructed.
+ * <p>Expected to be used for support library extension, should not be used for general
+ * development
+ * @param subType Optional template-specific type information
+ * @see {@link SliceItem#getSubType()}
+ */
+ public Slice.Builder addBundle(Bundle bundle, @Nullable String subType,
+ @SliceHint List<String> hints) {
+ return addBundle(bundle, subType, hints.toArray(new String[hints.size()]));
+ }
+
+ /**
* Construct the slice.
*/
public Slice build() {
diff --git a/core/java/android/app/slice/SliceItem.java b/core/java/android/app/slice/SliceItem.java
index 5d04c3e..bcfd413 100644
--- a/core/java/android/app/slice/SliceItem.java
+++ b/core/java/android/app/slice/SliceItem.java
@@ -21,6 +21,7 @@
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.graphics.drawable.Icon;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -47,6 +48,7 @@
* <li>{@link #FORMAT_INT}</li>
* <li>{@link #FORMAT_TIMESTAMP}</li>
* <li>{@link #FORMAT_REMOTE_INPUT}</li>
+ * <li>{@link #FORMAT_BUNDLE}</li>
*
* The hints that a {@link SliceItem} are a set of strings which annotate
* the content. The hints that are guaranteed to be understood by the system
@@ -54,6 +56,8 @@
*/
public final class SliceItem implements Parcelable {
+ private static final String TAG = "SliceItem";
+
/**
* @hide
*/
@@ -65,6 +69,7 @@
FORMAT_INT,
FORMAT_TIMESTAMP,
FORMAT_REMOTE_INPUT,
+ FORMAT_BUNDLE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SliceType {}
@@ -105,6 +110,10 @@
* A {@link SliceItem} that contains a {@link RemoteInput}.
*/
public static final String FORMAT_REMOTE_INPUT = "input";
+ /**
+ * A {@link SliceItem} that contains a {@link Bundle}.
+ */
+ public static final String FORMAT_BUNDLE = "bundle";
/**
* @hide
@@ -143,20 +152,6 @@
}
/**
- * @hide
- */
- public void addHint(@Slice.SliceHint String hint) {
- mHints = ArrayUtils.appendElement(String.class, mHints, hint);
- }
-
- /**
- * @hide
- */
- public void removeHint(String hint) {
- ArrayUtils.removeElement(String.class, mHints, hint);
- }
-
- /**
* Get the format of this SliceItem.
* <p>
* The format will be one of the following types supported by the platform:
@@ -167,6 +162,7 @@
* <li>{@link #FORMAT_INT}</li>
* <li>{@link #FORMAT_TIMESTAMP}</li>
* <li>{@link #FORMAT_REMOTE_INPUT}</li>
+ * <li>{@link #FORMAT_BUNDLE}</li>
* @see #getSubType() ()
*/
public String getFormat() {
@@ -193,6 +189,13 @@
}
/**
+ * @return The parcelable held by this {@link #FORMAT_BUNDLE} SliceItem
+ */
+ public Bundle getBundle() {
+ return (Bundle) mObj;
+ }
+
+ /**
* @return The icon held by this {@link #FORMAT_IMAGE} SliceItem
*/
public Icon getIcon() {
@@ -321,6 +324,7 @@
case FORMAT_SLICE:
case FORMAT_IMAGE:
case FORMAT_REMOTE_INPUT:
+ case FORMAT_BUNDLE:
((Parcelable) obj).writeToParcel(dest, flags);
break;
case FORMAT_ACTION:
@@ -357,6 +361,8 @@
return in.readLong();
case FORMAT_REMOTE_INPUT:
return RemoteInput.CREATOR.createFromParcel(in);
+ case FORMAT_BUNDLE:
+ return Bundle.CREATOR.createFromParcel(in);
}
throw new RuntimeException("Unsupported type " + type);
}
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index e99f676..0c5f225d 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -16,24 +16,224 @@
package android.app.slice;
+import android.annotation.NonNull;
import android.annotation.SystemService;
import android.content.Context;
+import android.net.Uri;
import android.os.Handler;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.ArrayMap;
+import android.util.Pair;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Executor;
/**
- * @hide
+ * Class to handle interactions with {@link Slice}s.
+ * <p>
+ * The SliceManager manages permissions and pinned state for slices.
*/
@SystemService(Context.SLICE_SERVICE)
public class SliceManager {
private final ISliceManager mService;
private final Context mContext;
+ private final ArrayMap<Pair<Uri, SliceCallback>, ISliceListener> mListenerLookup =
+ new ArrayMap<>();
+ /**
+ * @hide
+ */
public SliceManager(Context context, Handler handler) throws ServiceNotFoundException {
mContext = context;
mService = ISliceManager.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.SLICE_SERVICE));
}
+
+ /**
+ * Adds a callback to a specific slice uri.
+ * <p>
+ * This is a convenience that performs a few slice actions at once. It will put
+ * the slice in a pinned state since there is a callback attached. It will also
+ * listen for content changes, when a content change observes, the android system
+ * will bind the new slice and provide it to all registered {@link SliceCallback}s.
+ *
+ * @param uri The uri of the slice being listened to.
+ * @param callback The listener that should receive the callbacks.
+ * @param specs The list of supported {@link SliceSpec}s of the callback.
+ * @see SliceProvider#onSlicePinned(Uri)
+ */
+ public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback,
+ @NonNull List<SliceSpec> specs) {
+ registerSliceCallback(uri, callback, specs, Handler.getMain());
+ }
+
+ /**
+ * Adds a callback to a specific slice uri.
+ * <p>
+ * This is a convenience that performs a few slice actions at once. It will put
+ * the slice in a pinned state since there is a callback attached. It will also
+ * listen for content changes, when a content change observes, the android system
+ * will bind the new slice and provide it to all registered {@link SliceCallback}s.
+ *
+ * @param uri The uri of the slice being listened to.
+ * @param callback The listener that should receive the callbacks.
+ * @param specs The list of supported {@link SliceSpec}s of the callback.
+ * @see SliceProvider#onSlicePinned(Uri)
+ */
+ public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback,
+ @NonNull List<SliceSpec> specs, Handler handler) {
+ try {
+ mService.addSliceListener(uri, mContext.getPackageName(),
+ getListener(uri, callback, new ISliceListener.Stub() {
+ @Override
+ public void onSliceUpdated(Slice s) throws RemoteException {
+ handler.post(() -> callback.onSliceUpdated(s));
+ }
+ }), specs.toArray(new SliceSpec[specs.size()]));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Adds a callback to a specific slice uri.
+ * <p>
+ * This is a convenience that performs a few slice actions at once. It will put
+ * the slice in a pinned state since there is a callback attached. It will also
+ * listen for content changes, when a content change observes, the android system
+ * will bind the new slice and provide it to all registered {@link SliceCallback}s.
+ *
+ * @param uri The uri of the slice being listened to.
+ * @param callback The listener that should receive the callbacks.
+ * @param specs The list of supported {@link SliceSpec}s of the callback.
+ * @see SliceProvider#onSlicePinned(Uri)
+ */
+ public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback,
+ @NonNull List<SliceSpec> specs, Executor executor) {
+ try {
+ mService.addSliceListener(uri, mContext.getPackageName(),
+ getListener(uri, callback, new ISliceListener.Stub() {
+ @Override
+ public void onSliceUpdated(Slice s) throws RemoteException {
+ executor.execute(() -> callback.onSliceUpdated(s));
+ }
+ }), specs.toArray(new SliceSpec[specs.size()]));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private ISliceListener getListener(Uri uri, SliceCallback callback,
+ ISliceListener listener) {
+ Pair<Uri, SliceCallback> key = new Pair<>(uri, callback);
+ if (mListenerLookup.containsKey(key)) {
+ try {
+ mService.removeSliceListener(uri, mContext.getPackageName(),
+ mListenerLookup.get(key));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ mListenerLookup.put(key, listener);
+ return listener;
+ }
+
+ /**
+ * Removes a callback for a specific slice uri.
+ * <p>
+ * Removes the app from the pinned state (if there are no other apps/callbacks pinning it)
+ * in addition to removing the callback.
+ *
+ * @param uri The uri of the slice being listened to
+ * @param callback The listener that should no longer receive callbacks.
+ * @see #registerSliceCallback
+ */
+ public void unregisterSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback) {
+ try {
+ mService.removeSliceListener(uri, mContext.getPackageName(),
+ mListenerLookup.remove(new Pair<>(uri, callback)));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Ensures that a slice is in a pinned state.
+ * <p>
+ * Pinned state is not persisted across reboots, so apps are expected to re-pin any slices
+ * they still care about after a reboot.
+ *
+ * @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)
+ */
+ public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
+ try {
+ mService.pinSlice(mContext.getPackageName(), uri,
+ specs.toArray(new SliceSpec[specs.size()]));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Remove a pin for a slice.
+ * <p>
+ * If the slice has no other pins/callbacks then the slice will be unpinned.
+ *
+ * @param uri The uri of the slice being unpinned.
+ * @see #pinSlice
+ * @see SliceProvider#onSliceUnpinned(Uri)
+ */
+ public void unpinSlice(@NonNull Uri uri) {
+ try {
+ mService.unpinSlice(mContext.getPackageName(), uri);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasSliceAccess() {
+ try {
+ return mService.hasSliceAccess(mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the current set of specs for a pinned slice.
+ * <p>
+ * This is the set of specs supported for a specific pinned slice. It will take
+ * into account all clients and returns only specs supported by all.
+ * @see SliceSpec
+ */
+ public @NonNull List<SliceSpec> getPinnedSpecs(Uri uri) {
+ try {
+ return Arrays.asList(mService.getPinnedSpecs(uri, mContext.getPackageName()));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Class that listens to changes in {@link Slice}s.
+ */
+ public interface SliceCallback {
+
+ /**
+ * Called when slice is updated.
+ *
+ * @param s The updated slice.
+ * @see #registerSliceCallback
+ */
+ void onSliceUpdated(Slice s);
+ }
}
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index ac5365c..8483931 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -105,6 +105,14 @@
/**
* @hide
*/
+ public static final String METHOD_PIN = "pin";
+ /**
+ * @hide
+ */
+ public static final String METHOD_UNPIN = "unpin";
+ /**
+ * @hide
+ */
public static final String EXTRA_INTENT = "slice_intent";
/**
* @hide
@@ -143,6 +151,38 @@
}
/**
+ * Called to inform an app that a slice has been pinned.
+ * <p>
+ * Pinning is a way that slice hosts use to notify apps of which slices
+ * they care about updates for. When a slice is pinned the content is
+ * expected to be relatively fresh and kept up to date.
+ * <p>
+ * Being pinned does not provide any escalated privileges for the slice
+ * provider. So apps should do things such as turn on syncing or schedule
+ * a job in response to a onSlicePinned.
+ * <p>
+ * Pinned state is not persisted through a reboot, and apps can expect a
+ * new call to onSlicePinned for any slices that should remain pinned
+ * after a reboot occurs.
+ *
+ * @param sliceUri The uri of the slice being unpinned.
+ * @see #onSliceUnpinned(Uri)
+ */
+ public void onSlicePinned(Uri sliceUri) {
+ }
+
+ /**
+ * Called to inform an app that a slices is no longer pinned.
+ * <p>
+ * This means that no other apps on the device care about updates to this
+ * slice anymore and therefore it is not important to be updated. Any syncs
+ * or jobs related to this slice should be cancelled.
+ * @see #onSlicePinned(Uri)
+ */
+ public void onSliceUnpinned(Uri sliceUri) {
+ }
+
+ /**
* This method must be overridden if an {@link IntentFilter} is specified on the SliceProvider.
* In that case, this method can be called and is expected to return a non-null Uri representing
* a slice. Otherwise this will throw {@link UnsupportedOperationException}.
@@ -221,6 +261,7 @@
getContext().enforceCallingPermission(permission.BIND_SLICE,
"Slice binding requires the permission BIND_SLICE");
Intent intent = extras.getParcelable(EXTRA_INTENT);
+ if (intent == null) return null;
Uri uri = onMapIntentToUri(intent);
List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
Bundle b = new Bundle();
@@ -231,10 +272,62 @@
b.putParcelable(EXTRA_SLICE, null);
}
return b;
+ } else if (method.equals(METHOD_PIN)) {
+ Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+ if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
+ getContext().enforceUriPermission(uri, permission.BIND_SLICE,
+ permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ "Slice binding requires the permission BIND_SLICE");
+ }
+ handlePinSlice(uri);
+ } else if (method.equals(METHOD_UNPIN)) {
+ Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+ if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
+ getContext().enforceUriPermission(uri, permission.BIND_SLICE,
+ permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ "Slice binding requires the permission BIND_SLICE");
+ }
+ handleUnpinSlice(uri);
}
return super.call(method, arg, extras);
}
+ private void handlePinSlice(Uri sliceUri) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ onSlicePinned(sliceUri);
+ } else {
+ CountDownLatch latch = new CountDownLatch(1);
+ Handler.getMain().post(() -> {
+ onSlicePinned(sliceUri);
+ latch.countDown();
+ });
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private void handleUnpinSlice(Uri sliceUri) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ onSliceUnpinned(sliceUri);
+ } else {
+ CountDownLatch latch = new CountDownLatch(1);
+ Handler.getMain().post(() -> {
+ onSliceUnpinned(sliceUri);
+ latch.countDown();
+ });
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
if (Looper.myLooper() == Looper.getMainLooper()) {
return onBindSliceStrict(sliceUri, supportedSpecs);
diff --git a/core/java/android/app/slice/SliceSpec.aidl b/core/java/android/app/slice/SliceSpec.aidl
new file mode 100644
index 0000000..92e98b7
--- /dev/null
+++ b/core/java/android/app/slice/SliceSpec.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.slice;
+
+parcelable SliceSpec;
diff --git a/core/java/android/app/slice/SliceSpec.java b/core/java/android/app/slice/SliceSpec.java
index 433b67e..8cc0384 100644
--- a/core/java/android/app/slice/SliceSpec.java
+++ b/core/java/android/app/slice/SliceSpec.java
@@ -103,6 +103,11 @@
return mType.equals(other.mType) && mRevision == other.mRevision;
}
+ @Override
+ public String toString() {
+ return String.format("SliceSpec{%s,%d}", mType, mRevision);
+ }
+
public static final Creator<SliceSpec> CREATOR = new Creator<SliceSpec>() {
@Override
public SliceSpec createFromParcel(Parcel source) {
diff --git a/core/java/android/app/timezone/Callback.java b/core/java/android/app/timezone/Callback.java
index aea8038..e3840be 100644
--- a/core/java/android/app/timezone/Callback.java
+++ b/core/java/android/app/timezone/Callback.java
@@ -30,9 +30,14 @@
public abstract class Callback {
@Retention(RetentionPolicy.SOURCE)
- @IntDef({SUCCESS, ERROR_UNKNOWN_FAILURE, ERROR_INSTALL_BAD_DISTRO_STRUCTURE,
- ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION, ERROR_INSTALL_RULES_TOO_OLD,
- ERROR_INSTALL_VALIDATION_ERROR})
+ @IntDef(prefix = { "SUCCESS", "ERROR_" }, value = {
+ SUCCESS,
+ ERROR_UNKNOWN_FAILURE,
+ ERROR_INSTALL_BAD_DISTRO_STRUCTURE,
+ ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION,
+ ERROR_INSTALL_RULES_TOO_OLD,
+ ERROR_INSTALL_VALIDATION_ERROR
+ })
public @interface AsyncResultCode {}
/**
diff --git a/core/java/android/app/timezone/RulesManager.java b/core/java/android/app/timezone/RulesManager.java
index 417e7d2..0a38eb9 100644
--- a/core/java/android/app/timezone/RulesManager.java
+++ b/core/java/android/app/timezone/RulesManager.java
@@ -69,7 +69,11 @@
private static final boolean DEBUG = false;
@Retention(RetentionPolicy.SOURCE)
- @IntDef({SUCCESS, ERROR_UNKNOWN_FAILURE, ERROR_OPERATION_IN_PROGRESS})
+ @IntDef(prefix = { "SUCCESS", "ERROR_" }, value = {
+ SUCCESS,
+ ERROR_UNKNOWN_FAILURE,
+ ERROR_OPERATION_IN_PROGRESS
+ })
public @interface ResultCode {}
/**
diff --git a/core/java/android/app/timezone/RulesState.java b/core/java/android/app/timezone/RulesState.java
index ec247eb..16309fa 100644
--- a/core/java/android/app/timezone/RulesState.java
+++ b/core/java/android/app/timezone/RulesState.java
@@ -63,11 +63,12 @@
public final class RulesState implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
- @IntDef({
+ @IntDef(prefix = { "STAGED_OPERATION_" }, value = {
STAGED_OPERATION_UNKNOWN,
STAGED_OPERATION_NONE,
STAGED_OPERATION_UNINSTALL,
- STAGED_OPERATION_INSTALL })
+ STAGED_OPERATION_INSTALL
+ })
private @interface StagedOperationType {}
/** Staged state could not be determined. */
@@ -80,10 +81,11 @@
public static final int STAGED_OPERATION_INSTALL = 3;
@Retention(RetentionPolicy.SOURCE)
- @IntDef({
+ @IntDef(prefix = { "DISTRO_STATUS_" }, value = {
DISTRO_STATUS_UNKNOWN,
DISTRO_STATUS_NONE,
- DISTRO_STATUS_INSTALLED })
+ DISTRO_STATUS_INSTALLED
+ })
private @interface DistroStatus {}
/** The current distro status could not be determined. */
diff --git a/core/java/android/app/usage/IStorageStatsManager.aidl b/core/java/android/app/usage/IStorageStatsManager.aidl
index 15e5ea5..7eacc89 100644
--- a/core/java/android/app/usage/IStorageStatsManager.aidl
+++ b/core/java/android/app/usage/IStorageStatsManager.aidl
@@ -22,6 +22,7 @@
/** {@hide} */
interface IStorageStatsManager {
boolean isQuotaSupported(String volumeUuid, String callingPackage);
+ boolean isReservedSupported(String volumeUuid, String callingPackage);
long getTotalBytes(String volumeUuid, String callingPackage);
long getFreeBytes(String volumeUuid, String callingPackage);
long getCacheBytes(String volumeUuid, String callingPackage);
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 4fbbdf2..f089c127 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -19,6 +19,8 @@
import android.app.usage.UsageEvents;
import android.content.pm.ParceledListSlice;
+import java.util.Map;
+
/**
* System private API for talking with the UsageStatsManagerService.
*
@@ -38,4 +40,6 @@
in String[] annotations, String action);
int getAppStandbyBucket(String packageName, String callingPackage, int userId);
void setAppStandbyBucket(String packageName, int bucket, int userId);
+ Map getAppStandbyBuckets(String callingPackage, int userId);
+ void setAppStandbyBuckets(in Map appBuckets, int userId);
}
diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java
index 222e9a0..2e44a63 100644
--- a/core/java/android/app/usage/NetworkStats.java
+++ b/core/java/android/app/usage/NetworkStats.java
@@ -129,7 +129,11 @@
*/
public static class Bucket {
/** @hide */
- @IntDef({STATE_ALL, STATE_DEFAULT, STATE_FOREGROUND})
+ @IntDef(prefix = { "STATE_" }, value = {
+ STATE_ALL,
+ STATE_DEFAULT,
+ STATE_FOREGROUND
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface State {}
@@ -164,7 +168,11 @@
public static final int UID_TETHERING = TrafficStats.UID_TETHERING;
/** @hide */
- @IntDef({METERED_ALL, METERED_NO, METERED_YES})
+ @IntDef(prefix = { "METERED_" }, value = {
+ METERED_ALL,
+ METERED_NO,
+ METERED_YES
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface Metered {}
@@ -187,7 +195,11 @@
public static final int METERED_YES = 0x2;
/** @hide */
- @IntDef({ROAMING_ALL, ROAMING_NO, ROAMING_YES})
+ @IntDef(prefix = { "ROAMING_" }, value = {
+ ROAMING_ALL,
+ ROAMING_NO,
+ ROAMING_YES
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface Roaming {}
diff --git a/core/java/android/app/usage/StorageStatsManager.java b/core/java/android/app/usage/StorageStatsManager.java
index 3d187ec..a86c27a 100644
--- a/core/java/android/app/usage/StorageStatsManager.java
+++ b/core/java/android/app/usage/StorageStatsManager.java
@@ -78,6 +78,16 @@
return isQuotaSupported(convert(uuid));
}
+ /** {@hide} */
+ @TestApi
+ public boolean isReservedSupported(@NonNull UUID storageUuid) {
+ try {
+ return mService.isReservedSupported(convert(storageUuid), mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Return the total size of the underlying physical media that is hosting
* this storage volume.
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 8200414..f04e907 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -110,10 +110,9 @@
public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
/** @hide */
- @IntDef(flag = true,
- value = {
- FLAG_IS_PACKAGE_INSTANT_APP,
- })
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+ FLAG_IS_PACKAGE_INSTANT_APP,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface EventFlags {}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index d614b20..edb6a74 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -151,7 +151,7 @@
public static final String REASON_PREDICTED = "predicted";
/** @hide */
- @IntDef(flag = false, value = {
+ @IntDef(flag = false, prefix = { "STANDBY_BUCKET_" }, value = {
STANDBY_BUCKET_EXEMPTED,
STANDBY_BUCKET_ACTIVE,
STANDBY_BUCKET_WORKING_SET,
@@ -324,11 +324,11 @@
* state of the app based on app usage patterns. Standby buckets determine how much an app will
* be restricted from running background tasks such as jobs, alarms and certain PendingIntent
* callbacks.
- * Restrictions increase progressively from {@link #STANDBY_BUCKET_ACTIVE} to
+ * <p>Restrictions increase progressively from {@link #STANDBY_BUCKET_ACTIVE} to
* {@link #STANDBY_BUCKET_RARE}, with {@link #STANDBY_BUCKET_ACTIVE} being the least
* restrictive. The battery level of the device might also affect the restrictions.
*
- * @return the current standby bucket of the calling app.
+ * @return the current standby bucket of the calling app. One of STANDBY_BUCKET_* constants.
*/
public @StandbyBuckets int getAppStandbyBucket() {
try {
@@ -359,7 +359,13 @@
/**
* {@hide}
- * Changes the app standby state to the provided bucket.
+ * Changes an app's standby bucket to the provided value. The caller can only set the standby
+ * bucket for a different app than itself.
+ * @param packageName the package name of the app to set the bucket for. A SecurityException
+ * will be thrown if the package name is that of the caller.
+ * @param bucket the standby bucket to set it to, which should be one of STANDBY_BUCKET_*.
+ * Setting a standby bucket outside of the range of STANDBY_BUCKET_ACTIVE to
+ * STANDBY_BUCKET_NEVER will result in a SecurityException.
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.CHANGE_APP_IDLE_STATE)
@@ -373,6 +379,39 @@
/**
* {@hide}
+ * Returns the current standby bucket of every app that has a bucket assigned to it.
+ * The caller must hold the permission android.permission.PACKAGE_USAGE_STATS. The key of the
+ * returned Map is the package name and the value is the bucket assigned to the package.
+ * @see #getAppStandbyBucket()
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
+ public Map<String, Integer> getAppStandbyBuckets() {
+ try {
+ return (Map<String, Integer>) mService.getAppStandbyBuckets(
+ mContext.getOpPackageName(), mContext.getUserId());
+ } catch (RemoteException e) {
+ }
+ return Collections.EMPTY_MAP;
+ }
+
+ /**
+ * {@hide}
+ * Changes the app standby bucket for multiple apps at once. The Map is keyed by the package
+ * name and the value is one of STANDBY_BUCKET_*.
+ * @param appBuckets a map of package name to bucket value.
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.CHANGE_APP_IDLE_STATE)
+ public void setAppStandbyBuckets(Map<String, Integer> appBuckets) {
+ try {
+ mService.setAppStandbyBuckets(appBuckets, mContext.getUserId());
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * {@hide}
* Temporarily whitelist the specified app for a short duration. This is to allow an app
* receiving a high priority message to be able to access the network and acquire wakelocks
* even if the device is in power-save mode or the app is currently considered inactive.
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index c7be0f3..3290d57 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -156,7 +156,7 @@
"android.bluetooth.adapter.extra.PREVIOUS_STATE";
/** @hide */
- @IntDef({
+ @IntDef(prefix = { "STATE_" }, value = {
STATE_OFF,
STATE_TURNING_ON,
STATE_ON,
@@ -357,7 +357,11 @@
"android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE";
/** @hide */
- @IntDef({SCAN_MODE_NONE, SCAN_MODE_CONNECTABLE, SCAN_MODE_CONNECTABLE_DISCOVERABLE})
+ @IntDef(prefix = { "SCAN_" }, value = {
+ SCAN_MODE_NONE,
+ SCAN_MODE_CONNECTABLE,
+ SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface ScanMode {}
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index 4a00486..2fab305 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -35,8 +35,6 @@
*
* <p>BluetoothHidDevice is a proxy object for controlling the Bluetooth HID Device Service via IPC.
* Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object.
- *
- * <p>{@hide}
*/
public final class BluetoothHidDevice implements BluetoothProfile {
@@ -87,7 +85,7 @@
*
* @see BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)
* @see BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])
- * @see BluetoothHidDeviceCallback#onIntrData(BluetoothDevice, byte, byte[])
+ * @see BluetoothHidDeviceCallback#onInterruptData(BluetoothDevice, byte, byte[])
*/
public static final byte REPORT_TYPE_INPUT = (byte) 1;
public static final byte REPORT_TYPE_OUTPUT = (byte) 2;
@@ -157,8 +155,8 @@
}
@Override
- public void onIntrData(BluetoothDevice device, byte reportId, byte[] data) {
- mCallback.onIntrData(device, reportId, data);
+ public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
+ mCallback.onInterruptData(device, reportId, data);
}
@Override
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
index 4609d52..c05df2d 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
@@ -26,8 +26,6 @@
* registration.
*
* <p>{@see BluetoothHidDevice}
- *
- * <p>{@hide}
*/
public final class BluetoothHidDeviceAppQosSettings implements Parcelable {
@@ -46,10 +44,8 @@
/**
* Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS
- * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. {@see <a
- * href="https://www.bluetooth.com/specifications/profiles-overview">
- * https://www.bluetooth.com/specifications/profiles-overview </a> Bluetooth HID Specfication
- * v1.1.1 Section 5.2 and Appendix D }
+ * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder.
+ * Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D for parameters.
*
* @param serviceType L2CAP service type
* @param tokenRate L2CAP token rate
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
index 2da64e5..562c559 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
@@ -28,8 +28,6 @@
* Android device can be discovered as a Bluetooth HID Device.
*
* <p>{@see BluetoothHidDevice}
- *
- * <p>{@hide}
*/
public final class BluetoothHidDeviceAppSdpSettings implements Parcelable {
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
index 6ed1965..e71b00f 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
@@ -24,8 +24,6 @@
* registration.
*
* <p>{@see BluetoothHidDevice}
- *
- * <p>{@hide}
*/
public abstract class BluetoothHidDeviceCallback {
@@ -108,8 +106,8 @@
* @param reportId Report Id.
* @param data Report data.
*/
- public void onIntrData(BluetoothDevice device, byte reportId, byte[] data) {
- Log.d(TAG, "onIntrData: device=" + device + " reportId=" + reportId);
+ public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
+ Log.d(TAG, "onInterruptData: device=" + device + " reportId=" + reportId);
}
/**
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index a1a9347..7944354 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -25,6 +25,10 @@
import android.os.RemoteException;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
/**
* The Android Bluetooth API is not finalized, and *will* change. Use at your
* own risk.
@@ -48,11 +52,10 @@
*
* @hide
*/
-public class BluetoothPbap {
+public class BluetoothPbap implements BluetoothProfile {
private static final String TAG = "BluetoothPbap";
- private static final boolean DBG = true;
- private static final boolean VDBG = false;
+ private static final boolean DBG = false;
/**
* Intent used to broadcast the change in connection state of the PBAP
@@ -111,9 +114,9 @@
private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
new IBluetoothStateChangeCallback.Stub() {
public void onBluetoothStateChange(boolean up) {
- if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
+ log("onBluetoothStateChange: up=" + up);
if (!up) {
- if (VDBG) Log.d(TAG, "Unbinding service...");
+ log("Unbinding service...");
synchronized (mConnection) {
try {
mService = null;
@@ -126,7 +129,7 @@
synchronized (mConnection) {
try {
if (mService == null) {
- if (VDBG) Log.d(TAG, "Binding service...");
+ log("Binding service...");
doBind();
}
} catch (Exception re) {
@@ -205,47 +208,60 @@
}
/**
- * Get the current state of the BluetoothPbap service.
- *
- * @return One of the STATE_ return codes, or {@link BluetoothProfile#STATE_DISCONNECTED}
- * if this proxy object is currently not connected to the Pbap service.
+ * {@inheritDoc}
*/
- public int getState() {
- if (VDBG) log("getState()");
+ @Override
+ public List<BluetoothDevice> getConnectedDevices() {
+ log("getConnectedDevices()");
final IBluetoothPbap service = mService;
- if (service != null) {
- try {
- return service.getState();
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) log(Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ }
+ try {
+ return service.getConnectedDevices();
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ return new ArrayList<BluetoothDevice>();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getConnectionState(BluetoothDevice device) {
+ log("getConnectionState: device=" + device);
+ final IBluetoothPbap service = mService;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ try {
+ return service.getConnectionState(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
}
return BluetoothProfile.STATE_DISCONNECTED;
}
/**
- * Get the currently connected remote Bluetooth device (PCE).
- *
- * @return The remote Bluetooth device, or null if not in connected or connecting state, or if
- * this proxy object is not connected to the Pbap service.
+ * {@inheritDoc}
*/
- public BluetoothDevice getClient() {
- if (VDBG) log("getClient()");
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ log("getDevicesMatchingConnectionStates: states=" + Arrays.toString(states));
final IBluetoothPbap service = mService;
- if (service != null) {
- try {
- return service.getClient();
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) log(Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
}
- return null;
+ try {
+ return service.getDevicesMatchingConnectionStates(states);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ return new ArrayList<BluetoothDevice>();
}
/**
@@ -253,20 +269,9 @@
* include connecting). Returns false if not connected, or if this proxy
* object is not currently connected to the Pbap service.
*/
+ // TODO: This is currently being used by SettingsLib and internal app.
public boolean isConnected(BluetoothDevice device) {
- if (VDBG) log("isConnected(" + device + ")");
- final IBluetoothPbap service = mService;
- if (service != null) {
- try {
- return service.isConnected(device);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) log(Log.getStackTraceString(new Throwable()));
- }
- return false;
+ return getConnectionState(device) == BluetoothAdapter.STATE_CONNECTED;
}
/**
@@ -274,47 +279,27 @@
* it may soon be made asynchronous. Returns false if this proxy object is
* not currently connected to the Pbap service.
*/
- public boolean disconnect() {
- if (DBG) log("disconnect()");
+ // TODO: This is currently being used by SettingsLib and will be used in the future.
+ // TODO: Must specify target device. Implement this in the service.
+ public boolean disconnect(BluetoothDevice device) {
+ log("disconnect()");
final IBluetoothPbap service = mService;
- if (service != null) {
- try {
- service.disconnect();
- return true;
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) log(Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ try {
+ service.disconnect(device);
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
}
return false;
}
- /**
- * Check class bits for possible PBAP support.
- * This is a simple heuristic that tries to guess if a device with the
- * given class bits might support PBAP. It is not accurate for all
- * devices. It tries to err on the side of false positives.
- *
- * @return True if this device might support PBAP.
- */
- public static boolean doesClassMatchSink(BluetoothClass btClass) {
- // TODO optimize the rule
- switch (btClass.getDeviceClass()) {
- case BluetoothClass.Device.COMPUTER_DESKTOP:
- case BluetoothClass.Device.COMPUTER_LAPTOP:
- case BluetoothClass.Device.COMPUTER_SERVER:
- case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
- return true;
- default:
- return false;
- }
- }
-
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) log("Proxy object connected");
+ log("Proxy object connected");
mService = IBluetoothPbap.Stub.asInterface(service);
if (mServiceListener != null) {
mServiceListener.onServiceConnected(BluetoothPbap.this);
@@ -322,7 +307,7 @@
}
public void onServiceDisconnected(ComponentName className) {
- if (DBG) log("Proxy object disconnected");
+ log("Proxy object disconnected");
mService = null;
if (mServiceListener != null) {
mServiceListener.onServiceDisconnected();
@@ -331,6 +316,8 @@
};
private static void log(String msg) {
- Log.d(TAG, msg);
+ if (DBG) {
+ Log.d(TAG, msg);
+ }
}
}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 46a230b..df2028a 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -153,8 +153,6 @@
/**
* HID Device
- *
- * @hide
*/
public static final int HID_DEVICE = 19;
@@ -254,4 +252,28 @@
*/
public void onServiceDisconnected(int profile);
}
+
+ /**
+ * Convert an integer value of connection state into human readable string
+ *
+ * @param connectionState - One of {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+ * {@link #STATE_CONNECTED}, or {@link #STATE_DISCONNECTED}
+ * @return a string representation of the connection state, STATE_UNKNOWN if the state
+ * is not defined
+ * @hide
+ */
+ static String getConnectionStateName(int connectionState) {
+ switch (connectionState) {
+ case STATE_DISCONNECTED:
+ return "STATE_DISCONNECTED";
+ case STATE_CONNECTING:
+ return "STATE_CONNECTING";
+ case STATE_CONNECTED:
+ return "STATE_CONNECTED";
+ case STATE_DISCONNECTING:
+ return "STATE_DISCONNECTING";
+ default:
+ return "STATE_UNKNOWN";
+ }
+ }
}
diff --git a/core/java/android/companion/DeviceFilter.java b/core/java/android/companion/DeviceFilter.java
index 9b4fdfd..10135a4 100644
--- a/core/java/android/companion/DeviceFilter.java
+++ b/core/java/android/companion/DeviceFilter.java
@@ -62,7 +62,11 @@
}
/** @hide */
- @IntDef({MEDIUM_TYPE_BLUETOOTH, MEDIUM_TYPE_BLUETOOTH_LE, MEDIUM_TYPE_WIFI})
+ @IntDef(prefix = { "MEDIUM_TYPE_" }, value = {
+ MEDIUM_TYPE_BLUETOOTH,
+ MEDIUM_TYPE_BLUETOOTH_LE,
+ MEDIUM_TYPE_WIFI
+ })
@Retention(RetentionPolicy.SOURCE)
@interface MediumType {}
}
diff --git a/core/java/android/content/AsyncTaskLoader.java b/core/java/android/content/AsyncTaskLoader.java
index 6e9f09c..c44e356 100644
--- a/core/java/android/content/AsyncTaskLoader.java
+++ b/core/java/android/content/AsyncTaskLoader.java
@@ -50,7 +50,8 @@
*
* @param <D> the data type to be loaded.
*
- * @deprecated Use {@link android.support.v4.content.AsyncTaskLoader}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link android.support.v4.content.AsyncTaskLoader}
*/
@Deprecated
public abstract class AsyncTaskLoader<D> extends Loader<D> {
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 9ccc552..8d2e141 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -335,7 +335,7 @@
public static final String EXTRA_HONORED_ARGS = "android.content.extra.HONORED_ARGS";
/** @hide */
- @IntDef(flag = false, value = {
+ @IntDef(flag = false, prefix = { "QUERY_SORT_DIRECTION_" }, value = {
QUERY_SORT_DIRECTION_ASCENDING,
QUERY_SORT_DIRECTION_DESCENDING
})
@@ -482,11 +482,10 @@
public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
/** @hide */
- @IntDef(flag = true,
- value = {
- NOTIFY_SYNC_TO_NETWORK,
- NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS
- })
+ @IntDef(flag = true, prefix = { "NOTIFY_" }, value = {
+ NOTIFY_SYNC_TO_NETWORK,
+ NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface NotifyFlags {}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a474330..137c169 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -51,6 +51,7 @@
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
import android.os.StatFs;
@@ -75,6 +76,7 @@
import java.io.InputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
/**
* Interface to global information about an application environment. This is
@@ -219,17 +221,16 @@
public static final int MODE_NO_LOCALIZED_COLLATORS = 0x0010;
/** @hide */
- @IntDef(flag = true,
- value = {
- BIND_AUTO_CREATE,
- BIND_DEBUG_UNBIND,
- BIND_NOT_FOREGROUND,
- BIND_ABOVE_CLIENT,
- BIND_ALLOW_OOM_MANAGEMENT,
- BIND_WAIVE_PRIORITY,
- BIND_IMPORTANT,
- BIND_ADJUST_WITH_ACTIVITY
- })
+ @IntDef(flag = true, prefix = { "BIND_" }, value = {
+ BIND_AUTO_CREATE,
+ BIND_DEBUG_UNBIND,
+ BIND_NOT_FOREGROUND,
+ BIND_ABOVE_CLIENT,
+ BIND_ALLOW_OOM_MANAGEMENT,
+ BIND_WAIVE_PRIORITY,
+ BIND_IMPORTANT,
+ BIND_ADJUST_WITH_ACTIVITY
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface BindServiceFlags {}
@@ -404,10 +405,9 @@
public static final int BIND_EXTERNAL_SERVICE = 0x80000000;
/** @hide */
- @IntDef(flag = true,
- value = {
- RECEIVER_VISIBLE_TO_INSTANT_APPS
- })
+ @IntDef(flag = true, prefix = { "RECEIVER_VISIBLE_" }, value = {
+ RECEIVER_VISIBLE_TO_INSTANT_APPS
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface RegisterReceiverFlags {}
@@ -462,6 +462,16 @@
public abstract Looper getMainLooper();
/**
+ * Return an {@link Executor} that will run enqueued tasks on the main
+ * thread associated with this context. This is the thread used to dispatch
+ * calls to application components (activities, services, etc).
+ */
+ public Executor getMainExecutor() {
+ // This is pretty inefficient, which is why ContextImpl overrides it
+ return new HandlerExecutor(new Handler(getMainLooper()));
+ }
+
+ /**
* Return the context of the single, global Application object of the
* current process. This generally should only be used if you need a
* Context whose lifecycle is separate from the current context, that is
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 85acdc6..67de4fe 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -45,6 +45,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.concurrent.Executor;
/**
* Proxying implementation of Context that simply delegates all of its calls to
@@ -103,7 +104,12 @@
public Looper getMainLooper() {
return mBase.getMainLooper();
}
-
+
+ @Override
+ public Executor getMainExecutor() {
+ return mBase.getMainExecutor();
+ }
+
@Override
public Context getApplicationContext() {
return mBase.getApplicationContext();
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
index 7f24c51..5a08636 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -39,7 +39,8 @@
* {@link #setSelectionArgs(String[])}, {@link #setSortOrder(String)},
* and {@link #setProjection(String[])}.
*
- * @deprecated Use {@link android.support.v4.content.CursorLoader}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link android.support.v4.content.CursorLoader}
*/
@Deprecated
public class CursorLoader extends AsyncTaskLoader<Cursor> {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6fb1afd..e940769 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5690,11 +5690,14 @@
private static final int COPY_MODE_HISTORY = 2;
/** @hide */
- @IntDef(value = {COPY_MODE_ALL, COPY_MODE_FILTER, COPY_MODE_HISTORY})
+ @IntDef(prefix = { "COPY_MODE_" }, value = {
+ COPY_MODE_ALL,
+ COPY_MODE_FILTER,
+ COPY_MODE_HISTORY
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface CopyMode {}
-
/**
* Create an empty intent.
*/
@@ -8966,17 +8969,16 @@
}
/** @hide */
- @IntDef(flag = true,
- value = {
- FILL_IN_ACTION,
- FILL_IN_DATA,
- FILL_IN_CATEGORIES,
- FILL_IN_COMPONENT,
- FILL_IN_PACKAGE,
- FILL_IN_SOURCE_BOUNDS,
- FILL_IN_SELECTOR,
- FILL_IN_CLIP_DATA
- })
+ @IntDef(flag = true, prefix = { "FILL_IN_" }, value = {
+ FILL_IN_ACTION,
+ FILL_IN_DATA,
+ FILL_IN_CATEGORIES,
+ FILL_IN_COMPONENT,
+ FILL_IN_PACKAGE,
+ FILL_IN_SOURCE_BOUNDS,
+ FILL_IN_SELECTOR,
+ FILL_IN_CLIP_DATA
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface FillInFlags {}
diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java
index 80f9a14..b0555d4 100644
--- a/core/java/android/content/Loader.java
+++ b/core/java/android/content/Loader.java
@@ -49,7 +49,8 @@
*
* @param <D> The result returned when the load is complete
*
- * @deprecated Use {@link android.support.v4.content.Loader}
+ * @deprecated Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link android.support.v4.content.Loader}
*/
@Deprecated
public class Loader<D> {
@@ -561,4 +562,4 @@
writer.print(" mReset="); writer.println(mReset);
}
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index f8cdce6..1461711 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -262,10 +262,10 @@
public static final int COLOR_MODE_HDR = 2;
/** @hide */
- @IntDef({
- COLOR_MODE_DEFAULT,
- COLOR_MODE_WIDE_COLOR_GAMUT,
- COLOR_MODE_HDR,
+ @IntDef(prefix = { "COLOR_MODE_" }, value = {
+ COLOR_MODE_DEFAULT,
+ COLOR_MODE_WIDE_COLOR_GAMUT,
+ COLOR_MODE_HDR,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ColorMode {}
@@ -492,7 +492,7 @@
public int flags;
/** @hide */
- @IntDef({
+ @IntDef(prefix = { "SCREEN_ORIENTATION_" }, value = {
SCREEN_ORIENTATION_UNSET,
SCREEN_ORIENTATION_UNSPECIFIED,
SCREEN_ORIENTATION_LANDSCAPE,
@@ -638,25 +638,24 @@
public int screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
/** @hide */
- @IntDef(flag = true,
- value = {
- CONFIG_MCC,
- CONFIG_MNC,
- CONFIG_LOCALE,
- CONFIG_TOUCHSCREEN,
- CONFIG_KEYBOARD,
- CONFIG_KEYBOARD_HIDDEN,
- CONFIG_NAVIGATION,
- CONFIG_ORIENTATION,
- CONFIG_SCREEN_LAYOUT,
- CONFIG_UI_MODE,
- CONFIG_SCREEN_SIZE,
- CONFIG_SMALLEST_SCREEN_SIZE,
- CONFIG_DENSITY,
- CONFIG_LAYOUT_DIRECTION,
- CONFIG_COLOR_MODE,
- CONFIG_FONT_SCALE,
- })
+ @IntDef(flag = true, prefix = { "CONFIG_" }, value = {
+ CONFIG_MCC,
+ CONFIG_MNC,
+ CONFIG_LOCALE,
+ CONFIG_TOUCHSCREEN,
+ CONFIG_KEYBOARD,
+ CONFIG_KEYBOARD_HIDDEN,
+ CONFIG_NAVIGATION,
+ CONFIG_ORIENTATION,
+ CONFIG_SCREEN_LAYOUT,
+ CONFIG_UI_MODE,
+ CONFIG_SCREEN_SIZE,
+ CONFIG_SMALLEST_SCREEN_SIZE,
+ CONFIG_DENSITY,
+ CONFIG_LAYOUT_DIRECTION,
+ CONFIG_COLOR_MODE,
+ CONFIG_FONT_SCALE,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface Config {}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 84b1ff3..15e119b 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
@@ -952,6 +953,13 @@
public int targetSandboxVersion;
/**
+ * The factory of this package, as specified by the <manifest>
+ * tag's {@link android.R.styleable#AndroidManifestApplication_appComponentFactory}
+ * attribute.
+ */
+ public String appComponentFactory;
+
+ /**
* The category of this app. Categories are used to cluster multiple apps
* together into meaningful groups, such as when summarizing battery,
* network, or disk usage. Apps should only define this value when they fit
@@ -1267,6 +1275,7 @@
targetSandboxVersion = orig.targetSandboxVersion;
classLoaderName = orig.classLoaderName;
splitClassLoaderNames = orig.splitClassLoaderNames;
+ appComponentFactory = orig.appComponentFactory;
}
public String toString() {
@@ -1339,6 +1348,7 @@
dest.writeStringArray(splitClassLoaderNames);
dest.writeInt(compileSdkVersion);
dest.writeString(compileSdkVersionCodename);
+ dest.writeString(appComponentFactory);
}
public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1408,6 +1418,7 @@
splitClassLoaderNames = source.readStringArray();
compileSdkVersion = source.readInt();
compileSdkVersionCodename = source.readString();
+ appComponentFactory = source.readString();
}
/**
@@ -1582,6 +1593,11 @@
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
}
+ /** @hide */
+ public boolean isTargetingDeprecatedSdkVersion() {
+ return targetSdkVersion < Build.VERSION.MIN_SUPPORTED_TARGET_SDK_INT;
+ }
+
/**
* Returns whether or not this application was installed as a virtual preload.
*/
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index d09ba0b..b4a7eec 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -343,14 +343,13 @@
public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
/** @hide */
- @IntDef(flag = true,
- value = {
- FLAG_MATCH_DYNAMIC,
- FLAG_MATCH_PINNED,
- FLAG_MATCH_MANIFEST,
- FLAG_GET_KEY_FIELDS_ONLY,
- FLAG_MATCH_MANIFEST,
- })
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+ FLAG_MATCH_DYNAMIC,
+ FLAG_MATCH_PINNED,
+ FLAG_MATCH_MANIFEST,
+ FLAG_GET_KEY_FIELDS_ONLY,
+ FLAG_MATCH_MANIFEST,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface QueryFlags {}
@@ -1380,7 +1379,10 @@
public static final int REQUEST_TYPE_APPWIDGET = 2;
/** @hide */
- @IntDef(value = {REQUEST_TYPE_SHORTCUT})
+ @IntDef(prefix = { "REQUEST_TYPE_" }, value = {
+ REQUEST_TYPE_SHORTCUT,
+ REQUEST_TYPE_APPWIDGET
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface RequestType {}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index fb1b885..21e203b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -32,7 +32,6 @@
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
@@ -87,7 +86,7 @@
import android.util.SparseArray;
import android.util.TypedValue;
import android.util.apk.ApkSignatureSchemeV2Verifier;
-import android.util.jar.StrictJarFile;
+import android.util.apk.ApkSignatureVerifier;
import android.view.Gravity;
import com.android.internal.R;
@@ -106,12 +105,10 @@
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
-import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
@@ -129,8 +126,6 @@
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.zip.ZipEntry;
/**
* Parser for package files (APKs) on disk. This supports apps packaged either
@@ -173,7 +168,7 @@
// TODO: refactor "codePath" to "apkPath"
/** File name in an APK for the Android manifest. */
- private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
+ public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
/** Path prefix for apps on expanded storage */
private static final String MNT_EXPAND = "/mnt/expand/";
@@ -821,23 +816,6 @@
return pi;
}
- private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry entry)
- throws PackageParserException {
- InputStream is = null;
- try {
- // We must read the stream for the JarEntry to retrieve
- // its certificates.
- is = jarFile.getInputStream(entry);
- readFullyIgnoringContents(is);
- return jarFile.getCertificateChains(entry);
- } catch (IOException | RuntimeException e) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
- "Failed reading " + entry.getName() + " in " + jarFile, e);
- } finally {
- IoUtils.closeQuietly(is);
- }
- }
-
public static final int PARSE_MUST_BE_APK = 1 << 0;
public static final int PARSE_IGNORE_PROCESSES = 1 << 1;
public static final int PARSE_FORWARD_LOCK = 1 << 2;
@@ -1517,7 +1495,7 @@
pkg.mCertificates = certificates;
try {
- pkg.mSignatures = convertToSignatures(certificates);
+ pkg.mSignatures = ApkSignatureVerifier.convertToSignatures(certificates);
} catch (CertificateEncodingException e) {
// certificates weren't encoded properly; something went wrong
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
@@ -1580,157 +1558,40 @@
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
- // Try to verify the APK using APK Signature Scheme v2.
- boolean verified = false;
- {
- Certificate[][] allSignersCerts = null;
- Signature[] signatures = null;
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV2");
- allSignersCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
- signatures = convertToSignatures(allSignersCerts);
- // APK verified using APK Signature Scheme v2.
- verified = true;
- } catch (ApkSignatureSchemeV2Verifier.SignatureNotFoundException e) {
- // No APK Signature Scheme v2 signature found
- if ((parseFlags & PARSE_IS_EPHEMERAL) != 0) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "No APK Signature Scheme v2 signature in ephemeral package " + apkPath,
- e);
- }
- // Static shared libraries must use only the V2 signing scheme
- if (pkg.applicationInfo.isStaticSharedLibrary()) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "Static shared libs must use v2 signature scheme " + apkPath);
- }
- } catch (Exception e) {
- // APK Signature Scheme v2 signature was found but did not verify
+ boolean systemDir = (parseFlags & PARSE_IS_SYSTEM_DIR) != 0;
+ int minSignatureScheme = ApkSignatureVerifier.VERSION_JAR_SIGNATURE_SCHEME;
+ if (pkg.applicationInfo.isStaticSharedLibrary()) {
+ // must use v2 signing scheme
+ minSignatureScheme = ApkSignatureVerifier.VERSION_APK_SIGNATURE_SCHEME_V2;
+ }
+ ApkSignatureVerifier.Result verified =
+ ApkSignatureVerifier.verify(apkPath, minSignatureScheme, systemDir);
+ if (verified.signatureSchemeVersion
+ < ApkSignatureVerifier.VERSION_APK_SIGNATURE_SCHEME_V2) {
+ // TODO (b/68860689): move this logic to packagemanagerserivce
+ if ((parseFlags & PARSE_IS_EPHEMERAL) != 0) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "Failed to collect certificates from " + apkPath
- + " using APK Signature Scheme v2",
- e);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- if (verified) {
- if (pkg.mCertificates == null) {
- pkg.mCertificates = allSignersCerts;
- pkg.mSignatures = signatures;
- pkg.mSigningKeys = new ArraySet<>(allSignersCerts.length);
- for (int i = 0; i < allSignersCerts.length; i++) {
- Certificate[] signerCerts = allSignersCerts[i];
- Certificate signerCert = signerCerts[0];
- pkg.mSigningKeys.add(signerCert.getPublicKey());
- }
- } else {
- if (!Signature.areExactMatch(pkg.mSignatures, signatures)) {
- throw new PackageParserException(
- INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
- apkPath + " has mismatched certificates");
- }
- }
- // Not yet done, because we need to confirm that AndroidManifest.xml exists and,
- // if requested, that classes.dex exists.
+ "No APK Signature Scheme v2 signature in ephemeral package " + apkPath);
}
}
-
- StrictJarFile jarFile = null;
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
- // Ignore signature stripping protections when verifying APKs from system partition.
- // For those APKs we only care about extracting signer certificates, and don't care
- // about verifying integrity.
- boolean signatureSchemeRollbackProtectionsEnforced =
- (parseFlags & PARSE_IS_SYSTEM_DIR) == 0;
- jarFile = new StrictJarFile(
- apkPath,
- !verified, // whether to verify JAR signature
- signatureSchemeRollbackProtectionsEnforced);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-
- // Always verify manifest, regardless of source
- final ZipEntry manifestEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
- if (manifestEntry == null) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Package " + apkPath + " has no manifest");
+ if (pkg.mCertificates == null) {
+ pkg.mCertificates = verified.certs;
+ pkg.mSignatures = verified.sigs;
+ pkg.mSigningKeys = new ArraySet<>(verified.certs.length);
+ for (int i = 0; i < verified.certs.length; i++) {
+ Certificate[] signerCerts = verified.certs[i];
+ Certificate signerCert = signerCerts[0];
+ pkg.mSigningKeys.add(signerCert.getPublicKey());
}
-
- // Optimization: early termination when APK already verified
- if (verified) {
- return;
+ } else {
+ if (!Signature.areExactMatch(pkg.mSignatures, verified.sigs)) {
+ throw new PackageParserException(
+ INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+ apkPath + " has mismatched certificates");
}
-
- // APK's integrity needs to be verified using JAR signature scheme.
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV1");
- final List<ZipEntry> toVerify = new ArrayList<>();
- toVerify.add(manifestEntry);
-
- // If we're parsing an untrusted package, verify all contents
- if ((parseFlags & PARSE_IS_SYSTEM_DIR) == 0) {
- final Iterator<ZipEntry> i = jarFile.iterator();
- while (i.hasNext()) {
- final ZipEntry entry = i.next();
-
- if (entry.isDirectory()) continue;
-
- final String entryName = entry.getName();
- if (entryName.startsWith("META-INF/")) continue;
- if (entryName.equals(ANDROID_MANIFEST_FILENAME)) continue;
-
- toVerify.add(entry);
- }
- }
-
- // Verify that entries are signed consistently with the first entry
- // we encountered. Note that for splits, certificates may have
- // already been populated during an earlier parse of a base APK.
- for (ZipEntry entry : toVerify) {
- final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
- if (ArrayUtils.isEmpty(entryCerts)) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "Package " + apkPath + " has no certificates at entry "
- + entry.getName());
- }
- final Signature[] entrySignatures = convertToSignatures(entryCerts);
-
- if (pkg.mCertificates == null) {
- pkg.mCertificates = entryCerts;
- pkg.mSignatures = entrySignatures;
- pkg.mSigningKeys = new ArraySet<PublicKey>();
- for (int i=0; i < entryCerts.length; i++) {
- pkg.mSigningKeys.add(entryCerts[i][0].getPublicKey());
- }
- } else {
- if (!Signature.areExactMatch(pkg.mSignatures, entrySignatures)) {
- throw new PackageParserException(
- INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, "Package " + apkPath
- + " has mismatched certificates at entry "
- + entry.getName());
- }
- }
- }
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- } catch (GeneralSecurityException e) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
- "Failed to collect certificates from " + apkPath, e);
- } catch (IOException | RuntimeException e) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "Failed to collect certificates from " + apkPath, e);
- } finally {
- closeQuietly(jarFile);
}
}
- private static Signature[] convertToSignatures(Certificate[][] certs)
- throws CertificateEncodingException {
- final Signature[] res = new Signature[certs.length];
- for (int i = 0; i < certs.length; i++) {
- res[i] = new Signature(certs[i]);
- }
- return res;
- }
-
private static AssetManager newConfiguredAssetManager() {
AssetManager assetManager = new AssetManager();
assetManager.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -3756,6 +3617,11 @@
}
ai.taskAffinity = buildTaskAffinityName(ai.packageName, ai.packageName,
str, outError);
+ String factory = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestApplication_appComponentFactory);
+ if (factory != null) {
+ ai.appComponentFactory = buildClassName(ai.packageName, factory, outError);
+ }
if (outError[0] == null) {
CharSequence pname;
@@ -6462,6 +6328,7 @@
mAdoptPermissions = dest.createStringArrayList();
mAppMetaData = dest.readBundle();
mVersionCode = dest.readInt();
+ mVersionCodeMajor = dest.readInt();
mVersionName = dest.readString();
if (mVersionName != null) {
mVersionName = mVersionName.intern();
@@ -6608,6 +6475,7 @@
dest.writeStringList(mAdoptPermissions);
dest.writeBundle(mAppMetaData);
dest.writeInt(mVersionCode);
+ dest.writeInt(mVersionCodeMajor);
dest.writeString(mVersionName);
dest.writeString(mSharedUserId);
dest.writeInt(mSharedUserLabel);
@@ -7645,33 +7513,6 @@
sCompatibilityModeEnabled = compatibilityModeEnabled;
}
- private static AtomicReference<byte[]> sBuffer = new AtomicReference<byte[]>();
-
- public static long readFullyIgnoringContents(InputStream in) throws IOException {
- byte[] buffer = sBuffer.getAndSet(null);
- if (buffer == null) {
- buffer = new byte[4096];
- }
-
- int n = 0;
- int count = 0;
- while ((n = in.read(buffer, 0, buffer.length)) != -1) {
- count += n;
- }
-
- sBuffer.set(buffer);
- return count;
- }
-
- public static void closeQuietly(StrictJarFile jarFile) {
- if (jarFile != null) {
- try {
- jarFile.close();
- } catch (Exception ignored) {
- }
- }
- }
-
public static class PackageParserException extends Exception {
public final int error;
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 2f1b256..33bc951 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -36,13 +36,11 @@
public final class SharedLibraryInfo implements Parcelable {
/** @hide */
- @IntDef(
- flag = true,
- value = {
- TYPE_BUILTIN,
- TYPE_DYNAMIC,
- TYPE_STATIC,
- })
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_BUILTIN,
+ TYPE_DYNAMIC,
+ TYPE_STATIC,
+ })
@Retention(RetentionPolicy.SOURCE)
@interface Type{}
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 9ff0775..8839cf9 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -109,8 +109,7 @@
public static final int FLAG_SHADOW = 1 << 12;
/** @hide */
- @IntDef(flag = true,
- value = {
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_DYNAMIC,
FLAG_PINNED,
FLAG_HAS_ICON_RES,
@@ -153,15 +152,14 @@
| CLONE_REMOVE_RES_NAMES;
/** @hide */
- @IntDef(flag = true,
- value = {
- CLONE_REMOVE_ICON,
- CLONE_REMOVE_INTENT,
- CLONE_REMOVE_NON_KEY_INFO,
- CLONE_REMOVE_RES_NAMES,
- CLONE_REMOVE_FOR_CREATOR,
- CLONE_REMOVE_FOR_LAUNCHER
- })
+ @IntDef(flag = true, prefix = { "CLONE_" }, value = {
+ CLONE_REMOVE_ICON,
+ CLONE_REMOVE_INTENT,
+ CLONE_REMOVE_NON_KEY_INFO,
+ CLONE_REMOVE_RES_NAMES,
+ CLONE_REMOVE_FOR_CREATOR,
+ CLONE_REMOVE_FOR_LAUNCHER
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface CloneFlags {}
@@ -212,7 +210,7 @@
public static final int DISABLED_REASON_OTHER_RESTORE_ISSUE = 103;
/** @hide */
- @IntDef(value = {
+ @IntDef(prefix = { "DISABLED_REASON_" }, value = {
DISABLED_REASON_NOT_DISABLED,
DISABLED_REASON_BY_APP,
DISABLED_REASON_APP_CHANGED,
@@ -220,7 +218,7 @@
DISABLED_REASON_BACKUP_NOT_SUPPORTED,
DISABLED_REASON_SIGNATURE_MISMATCH,
DISABLED_REASON_OTHER_RESTORE_ISSUE,
- })
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface DisabledReason{}
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 61b0eb0..30222b7 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -36,15 +36,26 @@
import java.util.List;
/**
- * The ShortcutManager manages an app's <em>shortcuts</em>. Shortcuts provide users with quick
- * access to activities other than an app's main activity in the currently-active launcher, provided
- * that the launcher supports app shortcuts. For example, an email app may publish the "compose new
- * email" action, which will directly open the compose activity. The {@link ShortcutInfo} class
- * contains information about each of the shortcuts themselves.
+ * The ShortcutManager performs operations on an app's set of <em>shortcuts</em>. The
+ * {@link ShortcutInfo} class contains information about each of the shortcuts themselves.
+ *
+ * <p>An app's shortcuts represent specific tasks and actions that users can perform within your
+ * app. When a user selects a shortcut in the currently-active launcher, your app opens an activity
+ * other than the app's starting activity, provided that the currently-active launcher supports app
+ * shortcuts.</p>
+ *
+ * <p>The types of shortcuts that you create for your app depend on the app's key use cases. For
+ * example, an email app may publish the "compose new email" shortcut, which allows the app to
+ * directly open the compose activity.</p>
+ *
+ * <p class="note"><b>Note:</b> Only main activities—activities that handle the
+ * {@link Intent#ACTION_MAIN} action and the {@link Intent#CATEGORY_LAUNCHER} category—can
+ * have shortcuts. If an app has multiple main activities, you need to define the set of shortcuts
+ * for <em>each</em> activity.
*
* <p>This page discusses the implementation details of the <code>ShortcutManager</code> class. For
- * guidance on performing operations on app shortcuts within your app, see the
- * <a href="/guide/topics/ui/shortcuts.html">App Shortcuts</a> feature guide.
+ * definitions of key terms and guidance on performing operations on shortcuts within your app, see
+ * the <a href="/guide/topics/ui/shortcuts.html">App Shortcuts</a> feature guide.
*
* <h3>Shortcut characteristics</h3>
*
@@ -69,8 +80,8 @@
* <ul>
* <li>The user removes it.
* <li>The publisher app associated with the shortcut is uninstalled.
- * <li>The user performs the clear data action on the publisher app from the device's
- * <b>Settings</b> app.
+ * <li>The user selects <b>Clear data</b> from the publisher app's <i>Storage</i> screen, within
+ * the system's <b>Settings</b> app.
* </ul>
*
* <p>Because the system performs
@@ -83,12 +94,17 @@
*
* <p>When the launcher displays an app's shortcuts, they should appear in the following order:
*
- * <ul>
- * <li>Static shortcuts (if {@link ShortcutInfo#isDeclaredInManifest()} is {@code true}),
- * and then show dynamic shortcuts (if {@link ShortcutInfo#isDynamic()} is {@code true}).
- * <li>Within each shortcut type (static and dynamic), sort the shortcuts in order of increasing
- * rank according to {@link ShortcutInfo#getRank()}.
- * </ul>
+ * <ol>
+ * <li><b>Static shortcuts:</b> Shortcuts whose {@link ShortcutInfo#isDeclaredInManifest()} method
+ * returns {@code true}.</li>
+ * <li><b>Dynamic shortcuts:</b> Shortcuts whose {@link ShortcutInfo#isDynamic()} method returns
+ * {@code true}.</li>
+ * </ol>
+ *
+ * <p>Within each shortcut type (static and dynamic), shortcuts are sorted in order of increasing
+ * rank according to {@link ShortcutInfo#getRank()}.</p>
+ *
+ * <h4>Shortcut ranks</h4>
*
* <p>Shortcut ranks are non-negative, sequential integers that determine the order in which
* shortcuts appear, assuming that the shortcuts are all in the same category. You can update ranks
@@ -103,64 +119,99 @@
*
* <h3>Options for static shortcuts</h3>
*
- * The following list includes descriptions for the different attributes within a static shortcut:
+ * The following list includes descriptions for the different attributes within a static shortcut.
+ * You must provide a value for {@code android:shortcutId} and {@code android:shortcutShortLabel};
+ * all other values are optional.
+ *
* <dl>
* <dt>{@code android:shortcutId}</dt>
- * <dd>Mandatory shortcut ID.
- * <p>
- * This must be a string literal.
- * A resource string, such as <code>@string/foo</code>, cannot be used.
+ * <dd><p>A string literal, which represents the shortcut when a {@code ShortcutManager} object
+ * performs operations on it.</p>
+ * <p class="note"><b>Note: </b>You cannot set this attribute's value to a resource string, such
+ * as <code>@string/foo</code>.</p>
* </dd>
*
* <dt>{@code android:enabled}</dt>
- * <dd>Default is {@code true}. Can be set to {@code false} in order
- * to disable a static shortcut that was published in a previous version and set a custom
- * disabled message. If a custom disabled message is not needed, then a static shortcut can
- * be simply removed from the XML file rather than keeping it with {@code enabled="false"}.</dd>
+ * <dd><p>Whether the user can interact with the shortcut from a supported launcher.</p>
+ * <p>The default value is {@code true}. If you set it to {@code false}, you should also set
+ * {@code android:shortcutDisabledMessage} to a message that explains why you've disabled the
+ * shortcut. If you don't think you need to provide such a message, it's easiest to just remove
+ * the shortcut from the XML file entirely, rather than changing the values of the shortcut's
+ * {@code android:enabled} and {@code android:shortcutDisabledMessage} attributes.
+ * </dd>
*
* <dt>{@code android:icon}</dt>
- * <dd>Shortcut icon.</dd>
+ * <dd><p>The <a href="/topic/performance/graphics/index.html">bitmap</a> or
+ * <a href="/guide/practices/ui_guidelines/icon_design_adaptive.html">adaptive icon</a> that the
+ * launcher uses when displaying the shortcut to the user. This value can be either the path to an
+ * image or the resource file that contains the image. Use adaptive icons whenever possible to
+ * improve performance and consistency.</p>
+ * <p class="note"><b>Note: </b>Shortcut icons cannot include
+ * <a href="/training/material/drawables.html#DrawableTint">tints</a>.
+ * </dd>
*
* <dt>{@code android:shortcutShortLabel}</dt>
- * <dd>Mandatory shortcut short label.
- * See {@link ShortcutInfo.Builder#setShortLabel(CharSequence)}.
- * <p>
- * This must be a resource string, such as <code>@string/shortcut_label</code>.
+ * <dd><p>A concise phrase that describes the shortcut's purpose. For more information, see
+ * {@link ShortcutInfo.Builder#setShortLabel(CharSequence)}.</p>
+ * <p class="note"><b>Note: </b>This attribute's value must be a resource string, such as
+ * <code>@string/shortcut_short_label</code>.</p>
* </dd>
*
* <dt>{@code android:shortcutLongLabel}</dt>
- * <dd>Shortcut long label.
- * See {@link ShortcutInfo.Builder#setLongLabel(CharSequence)}.
- * <p>
- * This must be a resource string, such as <code>@string/shortcut_long_label</code>.
+ * <dd><p>An extended phrase that describes the shortcut's purpose. If there's enough space, the
+ * launcher displays this value instead of {@code android:shortcutShortLabel}. For more
+ * information, see {@link ShortcutInfo.Builder#setLongLabel(CharSequence)}.</p>
+ * <p class="note"><b>Note: </b>This attribute's value must be a resource string, such as
+ * <code>@string/shortcut_long_label</code>.</p>
* </dd>
*
* <dt>{@code android:shortcutDisabledMessage}</dt>
- * <dd>When {@code android:enabled} is set to
- * {@code false}, this attribute is used to display a custom disabled message.
- * <p>
- * This must be a resource string, such as <code>@string/shortcut_disabled_message</code>.
+ * <dd><p>The message that appears in a supported launcher when the user attempts to launch a
+ * disabled shortcut. The message should explain to the user why the shortcut is now disabled.
+ * This attribute's value has no effect if {@code android:enabled} is {@code true}.</p>
+ * <p class="note"><b>Note: </b>This attribute's value must be a resource string, such as
+ * <code>@string/shortcut_disabled_message</code>.</p>
+ * </dd>
+ * </dl>
+ *
+ * <h3>Inner elements that define static shortcuts</h3>
+ *
+ * <p>The XML file that lists an app's static shortcuts supports the following elements inside each
+ * {@code <shortcut>} element. You must include an {@code intent} inner element for each
+ * static shortcut that you define.</p>
+ *
+ * <dl>
+ * <dt>{@code intent}</dt>
+ * <dd><p>The action that the system launches when the user selects the shortcut. This intent must
+ * provide a value for the {@code android:action} attribute.</p>
+ * <p>You can provide multiple intents for a single shortcut. If you do so, the last defined
+ * activity is launched, and the other activities are placed in the
+ * <a href="/guide/components/tasks-and-back-stack.html">back stack</a>. See
+ * <a href="/guide/topics/ui/shortcuts.html#static">Using Static Shortcuts</a> and the
+ * {@link android.app.TaskStackBuilder} class reference for details.</p>
+ * <p class="note"><b>Note:</b> This {@code intent} element cannot include string resources.</p>
+ * <p>To learn more about how to configure intents, see
+ * <a href="{@docRoot}guide/topics/ui/settings.html#Intents">Using intents</a>.</p>
* </dd>
*
- * <dt>{@code intent}</dt>
- * <dd>Intent to launch when the user selects the shortcut.
- * {@code android:action} is mandatory.
- * See <a href="{@docRoot}guide/topics/ui/settings.html#Intents">Using intents</a> for the
- * other supported tags.
- * <p>You can provide multiple intents for a single shortcut so that the last defined activity is
- * launched with the other activities in the
- * <a href="/guide/components/tasks-and-back-stack.html">back stack</a>. See
- * {@link android.app.TaskStackBuilder} for details.
- * <p><b>Note:</b> String resources may not be used within an {@code <intent>} element.
- * </dd>
* <dt>{@code categories}</dt>
- * <dd>Specify shortcut categories. Currently only
- * {@link ShortcutInfo#SHORTCUT_CATEGORY_CONVERSATION} is defined in the framework.
+ * <dd><p>Provides a grouping for the types of actions that your app's shortcuts perform, such as
+ * creating new chat messages.</p>
+ * <p>For a list of supported shortcut categories, see the {@link ShortcutInfo} class reference
+ * for a list of supported shortcut categories.
* </dd>
* </dl>
*
* <h3>Updating shortcuts</h3>
*
+ * <p>Each app's launcher icon can contain at most {@link #getMaxShortcutCountPerActivity()} number
+ * of static and dynamic shortcuts combined. There is no limit to the number of pinned shortcuts
+ * that an app can create, though.
+ *
+ * <p>When a dynamic shortcut is pinned, even when the publisher removes it as a dynamic shortcut,
+ * the pinned shortcut is still visible and launchable. This allows an app to have more than
+ * {@link #getMaxShortcutCountPerActivity()} number of shortcuts.
+ *
* <p>As an example, suppose {@link #getMaxShortcutCountPerActivity()} is 5:
* <ol>
* <li>A chat app publishes 5 dynamic shortcuts for the 5 most recent
@@ -168,18 +219,13 @@
*
* <li>The user pins all 5 of the shortcuts.
*
- * <li>Later, the user has started 3 additional conversations (c6, c7, and c8),
- * so the publisher app
- * re-publishes its dynamic shortcuts. The new dynamic shortcut list is:
- * c4, c5, ..., c8.
- * The publisher app has to remove c1, c2, and c3 because it can't have more than
- * 5 dynamic shortcuts.
- *
- * <li>However, even though c1, c2, and c3 are no longer dynamic shortcuts, the pinned
- * shortcuts for these conversations are still available and launchable.
- *
- * <li>At this point, the user can access a total of 8 shortcuts that link to activities in
- * the publisher app, including the 3 pinned shortcuts, even though an app can have at most 5
+ * <li>Later, the user has started 3 additional conversations (c6, c7, and c8), so the publisher
+ * app re-publishes its dynamic shortcuts. The new dynamic shortcut list is: c4, c5, ..., c8.
+ * <p>The publisher app has to remove c1, c2, and c3 because it can't have more than 5 dynamic
+ * shortcuts. However, c1, c2, and c3 are still pinned shortcuts that the user can access and
+ * launch.
+ * <p>At this point, the user can access a total of 8 shortcuts that link to activities in the
+ * publisher app, including the 3 pinned shortcuts, even though an app can have at most 5
* dynamic shortcuts.
*
* <li>The app can use {@link #updateShortcuts(List)} to update <em>any</em> of the existing
@@ -196,44 +242,23 @@
* Dynamic shortcuts can be published with any set of {@link Intent#addFlags Intent} flags.
* Typically, {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} is specified, possibly along with other
* flags; otherwise, if the app is already running, the app is simply brought to
- * the foreground, and the target activity may not appear.
+ * the foreground, and the target activity might not appear.
*
* <p>Static shortcuts <b>cannot</b> have custom intent flags.
* The first intent of a static shortcut will always have {@link Intent#FLAG_ACTIVITY_NEW_TASK}
* and {@link Intent#FLAG_ACTIVITY_CLEAR_TASK} set. This means, when the app is already running, all
- * the existing activities in your app will be destroyed when a static shortcut is launched.
+ * the existing activities in your app are destroyed when a static shortcut is launched.
* If this behavior is not desirable, you can use a <em>trampoline activity</em>, or an invisible
* activity that starts another activity in {@link Activity#onCreate}, then calls
* {@link Activity#finish()}:
* <ol>
* <li>In the <code>AndroidManifest.xml</code> file, the trampoline activity should include the
* attribute assignment {@code android:taskAffinity=""}.
- * <li>In the shortcuts resource file, the intent within the static shortcut should point at
+ * <li>In the shortcuts resource file, the intent within the static shortcut should reference
* the trampoline activity.
* </ol>
*
- * <h3>Handling system locale changes</h3>
- *
- * <p>Apps should update dynamic and pinned shortcuts when the system locale changes using the
- * {@link Intent#ACTION_LOCALE_CHANGED} broadcast. When the system locale changes,
- * <a href="/guide/topics/ui/shortcuts.html#rate-limit">rate limiting</a> is reset, so even
- * background apps can add and update dynamic shortcuts until the rate limit is reached again.
- *
- * <h3>Shortcut limits</h3>
- *
- * <p>Only main activities—activities that handle the {@code MAIN} action and the
- * {@code LAUNCHER} category—can have shortcuts. If an app has multiple main activities, you
- * need to define the set of shortcuts for <em>each</em> activity.
- *
- * <p>Each launcher icon can have at most {@link #getMaxShortcutCountPerActivity()} number of
- * static and dynamic shortcuts combined. There is no limit to the number of pinned shortcuts that
- * an app can create.
- *
- * <p>When a dynamic shortcut is pinned, even when the publisher removes it as a dynamic shortcut,
- * the pinned shortcut is still visible and launchable. This allows an app to have more than
- * {@link #getMaxShortcutCountPerActivity()} number of shortcuts.
- *
- * <h4>Rate limiting</h4>
+ * <h3>Rate limiting</h3>
*
* <p>When <a href="/guide/topics/ui/shortcuts.html#rate-limit">rate limiting</a> is active,
* {@link #isRateLimitingActive()} returns {@code true}.
@@ -243,8 +268,20 @@
* <ul>
* <li>An app comes to the foreground.
* <li>The system locale changes.
- * <li>The user performs the <strong>inline reply</strong> action on a notification.
+ * <li>The user performs the <a href="/guide/topics/ui/notifiers/notifications.html#direct">inline
+ * reply</a> action on a notification.
* </ul>
+ *
+ * <h3>Handling system locale changes</h3>
+ *
+ * <p>Apps should update dynamic and pinned shortcuts when they receive the
+ * {@link Intent#ACTION_LOCALE_CHANGED} broadcast, indicating that the system locale has changed.
+ * <p>When the system locale changes, <a href="/guide/topics/ui/shortcuts.html#rate-limit">rate
+ * limiting</a> is reset, so even background apps can add and update dynamic shortcuts until the
+ * rate limit is reached again.
+ *
+ * <h3>Retrieving class instances</h3>
+ * <!-- Provides a heading for the content filled in by the @SystemService annotation below -->
*/
@SystemService(Context.SHORTCUT_SERVICE)
public class ShortcutManager {
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 26efda1..eb30979 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -781,25 +781,24 @@
public int seq;
/** @hide */
- @IntDef(flag = true,
- value = {
- NATIVE_CONFIG_MCC,
- NATIVE_CONFIG_MNC,
- NATIVE_CONFIG_LOCALE,
- NATIVE_CONFIG_TOUCHSCREEN,
- NATIVE_CONFIG_KEYBOARD,
- NATIVE_CONFIG_KEYBOARD_HIDDEN,
- NATIVE_CONFIG_NAVIGATION,
- NATIVE_CONFIG_ORIENTATION,
- NATIVE_CONFIG_DENSITY,
- NATIVE_CONFIG_SCREEN_SIZE,
- NATIVE_CONFIG_VERSION,
- NATIVE_CONFIG_SCREEN_LAYOUT,
- NATIVE_CONFIG_UI_MODE,
- NATIVE_CONFIG_SMALLEST_SCREEN_SIZE,
- NATIVE_CONFIG_LAYOUTDIR,
- NATIVE_CONFIG_COLOR_MODE,
- })
+ @IntDef(flag = true, prefix = { "NATIVE_CONFIG_" }, value = {
+ NATIVE_CONFIG_MCC,
+ NATIVE_CONFIG_MNC,
+ NATIVE_CONFIG_LOCALE,
+ NATIVE_CONFIG_TOUCHSCREEN,
+ NATIVE_CONFIG_KEYBOARD,
+ NATIVE_CONFIG_KEYBOARD_HIDDEN,
+ NATIVE_CONFIG_NAVIGATION,
+ NATIVE_CONFIG_ORIENTATION,
+ NATIVE_CONFIG_DENSITY,
+ NATIVE_CONFIG_SCREEN_SIZE,
+ NATIVE_CONFIG_VERSION,
+ NATIVE_CONFIG_SCREEN_LAYOUT,
+ NATIVE_CONFIG_UI_MODE,
+ NATIVE_CONFIG_SMALLEST_SCREEN_SIZE,
+ NATIVE_CONFIG_LAYOUTDIR,
+ NATIVE_CONFIG_COLOR_MODE,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface NativeConfig {}
diff --git a/core/java/android/content/res/GradientColor.java b/core/java/android/content/res/GradientColor.java
index e465961..35ad503 100644
--- a/core/java/android/content/res/GradientColor.java
+++ b/core/java/android/content/res/GradientColor.java
@@ -75,9 +75,14 @@
private static final boolean DBG_GRADIENT = false;
- @IntDef({TILE_MODE_CLAMP, TILE_MODE_REPEAT, TILE_MODE_MIRROR})
+ @IntDef(prefix = { "TILE_MODE_" }, value = {
+ TILE_MODE_CLAMP,
+ TILE_MODE_REPEAT,
+ TILE_MODE_MIRROR
+ })
@Retention(RetentionPolicy.SOURCE)
private @interface GradientTileMode {}
+
private static final int TILE_MODE_CLAMP = 0;
private static final int TILE_MODE_REPEAT = 1;
private static final int TILE_MODE_MIRROR = 2;
diff --git a/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java b/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java
new file mode 100644
index 0000000..5bf3a7c
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java
@@ -0,0 +1,134 @@
+/*
+ * 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.database.sqlite;
+
+import android.app.ActivityThread;
+import android.app.Application;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.KeyValueListParser;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Helper class for accessing
+ * {@link Settings.Global#SQLITE_COMPATIBILITY_WAL_FLAGS global compatibility WAL settings}.
+ *
+ * <p>The value of {@link Settings.Global#SQLITE_COMPATIBILITY_WAL_FLAGS} is cached on first access
+ * for consistent behavior across all connections opened in the process.
+ * @hide
+ */
+public class SQLiteCompatibilityWalFlags {
+
+ private static final String TAG = "SQLiteCompatibilityWalFlags";
+
+ private static volatile boolean sInitialized;
+ private static volatile boolean sFlagsSet;
+ private static volatile boolean sCompatibilityWalSupported;
+ private static volatile String sWALSyncMode;
+ // This flag is used to avoid recursive initialization due to circular dependency on Settings
+ private static volatile boolean sCallingGlobalSettings;
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public static boolean areFlagsSet() {
+ initIfNeeded();
+ return sFlagsSet;
+ }
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public static boolean isCompatibilityWalSupported() {
+ initIfNeeded();
+ return sCompatibilityWalSupported;
+ }
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public static String getWALSyncMode() {
+ initIfNeeded();
+ return sWALSyncMode;
+ }
+
+ private static void initIfNeeded() {
+ if (sInitialized || sCallingGlobalSettings) {
+ return;
+ }
+ ActivityThread activityThread = ActivityThread.currentActivityThread();
+ Application app = activityThread == null ? null : activityThread.getApplication();
+ String flags = null;
+ if (app == null) {
+ Log.w(TAG, "Cannot read global setting "
+ + Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS + " - "
+ + "Application state not available");
+ } else {
+ try {
+ sCallingGlobalSettings = true;
+ flags = Settings.Global.getString(app.getContentResolver(),
+ Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS);
+ } finally {
+ sCallingGlobalSettings = false;
+ }
+ }
+
+ init(flags);
+ }
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public static void init(String flags) {
+ if (TextUtils.isEmpty(flags)) {
+ sInitialized = true;
+ return;
+ }
+ KeyValueListParser parser = new KeyValueListParser(',');
+ try {
+ parser.setString(flags);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Setting has invalid format: " + flags, e);
+ sInitialized = true;
+ return;
+ }
+ sCompatibilityWalSupported = parser.getBoolean("compatibility_wal_supported",
+ SQLiteGlobal.isCompatibilityWalSupported());
+ sWALSyncMode = parser.getString("wal_syncmode", SQLiteGlobal.getWALSyncMode());
+ Log.i(TAG, "Read compatibility WAL flags: compatibility_wal_supported="
+ + sCompatibilityWalSupported + ", wal_syncmode=" + sWALSyncMode);
+ sFlagsSet = true;
+ sInitialized = true;
+ }
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public static void reset() {
+ sInitialized = false;
+ sFlagsSet = false;
+ sCompatibilityWalSupported = false;
+ sWALSyncMode = null;
+ }
+}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 2c93a7f..7717b8d 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -296,7 +296,11 @@
&& mConfiguration.syncMode == null && mConfiguration.useCompatibilityWal;
if (walEnabled || useCompatibilityWal) {
setJournalMode("WAL");
- setSyncMode(SQLiteGlobal.getWALSyncMode());
+ if (useCompatibilityWal && SQLiteCompatibilityWalFlags.areFlagsSet()) {
+ setSyncMode(SQLiteCompatibilityWalFlags.getWALSyncMode());
+ } else {
+ setSyncMode(SQLiteGlobal.getWALSyncMode());
+ }
} else {
setJournalMode(mConfiguration.journalMode == null
? SQLiteGlobal.getDefaultJournalMode() : mConfiguration.journalMode);
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index 5adb119..b211700 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -1094,6 +1094,12 @@
printer.println(" Open: " + mIsOpen);
printer.println(" Max connections: " + mMaxConnectionPoolSize);
printer.println(" Total execution time: " + mTotalExecutionTimeCounter);
+ if (SQLiteCompatibilityWalFlags.areFlagsSet()) {
+ printer.println(" Compatibility WAL settings: compatibility_wal_supported="
+ + SQLiteCompatibilityWalFlags
+ .isCompatibilityWalSupported() + ", wal_syncmode="
+ + SQLiteCompatibilityWalFlags.getWALSyncMode());
+ }
if (mConfiguration.isLookasideConfigSet()) {
printer.println(" Lookaside config: sz=" + mConfiguration.lookasideSlotSize
+ " cnt=" + mConfiguration.lookasideSlotCount);
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 09bb9c6..c1c0812 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -289,6 +289,10 @@
mConfigurationLocked.journalMode = journalMode;
mConfigurationLocked.syncMode = syncMode;
mConfigurationLocked.useCompatibilityWal = SQLiteGlobal.isCompatibilityWalSupported();
+ if (!mConfigurationLocked.isInMemoryDb() && SQLiteCompatibilityWalFlags.areFlagsSet()) {
+ mConfigurationLocked.useCompatibilityWal = SQLiteCompatibilityWalFlags
+ .isCompatibilityWalSupported();
+ }
}
@Override
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index b111ad3..7866b52 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -42,7 +42,15 @@
public final class HardwareBuffer implements Parcelable, AutoCloseable {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({RGBA_8888, RGBA_FP16, RGBA_1010102, RGBX_8888, RGB_888, RGB_565, BLOB})
+ @IntDef(prefix = { "RGB", "BLOB" }, value = {
+ RGBA_8888,
+ RGBA_FP16,
+ RGBA_1010102,
+ RGBX_8888,
+ RGB_888,
+ RGB_565,
+ BLOB
+ })
public @interface Format {
}
diff --git a/core/java/android/hardware/SensorAdditionalInfo.java b/core/java/android/hardware/SensorAdditionalInfo.java
index 7c876cf..5ff627f 100644
--- a/core/java/android/hardware/SensorAdditionalInfo.java
+++ b/core/java/android/hardware/SensorAdditionalInfo.java
@@ -68,8 +68,15 @@
*
* @hide
*/
- @IntDef({TYPE_FRAME_BEGIN, TYPE_FRAME_END, TYPE_UNTRACKED_DELAY, TYPE_INTERNAL_TEMPERATURE,
- TYPE_VEC3_CALIBRATION, TYPE_SENSOR_PLACEMENT, TYPE_SAMPLING})
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_FRAME_BEGIN,
+ TYPE_FRAME_END,
+ TYPE_UNTRACKED_DELAY,
+ TYPE_INTERNAL_TEMPERATURE,
+ TYPE_VEC3_CALIBRATION,
+ TYPE_SENSOR_PLACEMENT,
+ TYPE_SAMPLING
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface AdditionalInfoType {}
diff --git a/core/java/android/hardware/SensorDirectChannel.java b/core/java/android/hardware/SensorDirectChannel.java
index 36607c9..214d3ec 100644
--- a/core/java/android/hardware/SensorDirectChannel.java
+++ b/core/java/android/hardware/SensorDirectChannel.java
@@ -40,8 +40,12 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = {TYPE_MEMORY_FILE, TYPE_HARDWARE_BUFFER})
- public @interface MemoryType {};
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_MEMORY_FILE,
+ TYPE_HARDWARE_BUFFER
+ })
+ public @interface MemoryType {}
+
/**
* Shared memory type ashmem, wrapped in MemoryFile object.
*
@@ -60,8 +64,13 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = {RATE_STOP, RATE_NORMAL, RATE_FAST, RATE_VERY_FAST})
- public @interface RateLevel {};
+ @IntDef(flag = true, prefix = { "RATE_" }, value = {
+ RATE_STOP,
+ RATE_NORMAL,
+ RATE_FAST,
+ RATE_VERY_FAST
+ })
+ public @interface RateLevel {}
/**
* Sensor stopped (no event output).
diff --git a/core/java/android/hardware/camera2/CameraAccessException.java b/core/java/android/hardware/camera2/CameraAccessException.java
index f9b659c..d238797 100644
--- a/core/java/android/hardware/camera2/CameraAccessException.java
+++ b/core/java/android/hardware/camera2/CameraAccessException.java
@@ -16,7 +16,6 @@
package android.hardware.camera2;
-import android.annotation.NonNull;
import android.annotation.IntDef;
import android.util.AndroidException;
@@ -81,15 +80,16 @@
*/
public static final int CAMERA_DEPRECATED_HAL = 1000;
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(
- {CAMERA_IN_USE,
- MAX_CAMERAS_IN_USE,
- CAMERA_DISABLED,
- CAMERA_DISCONNECTED,
- CAMERA_ERROR})
- public @interface AccessError {};
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "CAMERA_", "MAX_CAMERAS_IN_USE" }, value = {
+ CAMERA_IN_USE,
+ MAX_CAMERAS_IN_USE,
+ CAMERA_DISABLED,
+ CAMERA_DISCONNECTED,
+ CAMERA_ERROR
+ })
+ public @interface AccessError {}
// Make the eclipse warning about serializable exceptions go away
private static final long serialVersionUID = 5630338637471475675L; // randomly generated
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 7409671..a85b5f7 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -486,6 +486,7 @@
this.mConfiguredSize = other.mConfiguredSize;
this.mConfiguredGenerationId = other.mConfiguredGenerationId;
this.mIsDeferredConfig = other.mIsDeferredConfig;
+ this.mIsShared = other.mIsShared;
}
/**
@@ -498,6 +499,7 @@
int width = source.readInt();
int height = source.readInt();
boolean isDeferred = source.readInt() == 1;
+ boolean isShared = source.readInt() == 1;
ArrayList<Surface> surfaces = new ArrayList<Surface>();
source.readTypedList(surfaces, Surface.CREATOR);
@@ -508,6 +510,7 @@
mSurfaces = surfaces;
mConfiguredSize = new Size(width, height);
mIsDeferredConfig = isDeferred;
+ mIsShared = isShared;
mSurfaces = surfaces;
if (mSurfaces.size() > 0) {
mSurfaceType = SURFACE_TYPE_UNKNOWN;
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.aidl b/core/java/android/hardware/display/BrightnessConfiguration.aidl
new file mode 100644
index 0000000..5b6b464
--- /dev/null
+++ b/core/java/android/hardware/display/BrightnessConfiguration.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.hardware.display;
+
+parcelable BrightnessConfiguration;
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
new file mode 100644
index 0000000..6c3be81
--- /dev/null
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -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.
+ */
+
+package android.hardware.display;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pair;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+
+/** @hide */
+public final class BrightnessConfiguration implements Parcelable {
+ private final float[] mLux;
+ private final float[] mNits;
+
+ private BrightnessConfiguration(float[] lux, float[] nits) {
+ mLux = lux;
+ mNits = nits;
+ }
+
+ /**
+ * Gets the base brightness as curve.
+ *
+ * The curve is returned as a pair of float arrays, the first representing all of the lux
+ * points of the brightness curve and the second representing all of the nits values of the
+ * brightness curve.
+ *
+ * @return the control points for the brightness curve.
+ */
+ public Pair<float[], float[]> getCurve() {
+ return Pair.create(Arrays.copyOf(mLux, mLux.length), Arrays.copyOf(mNits, mNits.length));
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeFloatArray(mLux);
+ dest.writeFloatArray(mNits);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("BrightnessConfiguration{[");
+ final int size = mLux.length;
+ for (int i = 0; i < size; i++) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append("(").append(mLux[i]).append(", ").append(mNits[i]).append(")");
+ }
+ sb.append("]}");
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ result = result * 31 + Arrays.hashCode(mLux);
+ result = result * 31 + Arrays.hashCode(mNits);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof BrightnessConfiguration)) {
+ return false;
+ }
+ final BrightnessConfiguration other = (BrightnessConfiguration) o;
+ return Arrays.equals(mLux, other.mLux) && Arrays.equals(mNits, other.mNits);
+ }
+
+ public static final Creator<BrightnessConfiguration> CREATOR =
+ new Creator<BrightnessConfiguration>() {
+ public BrightnessConfiguration createFromParcel(Parcel in) {
+ Builder builder = new Builder();
+ float[] lux = in.createFloatArray();
+ float[] nits = in.createFloatArray();
+ builder.setCurve(lux, nits);
+ return builder.build();
+ }
+
+ public BrightnessConfiguration[] newArray(int size) {
+ return new BrightnessConfiguration[size];
+ }
+ };
+
+ /**
+ * A builder class for {@link BrightnessConfiguration}s.
+ */
+ public static class Builder {
+ private float[] mCurveLux;
+ private float[] mCurveNits;
+
+ /**
+ * Sets the control points for the brightness curve.
+ *
+ * Brightness curves must have strictly increasing ambient brightness values in lux and
+ * monotonically increasing display brightness values in nits. In addition, the initial
+ * control point must be 0 lux.
+ *
+ * @throws IllegalArgumentException if the initial control point is not at 0 lux.
+ * @throws IllegalArgumentException if the lux levels are not strictly increasing.
+ * @throws IllegalArgumentException if the nit levels are not monotonically increasing.
+ */
+ public Builder setCurve(float[] lux, float[] nits) {
+ Preconditions.checkNotNull(lux);
+ Preconditions.checkNotNull(nits);
+ if (lux.length == 0 || nits.length == 0) {
+ throw new IllegalArgumentException("Lux and nits arrays must not be empty");
+ }
+ if (lux.length != nits.length) {
+ throw new IllegalArgumentException("Lux and nits arrays must be the same length");
+ }
+ if (lux[0] != 0) {
+ throw new IllegalArgumentException("Initial control point must be for 0 lux");
+ }
+ Preconditions.checkArrayElementsInRange(lux, 0, Float.MAX_VALUE, "lux");
+ Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits");
+ checkMonotonic(lux, true/*strictly increasing*/, "lux");
+ checkMonotonic(nits, false /*strictly increasing*/, "nits");
+ mCurveLux = lux;
+ mCurveNits = nits;
+ return this;
+ }
+
+ /**
+ * Builds the {@link BrightnessConfiguration}.
+ *
+ * A brightness curve <b>must</b> be set before calling this.
+ */
+ public BrightnessConfiguration build() {
+ if (mCurveLux == null || mCurveNits == null) {
+ throw new IllegalStateException("A curve must be set!");
+ }
+ return new BrightnessConfiguration(mCurveLux, mCurveNits);
+ }
+
+ private static void checkMonotonic(float[] vals, boolean strictlyIncreasing, String name) {
+ if (vals.length <= 1) {
+ return;
+ }
+ float prev = vals[0];
+ for (int i = 1; i < vals.length; i++) {
+ if (prev > vals[i] || prev == vals[i] && strictlyIncreasing) {
+ String condition = strictlyIncreasing ? "strictly increasing" : "monotonic";
+ throw new IllegalArgumentException(name + " values must be " + condition);
+ }
+ prev = vals[i];
+ }
+ }
+ }
+}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 97ca231..7de667d 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -27,6 +27,7 @@
import android.graphics.Point;
import android.media.projection.MediaProjection;
import android.os.Handler;
+import android.os.UserHandle;
import android.util.SparseArray;
import android.view.Display;
import android.view.Surface;
@@ -634,6 +635,27 @@
}
/**
+ * Sets the global display brightness configuration.
+ *
+ * @hide
+ */
+ public void setBrightnessConfiguration(BrightnessConfiguration c) {
+ setBrightnessConfigurationForUser(c, UserHandle.myUserId());
+ }
+
+ /**
+ * Sets the global display brightness configuration for a specific user.
+ *
+ * Note this requires the INTERACT_ACROSS_USERS permission if setting the configuration for a
+ * user other than the one you're currently running as.
+ *
+ * @hide
+ */
+ public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId) {
+ mGlobal.setBrightnessConfigurationForUser(c, userId);
+ }
+
+ /**
* Listens for changes in available display devices.
*/
public interface DisplayListener {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index c3f82f5..bf4cc1d 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -487,6 +487,19 @@
}
}
+ /**
+ * Sets the global brightness configuration for a given user.
+ *
+ * @hide
+ */
+ public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId) {
+ try {
+ mDm.setBrightnessConfigurationForUser(c, userId);
+ } 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/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index f2ed9e7..8afae6e 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -18,6 +18,7 @@
import android.content.pm.ParceledListSlice;
import android.graphics.Point;
+import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.WifiDisplay;
@@ -89,4 +90,9 @@
// STOPSHIP remove when adaptive brightness code is updated to accept curves.
// Requires BRIGHTNESS_SLIDER_USAGE permission.
void setBrightness(int brightness);
+
+ // 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.
+ void setBrightnessConfigurationForUser(in BrightnessConfiguration c, int userId);
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index c531a89..1de8882 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -188,7 +188,11 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({SWITCH_STATE_UNKNOWN, SWITCH_STATE_OFF, SWITCH_STATE_ON})
+ @IntDef(prefix = { "SWITCH_STATE_" }, value = {
+ SWITCH_STATE_UNKNOWN,
+ SWITCH_STATE_OFF,
+ SWITCH_STATE_ON
+ })
public @interface SwitchState {}
/**
diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java
index e1137aa..c2b2800 100644
--- a/core/java/android/hardware/location/ContextHubInfo.java
+++ b/core/java/android/hardware/location/ContextHubInfo.java
@@ -26,7 +26,7 @@
* @hide
*/
@SystemApi
-public class ContextHubInfo {
+public class ContextHubInfo implements Parcelable {
private int mId;
private String mName;
private String mVendor;
@@ -262,7 +262,7 @@
@Override
public String toString() {
String retVal = "";
- retVal += "Id : " + mId;
+ retVal += "ID/handle : " + mId;
retVal += ", Name : " + mName;
retVal += "\n\tVendor : " + mVendor;
retVal += ", Toolchain : " + mToolchain;
@@ -275,8 +275,6 @@
retVal += ", StoppedPowerDraw : " + mStoppedPowerDrawMw + " mW";
retVal += ", PeakPowerDraw : " + mPeakPowerDrawMw + " mW";
retVal += ", MaxPacketLength : " + mMaxPacketLengthBytes + " Bytes";
- retVal += "\n\tSupported sensors : " + Arrays.toString(mSupportedSensors);
- retVal += "\n\tMemory Regions : " + Arrays.toString(mMemoryRegions);
return retVal;
}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 1d66dc6d..6da6fb7 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -258,9 +258,9 @@
}
/**
- * Returns the list of context hubs in the system.
+ * Returns the list of ContextHubInfo objects describing the available Context Hubs.
*
- * @return the list of context hub informations
+ * @return the list of ContextHubInfo objects
*
* @see ContextHubInfo
*
@@ -268,10 +268,14 @@
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public List<ContextHubInfo> getContextHubs() {
- throw new UnsupportedOperationException("TODO: Implement this");
+ try {
+ return mService.getContextHubs();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
- /*
+ /**
* Helper function to generate a stub for a non-query transaction callback.
*
* @param transaction the transaction to unblock when complete
@@ -297,7 +301,7 @@
};
}
- /*
+ /**
* Helper function to generate a stub for a query transaction callback.
*
* @param transaction the transaction to unblock when complete
@@ -392,7 +396,17 @@
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public ContextHubTransaction<Void> enableNanoApp(ContextHubInfo hubInfo, long nanoAppId) {
- throw new UnsupportedOperationException("TODO: Implement this");
+ ContextHubTransaction<Void> transaction =
+ new ContextHubTransaction<>(ContextHubTransaction.TYPE_ENABLE_NANOAPP);
+ IContextHubTransactionCallback callback = createTransactionCallback(transaction);
+
+ try {
+ mService.enableNanoApp(hubInfo.getId(), callback, nanoAppId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ return transaction;
}
/**
@@ -407,7 +421,17 @@
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public ContextHubTransaction<Void> disableNanoApp(ContextHubInfo hubInfo, long nanoAppId) {
- throw new UnsupportedOperationException("TODO: Implement this");
+ ContextHubTransaction<Void> transaction =
+ new ContextHubTransaction<>(ContextHubTransaction.TYPE_DISABLE_NANOAPP);
+ IContextHubTransactionCallback callback = createTransactionCallback(transaction);
+
+ try {
+ mService.disableNanoApp(hubInfo.getId(), callback, nanoAppId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ return transaction;
}
/**
diff --git a/core/java/android/hardware/location/ContextHubTransaction.java b/core/java/android/hardware/location/ContextHubTransaction.java
index b808de3..2a66cbc 100644
--- a/core/java/android/hardware/location/ContextHubTransaction.java
+++ b/core/java/android/hardware/location/ContextHubTransaction.java
@@ -47,13 +47,15 @@
* Constants describing the type of a transaction through the Context Hub Service.
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({
+ @IntDef(prefix = { "TYPE_" }, value = {
TYPE_LOAD_NANOAPP,
TYPE_UNLOAD_NANOAPP,
TYPE_ENABLE_NANOAPP,
TYPE_DISABLE_NANOAPP,
- TYPE_QUERY_NANOAPPS})
- public @interface Type {}
+ TYPE_QUERY_NANOAPPS
+ })
+ public @interface Type { }
+
public static final int TYPE_LOAD_NANOAPP = 0;
public static final int TYPE_UNLOAD_NANOAPP = 1;
public static final int TYPE_ENABLE_NANOAPP = 2;
@@ -64,7 +66,7 @@
* Constants describing the result of a transaction or request through the Context Hub Service.
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({
+ @IntDef(prefix = { "TRANSACTION_" }, value = {
TRANSACTION_SUCCESS,
TRANSACTION_FAILED_UNKNOWN,
TRANSACTION_FAILED_BAD_PARAMS,
@@ -73,7 +75,8 @@
TRANSACTION_FAILED_AT_HUB,
TRANSACTION_FAILED_TIMEOUT,
TRANSACTION_FAILED_SERVICE_INTERNAL_FAILURE,
- TRANSACTION_FAILED_HAL_UNAVAILABLE})
+ TRANSACTION_FAILED_HAL_UNAVAILABLE
+ })
public @interface Result {}
public static final int TRANSACTION_SUCCESS = 0;
/**
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index 628ebc7..233e857 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -43,23 +43,26 @@
ContextHubInfo getContextHubInfo(int contextHubHandle);
// Loads a nanoapp at the specified hub (old API)
- int loadNanoApp(int hubHandle, in NanoApp app);
+ int loadNanoApp(int contextHubHandle, in NanoApp nanoApp);
// Unloads a nanoapp given its instance ID (old API)
- int unloadNanoApp(int nanoAppInstanceHandle);
+ int unloadNanoApp(int nanoAppHandle);
// Gets the NanoAppInstanceInfo of a nanoapp give its instance ID
- NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppInstanceHandle);
+ NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle);
// Finds all nanoApp instances matching some filter
- int[] findNanoAppOnHub(int hubHandle, in NanoAppFilter filter);
+ int[] findNanoAppOnHub(int contextHubHandle, in NanoAppFilter filter);
// Sends a message to a nanoApp
- int sendMessage(int hubHandle, int nanoAppHandle, in ContextHubMessage msg);
+ int sendMessage(int contextHubHandle, int nanoAppHandle, in ContextHubMessage msg);
// Creates a client to send and receive messages
IContextHubClient createClient(in IContextHubClientCallback client, int contextHubId);
+ // Returns a list of ContextHub objects of available hubs
+ List<ContextHubInfo> getContextHubs();
+
// Loads a nanoapp at the specified hub (new API)
void loadNanoAppOnHub(
int contextHubId, in IContextHubTransactionCallback transactionCallback,
@@ -70,6 +73,16 @@
int contextHubId, in IContextHubTransactionCallback transactionCallback,
long nanoAppId);
+ // Enables a nanoapp at the specified hub
+ void enableNanoApp(
+ int contextHubId, in IContextHubTransactionCallback transactionCallback,
+ long nanoAppId);
+
+ // Disables a nanoapp at the specified hub
+ void disableNanoApp(
+ int contextHubId, in IContextHubTransactionCallback transactionCallback,
+ long nanoAppId);
+
// Queries for a list of nanoapps
void queryNanoApps(int contextHubId, in IContextHubTransactionCallback transactionCallback);
}
diff --git a/core/java/android/hardware/location/NanoAppInstanceInfo.java b/core/java/android/hardware/location/NanoAppInstanceInfo.java
index 2623830..f73fd87 100644
--- a/core/java/android/hardware/location/NanoAppInstanceInfo.java
+++ b/core/java/android/hardware/location/NanoAppInstanceInfo.java
@@ -16,9 +16,7 @@
package android.hardware.location;
-
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -26,29 +24,40 @@
import libcore.util.EmptyArray;
/**
+ * Describes an instance of a nanoapp, used by the internal state manged by ContextHubService.
+ *
+ * TODO(b/69270990) Remove this class once the old API is deprecated.
+ *
* @hide
*/
@SystemApi
public class NanoAppInstanceInfo {
- private String mPublisher;
- private String mName;
+ private String mPublisher = "Unknown";
+ private String mName = "Unknown";
+ private int mHandle;
private long mAppId;
private int mAppVersion;
-
- private int mNeededReadMemBytes;
- private int mNeededWriteMemBytes;
- private int mNeededExecMemBytes;
-
- private int[] mNeededSensors;
- private int[] mOutputEvents;
-
private int mContexthubId;
- private int mHandle;
+
+ private int mNeededReadMemBytes = 0;
+ private int mNeededWriteMemBytes = 0;
+ private int mNeededExecMemBytes = 0;
+
+ private int[] mNeededSensors = EmptyArray.INT;
+ private int[] mOutputEvents = EmptyArray.INT;
public NanoAppInstanceInfo() {
- mNeededSensors = EmptyArray.INT;
- mOutputEvents = EmptyArray.INT;
+ }
+
+ /**
+ * @hide
+ */
+ public NanoAppInstanceInfo(int handle, long appId, int appVersion, int contextHubId) {
+ mHandle = handle;
+ mAppId = appId;
+ mAppVersion = appVersion;
+ mContexthubId = contextHubId;
}
/**
@@ -60,18 +69,6 @@
return mPublisher;
}
-
- /**
- * set the publisher name for the app
- *
- * @param publisher - name of the publisher
- *
- * @hide
- */
- public void setPublisher(String publisher) {
- mPublisher = publisher;
- }
-
/**
* get the name of the app
*
@@ -82,17 +79,6 @@
}
/**
- * set the name of the app
- *
- * @param name - name of the app
- *
- * @hide
- */
- public void setName(String name) {
- mName = name;
- }
-
- /**
* Get the application identifier
*
* @return int - application identifier
@@ -102,17 +88,6 @@
}
/**
- * Set the application identifier
- *
- * @param appId - application identifier
- *
- * @hide
- */
- public void setAppId(long appId) {
- mAppId = appId;
- }
-
- /**
* Get the application version
*
* NOTE: There is a race condition where shortly after loading, this
@@ -127,17 +102,6 @@
}
/**
- * Set the application version
- *
- * @param appVersion - version of the app
- *
- * @hide
- */
- public void setAppVersion(int appVersion) {
- mAppVersion = appVersion;
- }
-
- /**
* Get the read memory needed by the app
*
* @return int - readable memory needed in bytes
@@ -147,17 +111,6 @@
}
/**
- * Set the read memory needed by the app
- *
- * @param neededReadMemBytes - readable Memory needed in bytes
- *
- * @hide
- */
- public void setNeededReadMemBytes(int neededReadMemBytes) {
- mNeededReadMemBytes = neededReadMemBytes;
- }
-
- /**
* get writable memory needed by the app
*
* @return int - writable memory needed by the app
@@ -167,18 +120,6 @@
}
/**
- * set writable memory needed by the app
- *
- * @param neededWriteMemBytes - writable memory needed by the
- * app
- *
- * @hide
- */
- public void setNeededWriteMemBytes(int neededWriteMemBytes) {
- mNeededWriteMemBytes = neededWriteMemBytes;
- }
-
- /**
* get executable memory needed by the app
*
* @return int - executable memory needed by the app
@@ -188,18 +129,6 @@
}
/**
- * set executable memory needed by the app
- *
- * @param neededExecMemBytes - executable memory needed by the
- * app
- *
- * @hide
- */
- public void setNeededExecMemBytes(int neededExecMemBytes) {
- mNeededExecMemBytes = neededExecMemBytes;
- }
-
- /**
* Get the sensors needed by this app
*
* @return int[] all the required sensors needed by this app
@@ -210,17 +139,6 @@
}
/**
- * set the sensors needed by this app
- *
- * @param neededSensors - all the sensors needed by this app
- *
- * @hide
- */
- public void setNeededSensors(@Nullable int[] neededSensors) {
- mNeededSensors = neededSensors != null ? neededSensors : EmptyArray.INT;
- }
-
- /**
* get the events generated by this app
*
* @return all the events that can be generated by this app
@@ -231,18 +149,6 @@
}
/**
- * set the output events that can be generated by this app
- *
- * @param outputEvents - the events that may be generated by
- * this app
- *
- * @hide
- */
- public void setOutputEvents(@Nullable int[] outputEvents) {
- mOutputEvents = outputEvents != null ? outputEvents : EmptyArray.INT;
- }
-
- /**
* get the context hub identifier
*
* @return int - system unique hub identifier
@@ -252,17 +158,6 @@
}
/**
- * set the context hub identifier
- *
- * @param contexthubId - system wide unique identifier
- *
- * @hide
- */
- public void setContexthubId(int contexthubId) {
- mContexthubId = contexthubId;
- }
-
- /**
* get a handle to the nano app instance
*
* @return int - handle to this instance
@@ -271,18 +166,6 @@
return mHandle;
}
- /**
- * set the handle for an app instance
- *
- * @param handle - handle to this instance
- *
- * @hide
- */
- public void setHandle(int handle) {
- mHandle = handle;
- }
-
-
private NanoAppInstanceInfo(Parcel in) {
mPublisher = in.readString();
mName = in.readString();
@@ -342,9 +225,7 @@
public String toString() {
String retVal = "handle : " + mHandle;
retVal += ", Id : 0x" + Long.toHexString(mAppId);
- retVal += ", Version : " + mAppVersion;
- retVal += ", Name : " + mName;
- retVal += ", Publisher : " + mPublisher;
+ retVal += ", Version : 0x" + Integer.toHexString(mAppVersion);
return retVal;
}
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index 4f4361f..4d54e31 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -161,7 +161,8 @@
private final Set<Integer> mSupportedIdentifierTypes;
@NonNull private final Map<String, String> mVendorInfo;
- ModuleProperties(int id, String serviceName, int classId, String implementor,
+ /** @hide */
+ public ModuleProperties(int id, String serviceName, int classId, String implementor,
String product, String version, String serial, int numTuners, int numAudioSources,
boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported,
@ProgramSelector.ProgramType int[] supportedProgramTypes,
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 8071e8b..11d338d 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1794,7 +1794,7 @@
ITelephony it = ITelephony.Stub.asInterface(b);
int subId = SubscriptionManager.getDefaultDataSubscriptionId();
Log.d("ConnectivityManager", "getMobileDataEnabled()+ subId=" + subId);
- boolean retVal = it.getDataEnabled(subId);
+ boolean retVal = it.isUserDataEnabled(subId);
Log.d("ConnectivityManager", "getMobileDataEnabled()- subId=" + subId
+ " retVal=" + retVal);
return retVal;
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index 0b1ea98..d9b57db 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -30,7 +30,7 @@
*/
interface IIpSecService
{
- IpSecSpiResponse reserveSecurityParameterIndex(
+ IpSecSpiResponse allocateSecurityParameterIndex(
int direction, in String remoteAddress, int requestedSpi, in IBinder binder);
void releaseSecurityParameterIndex(int resourceId);
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index a9e60ec..6a4b891 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -46,7 +46,7 @@
* to create a VPN should use {@link VpnService}.
*
* @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
- * Internet Protocol</a>
+ * Internet Protocol</a>
*/
@SystemService(Context.IPSEC_SERVICE)
public final class IpSecManager {
@@ -59,8 +59,7 @@
*
* @hide
*/
- @TestApi
- public static final int INVALID_SECURITY_PARAMETER_INDEX = 0;
+ @TestApi public static final int INVALID_SECURITY_PARAMETER_INDEX = 0;
/** @hide */
public interface Status {
@@ -78,7 +77,7 @@
* <p>The combination of remote {@code InetAddress} and SPI must be unique across all apps on
* one device. If this error is encountered, a new SPI is required before a transform may be
* created. This error can be avoided by calling {@link
- * IpSecManager#reserveSecurityParameterIndex}.
+ * IpSecManager#allocateSecurityParameterIndex}.
*/
public static final class SpiUnavailableException extends AndroidException {
private final int mSpi;
@@ -121,7 +120,7 @@
* This class represents a reserved SPI.
*
* <p>Objects of this type are used to track reserved security parameter indices. They can be
- * obtained by calling {@link IpSecManager#reserveSecurityParameterIndex} and must be released
+ * obtained by calling {@link IpSecManager#allocateSecurityParameterIndex} and must be released
* by calling {@link #close()} when they are no longer needed.
*/
public static final class SecurityParameterIndex implements AutoCloseable {
@@ -170,7 +169,7 @@
mRemoteAddress = remoteAddress;
try {
IpSecSpiResponse result =
- mService.reserveSecurityParameterIndex(
+ mService.allocateSecurityParameterIndex(
direction, remoteAddress.getHostAddress(), spi, new Binder());
if (result == null) {
@@ -228,7 +227,7 @@
* for this user
* @throws SpiUnavailableException indicating that a particular SPI cannot be reserved
*/
- public SecurityParameterIndex reserveSecurityParameterIndex(
+ public SecurityParameterIndex allocateSecurityParameterIndex(
int direction, InetAddress remoteAddress) throws ResourceUnavailableException {
try {
return new SecurityParameterIndex(
@@ -255,7 +254,7 @@
* for this user
* @throws SpiUnavailableException indicating that the requested SPI could not be reserved
*/
- public SecurityParameterIndex reserveSecurityParameterIndex(
+ public SecurityParameterIndex allocateSecurityParameterIndex(
int direction, InetAddress remoteAddress, int requestedSpi)
throws SpiUnavailableException, ResourceUnavailableException {
if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) {
@@ -273,16 +272,18 @@
* unprotected traffic can resume on that socket.
*
* <p>For security reasons, the destination address of any traffic on the socket must match the
- * remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
+ * remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
* other IP address will result in an IOException. In addition, reads and writes on the socket
* will throw IOException if the user deactivates the transform (by calling {@link
* IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
*
- * <h4>Rekey Procedure</h4> <p>When applying a new tranform to a socket, the previous transform
- * will be removed. However, inbound traffic on the old transform will continue to be decrypted
- * until that transform is deallocated by calling {@link IpSecTransform#close()}. This overlap
- * allows rekey procedures where both transforms are valid until both endpoints are using the
- * new transform and all in-flight packets have been received.
+ * <h4>Rekey Procedure</h4>
+ *
+ * <p>When applying a new tranform to a socket, the previous transform will be removed. However,
+ * inbound traffic on the old transform will continue to be decrypted until that transform is
+ * deallocated by calling {@link IpSecTransform#close()}. This overlap allows rekey procedures
+ * where both transforms are valid until both endpoints are using the new transform and all
+ * in-flight packets have been received.
*
* @param socket a stream socket
* @param transform a transport mode {@code IpSecTransform}
@@ -310,11 +311,13 @@
* will throw IOException if the user deactivates the transform (by calling {@link
* IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
*
- * <h4>Rekey Procedure</h4> <p>When applying a new tranform to a socket, the previous transform
- * will be removed. However, inbound traffic on the old transform will continue to be decrypted
- * until that transform is deallocated by calling {@link IpSecTransform#close()}. This overlap
- * allows rekey procedures where both transforms are valid until both endpoints are using the
- * new transform and all in-flight packets have been received.
+ * <h4>Rekey Procedure</h4>
+ *
+ * <p>When applying a new tranform to a socket, the previous transform will be removed. However,
+ * inbound traffic on the old transform will continue to be decrypted until that transform is
+ * deallocated by calling {@link IpSecTransform#close()}. This overlap allows rekey procedures
+ * where both transforms are valid until both endpoints are using the new transform and all
+ * in-flight packets have been received.
*
* @param socket a datagram socket
* @param transform a transport mode {@code IpSecTransform}
@@ -342,11 +345,13 @@
* will throw IOException if the user deactivates the transform (by calling {@link
* IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
*
- * <h4>Rekey Procedure</h4> <p>When applying a new tranform to a socket, the previous transform
- * will be removed. However, inbound traffic on the old transform will continue to be decrypted
- * until that transform is deallocated by calling {@link IpSecTransform#close()}. This overlap
- * allows rekey procedures where both transforms are valid until both endpoints are using the
- * new transform and all in-flight packets have been received.
+ * <h4>Rekey Procedure</h4>
+ *
+ * <p>When applying a new tranform to a socket, the previous transform will be removed. However,
+ * inbound traffic on the old transform will continue to be decrypted until that transform is
+ * deallocated by calling {@link IpSecTransform#close()}. This overlap allows rekey procedures
+ * where both transforms are valid until both endpoints are using the new transform and all
+ * in-flight packets have been received.
*
* @param socket a socket file descriptor
* @param transform a transport mode {@code IpSecTransform}
@@ -379,7 +384,8 @@
* Applications should probably not use this API directly. Instead, they should use {@link
* VpnService} to provide VPN capability in a more generic fashion.
*
- * TODO: Update javadoc for tunnel mode APIs at the same time the APIs are re-worked.
+ * <p>TODO: Update javadoc for tunnel mode APIs at the same time the APIs are re-worked.
+ *
* @param net a {@link Network} that will be tunneled via IP Sec.
* @param transform an {@link IpSecTransform}, which must be an active Tunnel Mode transform.
* @hide
@@ -469,7 +475,8 @@
* all traffic that cannot be routed to the Tunnel's outbound interface. If that interface is
* lost, all traffic will drop.
*
- * TODO: Update javadoc for tunnel mode APIs at the same time the APIs are re-worked.
+ * <p>TODO: Update javadoc for tunnel mode APIs at the same time the APIs are re-worked.
+ *
* @param net a network that currently has transform applied to it.
* @param transform a Tunnel Mode IPsec Transform that has been previously applied to the given
* network
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index cda4ec7..7cd742b 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -47,7 +47,7 @@
* system resources.
*
* @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
- * Internet Protocol</a>
+ * Internet Protocol</a>
*/
public final class IpSecTransform implements AutoCloseable {
private static final String TAG = "IpSecTransform";
@@ -116,8 +116,7 @@
}
/**
- * Checks the result status and throws an appropriate exception if
- * the status is not Status.OK.
+ * Checks the result status and throws an appropriate exception if the status is not Status.OK.
*/
private void checkResultStatus(int status)
throws IOException, IpSecManager.ResourceUnavailableException,
@@ -267,9 +266,7 @@
return;
}
- /**
- * This class is used to build {@link IpSecTransform} objects.
- */
+ /** This class is used to build {@link IpSecTransform} objects. */
public static class Builder {
private Context mContext;
private IpSecConfig mConfig;
@@ -339,7 +336,7 @@
*
* <p>Because IPsec operates at the IP layer, this 32-bit identifier uniquely identifies
* packets to a given destination address. To prevent SPI collisions, values should be
- * reserved by calling {@link IpSecManager#reserveSecurityParameterIndex}.
+ * reserved by calling {@link IpSecManager#allocateSecurityParameterIndex}.
*
* <p>If the SPI and algorithms are omitted for one direction, traffic in that direction
* will not be encrypted or authenticated.
@@ -374,10 +371,9 @@
* <p>This allows IPsec traffic to pass through a NAT.
*
* @see <a href="https://tools.ietf.org/html/rfc3948">RFC 3948, UDP Encapsulation of IPsec
- * ESP Packets</a>
+ * ESP Packets</a>
* @see <a href="https://tools.ietf.org/html/rfc7296#section-2.23">RFC 7296 section 2.23,
- * NAT Traversal of IKEv2</a>
- *
+ * NAT Traversal of IKEv2</a>
* @param localSocket a socket for sending and receiving encapsulated traffic
* @param remotePort the UDP port number of the remote host that will send and receive
* encapsulated traffic. In the case of IKEv2, this should be port 4500.
@@ -402,7 +398,6 @@
*
* @param intervalSeconds the maximum number of seconds between keepalive packets. Must be
* between 20s and 3600s.
- *
* @hide
*/
@SystemApi
@@ -418,7 +413,6 @@
* will not affect any network traffic until it has been applied to one or more sockets.
*
* @see IpSecManager#applyTransportModeTransform
- *
* @param remoteAddress the remote {@code InetAddress} of traffic on sockets that will use
* this transform
* @throws IllegalArgumentException indicating that a particular combination of transform
@@ -453,8 +447,8 @@
*/
public IpSecTransform buildTunnelModeTransform(
InetAddress localAddress, InetAddress remoteAddress) {
- //FIXME: argument validation here
- //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
+ // FIXME: argument validation here
+ // throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
mConfig.setLocalAddress(localAddress.getHostAddress());
mConfig.setRemoteAddress(remoteAddress.getHostAddress());
mConfig.setMode(MODE_TUNNEL);
diff --git a/core/java/android/net/OWNERS b/core/java/android/net/OWNERS
index d1ce60e..6758d95 100644
--- a/core/java/android/net/OWNERS
+++ b/core/java/android/net/OWNERS
@@ -1,6 +1,7 @@
ek@google.com
hugobenichi@google.com
jsharkey@android.com
+jchalard@google.com
lorenzo@google.com
satk@google.com
silberst@google.com
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 954e59c..d701550 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -19,6 +19,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.DownloadManager;
import android.app.backup.BackupManager;
import android.app.usage.NetworkStatsManager;
@@ -154,6 +155,8 @@
private static Object sProfilingLock = new Object();
+ private static final String LOOPBACK_IFACE = "lo";
+
/**
* Set active tag to use when accounting {@link Socket} traffic originating
* from the current thread. Only one active tag per thread is supported.
@@ -542,6 +545,30 @@
return nativeGetIfaceStat(iface, TYPE_RX_BYTES);
}
+ /** {@hide} */
+ @TestApi
+ public static long getLoopbackTxPackets() {
+ return nativeGetIfaceStat(LOOPBACK_IFACE, TYPE_TX_PACKETS);
+ }
+
+ /** {@hide} */
+ @TestApi
+ public static long getLoopbackRxPackets() {
+ return nativeGetIfaceStat(LOOPBACK_IFACE, TYPE_RX_PACKETS);
+ }
+
+ /** {@hide} */
+ @TestApi
+ public static long getLoopbackTxBytes() {
+ return nativeGetIfaceStat(LOOPBACK_IFACE, TYPE_TX_BYTES);
+ }
+
+ /** {@hide} */
+ @TestApi
+ public static long getLoopbackRxBytes() {
+ return nativeGetIfaceStat(LOOPBACK_IFACE, TYPE_RX_BYTES);
+ }
+
/**
* Return number of packets transmitted since device boot. Counts packets
* across all network interfaces, and always increases monotonically since
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 948d4ce..9513b9b 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -658,32 +658,40 @@
*/
public static final int PROCESS_STATE_FOREGROUND_SERVICE = 1;
/**
- * Time this uid has any process that is top while the device is sleeping, but none
- * in the "foreground service" or better state.
- */
- public static final int PROCESS_STATE_TOP_SLEEPING = 2;
- /**
* Time this uid has any process in an active foreground state, but none in the
* "top sleeping" or better state.
*/
- public static final int PROCESS_STATE_FOREGROUND = 3;
+ public static final int PROCESS_STATE_FOREGROUND = 2;
/**
* Time this uid has any process in an active background state, but none in the
* "foreground" or better state.
*/
- public static final int PROCESS_STATE_BACKGROUND = 4;
+ public static final int PROCESS_STATE_BACKGROUND = 3;
+ /**
+ * Time this uid has any process that is top while the device is sleeping, but not
+ * active for any other reason. We kind-of consider it a kind of cached process
+ * for execution restrictions.
+ */
+ public static final int PROCESS_STATE_TOP_SLEEPING = 4;
+ /**
+ * Time this uid has any process that is in the background but it has an activity
+ * marked as "can't save state". This is essentially a cached process, though the
+ * system will try much harder than normal to avoid killing it.
+ */
+ public static final int PROCESS_STATE_HEAVY_WEIGHT = 5;
/**
* Time this uid has any processes that are sitting around cached, not in one of the
* other active states.
*/
- public static final int PROCESS_STATE_CACHED = 5;
+ public static final int PROCESS_STATE_CACHED = 6;
/**
* Total number of process states we track.
*/
- public static final int NUM_PROCESS_STATE = 6;
+ public static final int NUM_PROCESS_STATE = 7;
static final String[] PROCESS_STATE_NAMES = {
- "Top", "Fg Service", "Top Sleeping", "Foreground", "Background", "Cached"
+ "Top", "Fg Service", "Foreground", "Background", "Top Sleeping", "Heavy Weight",
+ "Cached"
};
public abstract long getProcessStateTime(int state, long elapsedRealtimeUs, int which);
@@ -6742,7 +6750,7 @@
/** Dump #STATS_SINCE_CHARGED batterystats data to a proto. @hide */
public void dumpProtoLocked(Context context, FileDescriptor fd, List<ApplicationInfo> apps,
- int flags, long historyStart) {
+ int flags) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
final long bToken = proto.start(BatteryStatsServiceDumpProto.BATTERYSTATS);
prepareForDumpLocked();
@@ -6752,13 +6760,7 @@
proto.write(BatteryStatsProto.START_PLATFORM_VERSION, getStartPlatformVersion());
proto.write(BatteryStatsProto.END_PLATFORM_VERSION, getEndPlatformVersion());
- long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
-
- if ((flags & (DUMP_INCLUDE_HISTORY | DUMP_HISTORY_ONLY)) != 0) {
- if (startIteratingHistoryLocked()) {
- // TODO: implement dumpProtoHistoryLocked(proto);
- }
- }
+ // History intentionally not included in proto dump.
if ((flags & (DUMP_HISTORY_ONLY | DUMP_DAILY_ONLY)) == 0) {
final BatteryStatsHelper helper = new BatteryStatsHelper(context, false,
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index c1722d5..48f5684 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -221,13 +221,31 @@
public static final String SDK = getString("ro.build.version.sdk");
/**
- * The user-visible SDK version of the framework; its possible
- * values are defined in {@link Build.VERSION_CODES}.
+ * The SDK version of the software currently running on this hardware
+ * device. This value never changes while a device is booted, but it may
+ * increase when the hardware manufacturer provides an OTA update.
+ * <p>
+ * Possible values are defined in {@link Build.VERSION_CODES}.
+ *
+ * @see #FIRST_SDK_INT
*/
public static final int SDK_INT = SystemProperties.getInt(
"ro.build.version.sdk", 0);
/**
+ * The SDK version of the software that <em>initially</em> shipped on
+ * this hardware device. It <em>never</em> changes during the lifetime
+ * of the device, even when {@link #SDK_INT} increases due to an OTA
+ * update.
+ * <p>
+ * Possible values are defined in {@link Build.VERSION_CODES}.
+ *
+ * @see #SDK_INT
+ */
+ public static final int FIRST_SDK_INT = SystemProperties
+ .getInt("ro.product.first_api_level", 0);
+
+ /**
* The developer preview revision of a prerelease SDK. This value will always
* be <code>0</code> on production platform builds/devices.
*
@@ -270,6 +288,14 @@
* @hide
*/
public static final int RESOURCES_SDK_INT = SDK_INT + ACTIVE_CODENAMES.length;
+
+ /**
+ * The current lowest supported value of app target SDK. Applications targeting
+ * lower values will fail to install and run. Its possible values are defined
+ * in {@link Build.VERSION_CODES}.
+ */
+ public static final int MIN_SUPPORTED_TARGET_SDK_INT = SystemProperties.getInt(
+ "ro.build.version.min_supported_target_sdk", 0);
}
/**
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 2acf36f..848ab88 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1136,7 +1136,7 @@
int intervalUs) {
VMDebug.startMethodTracing(fixTracePath(tracePath), bufferSize, 0, true, intervalUs);
}
-
+
/**
* Formats name of trace log file for method tracing.
*/
@@ -1706,11 +1706,11 @@
* Retrieves information about this processes memory usages. This information is broken down by
* how much is in use by dalvik, the native heap, and everything else.
*
- * <p><b>Note:</b> this method directly retrieves memory information for the give process
+ * <p><b>Note:</b> this method directly retrieves memory information for the given process
* from low-level data available to it. It may not be able to retrieve information about
* some protected allocations, such as graphics. If you want to be sure you can see
- * all information about allocations by the process, use instead
- * {@link android.app.ActivityManager#getProcessMemoryInfo(int[])}.</p>
+ * all information about allocations by the process, use
+ * {@link android.app.ActivityManager#getProcessMemoryInfo(int[])} instead.</p>
*/
public static native void getMemoryInfo(MemoryInfo memoryInfo);
diff --git a/core/java/android/os/HandlerExecutor.java b/core/java/android/os/HandlerExecutor.java
new file mode 100644
index 0000000..416b24b
--- /dev/null
+++ b/core/java/android/os/HandlerExecutor.java
@@ -0,0 +1,45 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+/**
+ * An adapter {@link Executor} that posts all executed tasks onto the given
+ * {@link Handler}.
+ *
+ * @hide
+ */
+public class HandlerExecutor implements Executor {
+ private final Handler mHandler;
+
+ public HandlerExecutor(@NonNull Handler handler) {
+ mHandler = Preconditions.checkNotNull(handler);
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ if (!mHandler.post(command)) {
+ throw new RejectedExecutionException(mHandler + " is shutting down");
+ }
+ }
+}
diff --git a/core/java/android/os/HardwarePropertiesManager.java b/core/java/android/os/HardwarePropertiesManager.java
index aad202e..eae7d70 100644
--- a/core/java/android/os/HardwarePropertiesManager.java
+++ b/core/java/android/os/HardwarePropertiesManager.java
@@ -40,9 +40,11 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({
- DEVICE_TEMPERATURE_CPU, DEVICE_TEMPERATURE_GPU, DEVICE_TEMPERATURE_BATTERY,
- DEVICE_TEMPERATURE_SKIN
+ @IntDef(prefix = { "DEVICE_TEMPERATURE_" }, value = {
+ DEVICE_TEMPERATURE_CPU,
+ DEVICE_TEMPERATURE_GPU,
+ DEVICE_TEMPERATURE_BATTERY,
+ DEVICE_TEMPERATURE_SKIN
})
public @interface DeviceTemperatureType {}
@@ -50,9 +52,11 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({
- TEMPERATURE_CURRENT, TEMPERATURE_THROTTLING, TEMPERATURE_SHUTDOWN,
- TEMPERATURE_THROTTLING_BELOW_VR_MIN
+ @IntDef(prefix = { "TEMPERATURE_" }, value = {
+ TEMPERATURE_CURRENT,
+ TEMPERATURE_THROTTLING,
+ TEMPERATURE_SHUTDOWN,
+ TEMPERATURE_THROTTLING_BELOW_VR_MIN
})
public @interface TemperatureSource {}
diff --git a/core/java/android/os/IIncidentManager.aidl b/core/java/android/os/IIncidentManager.aidl
index 1a76648..b67b99f 100644
--- a/core/java/android/os/IIncidentManager.aidl
+++ b/core/java/android/os/IIncidentManager.aidl
@@ -16,7 +16,6 @@
package android.os;
-import android.os.IIncidentReportCompletedListener;
import android.os.IIncidentReportStatusListener;
import android.os.IncidentReportArgs;
diff --git a/core/java/android/os/IIncidentReportCompletedListener.aidl b/core/java/android/os/IIncidentReportCompletedListener.aidl
deleted file mode 100644
index 2d66bf6..0000000
--- a/core/java/android/os/IIncidentReportCompletedListener.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * 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.
- */
-
-package android.os;
-
-/**
- * Listener for incident report status
- *
- * {@hide}
- */
-oneway interface IIncidentReportCompletedListener {
- /**
- * Called when there has been an incident report.
- *
- * The system service implementing this method should delete or move the file
- * after it is finished with it.
- */
- void onIncidentReport(String filename);
-}
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 624e28a..96e7a59 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -871,7 +871,11 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag=true, value={EVENT_INPUT, EVENT_OUTPUT, EVENT_ERROR})
+ @IntDef(flag = true, prefix = { "EVENT_" }, value = {
+ EVENT_INPUT,
+ EVENT_OUTPUT,
+ EVENT_ERROR
+ })
public @interface Events {}
/**
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 01fe5bf..56c6391 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -466,7 +466,7 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({
+ @IntDef(prefix = { "SHUTDOWN_REASON_" }, value = {
SHUTDOWN_REASON_UNKNOWN,
SHUTDOWN_REASON_SHUTDOWN,
SHUTDOWN_REASON_REBOOT,
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index b9b9a18..bbb8a7b 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -334,6 +334,23 @@
}
/**
+ * Performs {@code action} for each cookie associated with a callback, calling
+ * {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
+ *
+ * @hide
+ */
+ public <C> void broadcastForEachCookie(Consumer<C> action) {
+ int itemCount = beginBroadcast();
+ try {
+ for (int i = 0; i < itemCount; i++) {
+ action.accept((C) getBroadcastCookie(i));
+ }
+ } finally {
+ finishBroadcast();
+ }
+ }
+
+ /**
* Returns the number of registered callbacks. Note that the number of registered
* callbacks may differ from the value returned by {@link #beginBroadcast()} since
* the former returns the number of callbacks registered at the time of the call
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 3ec744d..3e8161f 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -52,7 +52,7 @@
// pushed ones to be consistent.
write4Bytes(STATS_BUFFER_TAG_ID);
mStorage.write(EVENT_TYPE_LIST); // This is required to start the log entry.
- mStorage.write(fields); // Indicate number of elements in this list.
+ mStorage.write(fields + 1); // Indicate number of elements in this list. +1 for the tag
mStorage.write(EVENT_TYPE_INT);
// The first element is the real atom tag number
write4Bytes(tag);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 61dd462..3504142 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -103,8 +103,12 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag=true, value={RESTRICTION_NOT_SET, RESTRICTION_SOURCE_SYSTEM,
- RESTRICTION_SOURCE_DEVICE_OWNER, RESTRICTION_SOURCE_PROFILE_OWNER})
+ @IntDef(flag = true, prefix = { "RESTRICTION_" }, value = {
+ RESTRICTION_NOT_SET,
+ RESTRICTION_SOURCE_SYSTEM,
+ RESTRICTION_SOURCE_DEVICE_OWNER,
+ RESTRICTION_SOURCE_PROFILE_OWNER
+ })
@SystemApi
public @interface UserRestrictionSource {}
@@ -770,6 +774,25 @@
public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
/**
+ * Specifies that the managed profile is not allowed to have unified lock screen challenge with
+ * the primary user.
+ *
+ * <p><strong>Note:</strong> Setting this restriction alone doesn't automatically set a
+ * separate challenge. Profile owner can ask the user to set a new password using
+ * {@link DevicePolicyManager#ACTION_SET_NEW_PASSWORD} and verify it using
+ * {@link DevicePolicyManager#isUsingUnifiedPassword(ComponentName)}.
+ *
+ * <p>Can be set by profile owners. It only has effect on managed profiles when set by managed
+ * profile owner. Has no effect on non-managed profiles or users.
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_UNIFIED_PASSWORD = "no_unified_password";
+
+ /**
* Allows apps in the parent profile to handle web links from the managed profile.
*
* This user restriction has an effect only in a managed profile.
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index 9369eeb..6c9f1b2 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -130,7 +130,8 @@
* <p>Called by the {@link com.android.server.devicepolicy.DevicePolicyManagerService} when
* createAndManageUser is called by the device owner.
*/
- public abstract UserInfo createUserEvenWhenDisallowed(String name, int flags);
+ public abstract UserInfo createUserEvenWhenDisallowed(String name, int flags,
+ String[] disallowedPackages);
/**
* Same as {@link UserManager#removeUser(int userHandle)}, but bypasses the check for
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index ecec448..8632aad 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -1,10 +1,13 @@
package android.os;
+import android.annotation.Nullable;
import android.os.WorkSourceProto;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Objects;
/**
* Describes the source of some work that may be done by someone else.
@@ -19,6 +22,8 @@
int[] mUids;
String[] mNames;
+ private ArrayList<WorkChain> mChains;
+
/**
* Internal statics to avoid object allocations in some operations.
* The WorkSource object itself is not thread safe, but we need to
@@ -39,6 +44,7 @@
*/
public WorkSource() {
mNum = 0;
+ mChains = null;
}
/**
@@ -48,6 +54,7 @@
public WorkSource(WorkSource orig) {
if (orig == null) {
mNum = 0;
+ mChains = null;
return;
}
mNum = orig.mNum;
@@ -58,6 +65,16 @@
mUids = null;
mNames = null;
}
+
+ if (orig.mChains != null) {
+ // Make a copy of all WorkChains that exist on |orig| since they are mutable.
+ mChains = new ArrayList<>(orig.mChains.size());
+ for (WorkChain chain : orig.mChains) {
+ mChains.add(new WorkChain(chain));
+ }
+ } else {
+ mChains = null;
+ }
}
/** @hide */
@@ -65,6 +82,7 @@
mNum = 1;
mUids = new int[] { uid, 0 };
mNames = null;
+ mChains = null;
}
/** @hide */
@@ -75,12 +93,21 @@
mNum = 1;
mUids = new int[] { uid, 0 };
mNames = new String[] { name, null };
+ mChains = null;
}
WorkSource(Parcel in) {
mNum = in.readInt();
mUids = in.createIntArray();
mNames = in.createStringArray();
+
+ int numChains = in.readInt();
+ if (numChains > 0) {
+ mChains = new ArrayList<>(numChains);
+ in.readParcelableList(mChains, WorkChain.class.getClassLoader());
+ } else {
+ mChains = null;
+ }
}
/** @hide */
@@ -99,7 +126,8 @@
}
/**
- * Clear names from this WorkSource. Uids are left intact.
+ * Clear names from this WorkSource. Uids are left intact. WorkChains if any, are left
+ * intact.
*
* <p>Useful when combining with another WorkSource that doesn't have names.
* @hide
@@ -127,11 +155,16 @@
*/
public void clear() {
mNum = 0;
+ if (mChains != null) {
+ mChains.clear();
+ }
}
@Override
public boolean equals(Object o) {
- return o instanceof WorkSource && !diff((WorkSource)o);
+ return o instanceof WorkSource
+ && !diff((WorkSource) o)
+ && Objects.equals(mChains, ((WorkSource) o).mChains);
}
@Override
@@ -145,6 +178,11 @@
result = ((result << 4) | (result >>> 28)) ^ mNames[i].hashCode();
}
}
+
+ if (mChains != null) {
+ result = ((result << 4) | (result >>> 28)) ^ mChains.hashCode();
+ }
+
return result;
}
@@ -153,6 +191,8 @@
* @param other The WorkSource to compare against.
* @return If there is a difference, true is returned.
*/
+ // TODO: This is a public API so it cannot be renamed. Because it is used in several places,
+ // we keep its semantics the same and ignore any differences in WorkChains (if any).
public boolean diff(WorkSource other) {
int N = mNum;
if (N != other.mNum) {
@@ -175,12 +215,15 @@
/**
* Replace the current contents of this work source with the given
- * work source. If <var>other</var> is null, the current work source
+ * work source. If {@code other} is null, the current work source
* will be made empty.
*/
public void set(WorkSource other) {
if (other == null) {
mNum = 0;
+ if (mChains != null) {
+ mChains.clear();
+ }
return;
}
mNum = other.mNum;
@@ -203,6 +246,18 @@
mUids = null;
mNames = null;
}
+
+ if (other.mChains != null) {
+ if (mChains != null) {
+ mChains.clear();
+ } else {
+ mChains = new ArrayList<>(other.mChains.size());
+ }
+
+ for (WorkChain chain : other.mChains) {
+ mChains.add(new WorkChain(chain));
+ }
+ }
}
/** @hide */
@@ -211,6 +266,7 @@
if (mUids == null) mUids = new int[2];
mUids[0] = uid;
mNames = null;
+ mChains.clear();
}
/** @hide */
@@ -225,9 +281,21 @@
}
mUids[0] = uid;
mNames[0] = name;
+ mChains.clear();
}
- /** @hide */
+ /**
+ * Legacy API, DO NOT USE: Only deals with flat UIDs and tags. No chains are transferred, and no
+ * differences in chains are returned. This will be removed once its callers have been
+ * rewritten.
+ *
+ * NOTE: This is currently only used in GnssLocationProvider.
+ *
+ * @hide
+ * @deprecated for internal use only. WorkSources are opaque and no external callers should need
+ * to be aware of internal differences.
+ */
+ @Deprecated
public WorkSource[] setReturningDiffs(WorkSource other) {
synchronized (sTmpWorkSource) {
sNewbWork = null;
@@ -251,11 +319,34 @@
*/
public boolean add(WorkSource other) {
synchronized (sTmpWorkSource) {
- return updateLocked(other, false, false);
+ boolean uidAdded = updateLocked(other, false, false);
+
+ boolean chainAdded = false;
+ if (other.mChains != null) {
+ // NOTE: This is quite an expensive operation, especially if the number of chains
+ // is large. We could look into optimizing it if it proves problematic.
+ if (mChains == null) {
+ mChains = new ArrayList<>(other.mChains.size());
+ }
+
+ for (WorkChain wc : other.mChains) {
+ if (!mChains.contains(wc)) {
+ mChains.add(new WorkChain(wc));
+ }
+ }
+ }
+
+ return uidAdded || chainAdded;
}
}
- /** @hide */
+ /**
+ * Legacy API: DO NOT USE. Only in use from unit tests.
+ *
+ * @hide
+ * @deprecated meant for unit testing use only. Will be removed in a future API revision.
+ */
+ @Deprecated
public WorkSource addReturningNewbs(WorkSource other) {
synchronized (sTmpWorkSource) {
sNewbWork = null;
@@ -311,22 +402,14 @@
return true;
}
- /** @hide */
- public WorkSource addReturningNewbs(int uid) {
- synchronized (sTmpWorkSource) {
- sNewbWork = null;
- sTmpWorkSource.mUids[0] = uid;
- updateLocked(sTmpWorkSource, false, true);
- return sNewbWork;
- }
- }
-
public boolean remove(WorkSource other) {
if (mNum <= 0 || other.mNum <= 0) {
return false;
}
+
+ boolean uidRemoved = false;
if (mNames == null && other.mNames == null) {
- return removeUids(other);
+ uidRemoved = removeUids(other);
} else {
if (mNames == null) {
throw new IllegalArgumentException("Other " + other + " has names, but target "
@@ -336,24 +419,44 @@
throw new IllegalArgumentException("Target " + this + " has names, but other "
+ other + " does not");
}
- return removeUidsAndNames(other);
+ uidRemoved = removeUidsAndNames(other);
}
+
+ boolean chainRemoved = false;
+ if (other.mChains != null) {
+ if (mChains != null) {
+ chainRemoved = mChains.removeAll(other.mChains);
+ }
+ } else if (mChains != null) {
+ mChains.clear();
+ chainRemoved = true;
+ }
+
+ return uidRemoved || chainRemoved;
}
- /** @hide */
- public WorkSource stripNames() {
- if (mNum <= 0) {
- return new WorkSource();
+ /**
+ * Create a new {@code WorkChain} associated with this WorkSource and return it.
+ *
+ * @hide
+ */
+ public WorkChain createWorkChain() {
+ if (mChains == null) {
+ mChains = new ArrayList<>(4);
}
- WorkSource result = new WorkSource();
- int lastUid = -1;
- for (int i=0; i<mNum; i++) {
- int uid = mUids[i];
- if (i == 0 || lastUid != uid) {
- result.add(uid);
- }
- }
- return result;
+
+ final WorkChain wc = new WorkChain();
+ mChains.add(wc);
+
+ return wc;
+ }
+
+ /**
+ * @return the list of {@code WorkChains} associated with this {@code WorkSource}.
+ * @hide
+ */
+ public ArrayList<WorkChain> getWorkChains() {
+ return mChains;
}
private boolean removeUids(WorkSource other) {
@@ -664,6 +767,167 @@
}
}
+ /**
+ * Represents an attribution chain for an item of work being performed. An attribution chain is
+ * an indexed list of {code (uid, tag)} nodes. The node at {@code index == 0} is the initiator
+ * of the work, and the node at the highest index performs the work. Nodes at other indices
+ * are intermediaries that facilitate the work. Examples :
+ *
+ * (1) Work being performed by uid=2456 (no chaining):
+ * <pre>
+ * WorkChain {
+ * mUids = { 2456 }
+ * mTags = { null }
+ * mSize = 1;
+ * }
+ * </pre>
+ *
+ * (2) Work being performed by uid=2456 (from component "c1") on behalf of uid=5678:
+ *
+ * <pre>
+ * WorkChain {
+ * mUids = { 5678, 2456 }
+ * mTags = { null, "c1" }
+ * mSize = 1
+ * }
+ * </pre>
+ *
+ * Attribution chains are mutable, though the only operation that can be performed on them
+ * is the addition of a new node at the end of the attribution chain to represent
+ *
+ * @hide
+ */
+ public static class WorkChain implements Parcelable {
+ private int mSize;
+ private int[] mUids;
+ private String[] mTags;
+
+ // @VisibleForTesting
+ public WorkChain() {
+ mSize = 0;
+ mUids = new int[4];
+ mTags = new String[4];
+ }
+
+ // @VisibleForTesting
+ public WorkChain(WorkChain other) {
+ mSize = other.mSize;
+ mUids = other.mUids.clone();
+ mTags = other.mTags.clone();
+ }
+
+ private WorkChain(Parcel in) {
+ mSize = in.readInt();
+ mUids = in.createIntArray();
+ mTags = in.createStringArray();
+ }
+
+ /**
+ * Append a node whose uid is {@code uid} and whose optional tag is {@code tag} to this
+ * {@code WorkChain}.
+ */
+ public WorkChain addNode(int uid, @Nullable String tag) {
+ if (mSize == mUids.length) {
+ resizeArrays();
+ }
+
+ mUids[mSize] = uid;
+ mTags[mSize] = tag;
+ mSize++;
+
+ return this;
+ }
+
+ // TODO: The following three trivial getters are purely for testing and will be removed
+ // once we have higher level logic in place, e.g for serializing this WorkChain to a proto,
+ // diffing it etc.
+ //
+ // @VisibleForTesting
+ public int[] getUids() {
+ return mUids;
+ }
+ // @VisibleForTesting
+ public String[] getTags() {
+ return mTags;
+ }
+ // @VisibleForTesting
+ public int getSize() {
+ return mSize;
+ }
+
+ private void resizeArrays() {
+ final int newSize = mSize * 2;
+ int[] uids = new int[newSize];
+ String[] tags = new String[newSize];
+
+ System.arraycopy(mUids, 0, uids, 0, mSize);
+ System.arraycopy(mTags, 0, tags, 0, mSize);
+
+ mUids = uids;
+ mTags = tags;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder("WorkChain{");
+ for (int i = 0; i < mSize; ++i) {
+ if (i != 0) {
+ result.append(", ");
+ }
+ result.append("(");
+ result.append(mUids[i]);
+ if (mTags[i] != null) {
+ result.append(", ");
+ result.append(mTags[i]);
+ }
+ result.append(")");
+ }
+
+ result.append("}");
+ return result.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return (mSize + 31 * Arrays.hashCode(mUids)) * 31 + Arrays.hashCode(mTags);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof WorkChain) {
+ WorkChain other = (WorkChain) o;
+
+ return mSize == other.mSize
+ && Arrays.equals(mUids, other.mUids)
+ && Arrays.equals(mTags, other.mTags);
+ }
+
+ return false;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mSize);
+ dest.writeIntArray(mUids);
+ dest.writeStringArray(mTags);
+ }
+
+ public static final Parcelable.Creator<WorkChain> CREATOR =
+ new Parcelable.Creator<WorkChain>() {
+ public WorkChain createFromParcel(Parcel in) {
+ return new WorkChain(in);
+ }
+ public WorkChain[] newArray(int size) {
+ return new WorkChain[size];
+ }
+ };
+ }
+
@Override
public int describeContents() {
return 0;
@@ -674,6 +938,13 @@
dest.writeInt(mNum);
dest.writeIntArray(mUids);
dest.writeStringArray(mNames);
+
+ if (mChains == null) {
+ dest.writeInt(-1);
+ } else {
+ dest.writeInt(mChains.size());
+ dest.writeParcelableList(mChains, flags);
+ }
}
@Override
@@ -690,6 +961,17 @@
result.append(mNames[i]);
}
}
+
+ if (mChains != null) {
+ result.append(" chains=");
+ for (int i = 0; i < mChains.size(); ++i) {
+ if (i != 0) {
+ result.append(", ");
+ }
+ result.append(mChains.get(i));
+ }
+ }
+
result.append("}");
return result.toString();
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 4796712..9833fe1 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1690,7 +1690,7 @@
public static final int FLAG_ALLOCATE_DEFY_HALF_RESERVED = 1 << 2;
/** @hide */
- @IntDef(flag = true, value = {
+ @IntDef(flag = true, prefix = { "FLAG_ALLOCATE_" }, value = {
FLAG_ALLOCATE_AGGRESSIVE,
FLAG_ALLOCATE_DEFY_ALL_RESERVED,
FLAG_ALLOCATE_DEFY_HALF_RESERVED,
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 1fc0b82..070b8c1 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -19,7 +19,6 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
-import android.net.TrafficStats;
import android.net.Uri;
import android.os.Environment;
import android.os.Parcel;
@@ -78,13 +77,11 @@
public final class StorageVolume implements Parcelable {
private final String mId;
- private final int mStorageId;
private final File mPath;
private final String mDescription;
private final boolean mPrimary;
private final boolean mRemovable;
private final boolean mEmulated;
- private final long mMtpReserveSize;
private final boolean mAllowMassStorage;
private final long mMaxFileSize;
private final UserHandle mOwner;
@@ -121,17 +118,15 @@
public static final int STORAGE_ID_PRIMARY = 0x00010001;
/** {@hide} */
- public StorageVolume(String id, int storageId, File path, String description, boolean primary,
- boolean removable, boolean emulated, long mtpReserveSize, boolean allowMassStorage,
+ public StorageVolume(String id, File path, String description, boolean primary,
+ boolean removable, boolean emulated, boolean allowMassStorage,
long maxFileSize, UserHandle owner, String fsUuid, String state) {
mId = Preconditions.checkNotNull(id);
- mStorageId = storageId;
mPath = Preconditions.checkNotNull(path);
mDescription = Preconditions.checkNotNull(description);
mPrimary = primary;
mRemovable = removable;
mEmulated = emulated;
- mMtpReserveSize = mtpReserveSize;
mAllowMassStorage = allowMassStorage;
mMaxFileSize = maxFileSize;
mOwner = Preconditions.checkNotNull(owner);
@@ -141,13 +136,11 @@
private StorageVolume(Parcel in) {
mId = in.readString();
- mStorageId = in.readInt();
mPath = new File(in.readString());
mDescription = in.readString();
mPrimary = in.readInt() != 0;
mRemovable = in.readInt() != 0;
mEmulated = in.readInt() != 0;
- mMtpReserveSize = in.readLong();
mAllowMassStorage = in.readInt() != 0;
mMaxFileSize = in.readLong();
mOwner = in.readParcelable(null);
@@ -211,34 +204,6 @@
}
/**
- * Returns the MTP storage ID for the volume.
- * this is also used for the storage_id column in the media provider.
- *
- * @return MTP storage ID
- * @hide
- */
- public int getStorageId() {
- return mStorageId;
- }
-
- /**
- * Number of megabytes of space to leave unallocated by MTP.
- * MTP will subtract this value from the free space it reports back
- * to the host via GetStorageInfo, and will not allow new files to
- * be added via MTP if there is less than this amount left free in the storage.
- * If MTP has dedicated storage this value should be zero, but if MTP is
- * sharing storage with the rest of the system, set this to a positive value
- * to ensure that MTP activity does not result in the storage being
- * too close to full.
- *
- * @return MTP reserve space
- * @hide
- */
- public int getMtpReserveSpace() {
- return (int) (mMtpReserveSize / TrafficStats.MB_IN_BYTES);
- }
-
- /**
* Returns true if this volume can be shared via USB mass storage.
*
* @return whether mass storage is allowed
@@ -385,13 +350,11 @@
pw.println("StorageVolume:");
pw.increaseIndent();
pw.printPair("mId", mId);
- pw.printPair("mStorageId", mStorageId);
pw.printPair("mPath", mPath);
pw.printPair("mDescription", mDescription);
pw.printPair("mPrimary", mPrimary);
pw.printPair("mRemovable", mRemovable);
pw.printPair("mEmulated", mEmulated);
- pw.printPair("mMtpReserveSize", mMtpReserveSize);
pw.printPair("mAllowMassStorage", mAllowMassStorage);
pw.printPair("mMaxFileSize", mMaxFileSize);
pw.printPair("mOwner", mOwner);
@@ -420,13 +383,11 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(mId);
- parcel.writeInt(mStorageId);
parcel.writeString(mPath.toString());
parcel.writeString(mDescription);
parcel.writeInt(mPrimary ? 1 : 0);
parcel.writeInt(mRemovable ? 1 : 0);
parcel.writeInt(mEmulated ? 1 : 0);
- parcel.writeLong(mMtpReserveSize);
parcel.writeInt(mAllowMassStorage ? 1 : 0);
parcel.writeLong(mMaxFileSize);
parcel.writeParcelable(mOwner, flags);
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 76f79f1..d3877ca 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -343,9 +343,7 @@
String description = null;
String derivedFsUuid = fsUuid;
- long mtpReserveSize = 0;
long maxFileSize = 0;
- int mtpStorageId = StorageVolume.STORAGE_ID_INVALID;
if (type == TYPE_EMULATED) {
emulated = true;
@@ -356,12 +354,6 @@
derivedFsUuid = privateVol.fsUuid;
}
- if (isPrimary()) {
- mtpStorageId = StorageVolume.STORAGE_ID_PRIMARY;
- }
-
- mtpReserveSize = storage.getStorageLowBytes(userPath);
-
if (ID_EMULATED_INTERNAL.equals(id)) {
removable = false;
} else {
@@ -374,14 +366,6 @@
description = storage.getBestVolumeDescription(this);
- if (isPrimary()) {
- mtpStorageId = StorageVolume.STORAGE_ID_PRIMARY;
- } else {
- // Since MediaProvider currently persists this value, we need a
- // value that is stable over time.
- mtpStorageId = buildStableMtpStorageId(fsUuid);
- }
-
if ("vfat".equals(fsType)) {
maxFileSize = 4294967295L;
}
@@ -394,8 +378,8 @@
description = context.getString(android.R.string.unknownName);
}
- return new StorageVolume(id, mtpStorageId, userPath, description, isPrimary(), removable,
- emulated, mtpReserveSize, allowMassStorage, maxFileSize, new UserHandle(userId),
+ return new StorageVolume(id, userPath, description, isPrimary(), removable,
+ emulated, allowMassStorage, maxFileSize, new UserHandle(userId),
derivedFsUuid, envState);
}
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index ce5b11e..f0d9a0c 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -49,8 +49,9 @@
public final class PrintAttributes implements Parcelable {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = {
- COLOR_MODE_MONOCHROME, COLOR_MODE_COLOR
+ @IntDef(flag = true, prefix = { "COLOR_MODE_" }, value = {
+ COLOR_MODE_MONOCHROME,
+ COLOR_MODE_COLOR
})
@interface ColorMode {
}
@@ -64,8 +65,10 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = {
- DUPLEX_MODE_NONE, DUPLEX_MODE_LONG_EDGE, DUPLEX_MODE_SHORT_EDGE
+ @IntDef(flag = true, prefix = { "DUPLEX_MODE_" }, value = {
+ DUPLEX_MODE_NONE,
+ DUPLEX_MODE_LONG_EDGE,
+ DUPLEX_MODE_SHORT_EDGE
})
@interface DuplexMode {
}
diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java
index 6143404..55c902e 100644
--- a/core/java/android/print/PrintDocumentInfo.java
+++ b/core/java/android/print/PrintDocumentInfo.java
@@ -83,11 +83,14 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({
- CONTENT_TYPE_UNKNOWN, CONTENT_TYPE_DOCUMENT, CONTENT_TYPE_PHOTO
+ @IntDef(prefix = { "CONTENT_TYPE_" }, value = {
+ CONTENT_TYPE_UNKNOWN,
+ CONTENT_TYPE_DOCUMENT,
+ CONTENT_TYPE_PHOTO
})
public @interface ContentType {
}
+
/**
* Content type: unknown.
*/
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 94686a8..85fdd64 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -45,9 +45,14 @@
public final class PrintJobInfo implements Parcelable {
/** @hide */
- @IntDef({
- STATE_CREATED, STATE_QUEUED, STATE_STARTED, STATE_BLOCKED, STATE_COMPLETED,
- STATE_FAILED, STATE_CANCELED
+ @IntDef(prefix = { "STATE_" }, value = {
+ STATE_CREATED,
+ STATE_QUEUED,
+ STATE_STARTED,
+ STATE_BLOCKED,
+ STATE_COMPLETED,
+ STATE_FAILED,
+ STATE_CANCELED
})
@Retention(RetentionPolicy.SOURCE)
public @interface State {
diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java
index 88feab7..e79cc65 100644
--- a/core/java/android/print/PrinterInfo.java
+++ b/core/java/android/print/PrinterInfo.java
@@ -53,12 +53,15 @@
public final class PrinterInfo implements Parcelable {
/** @hide */
- @IntDef({
- STATUS_IDLE, STATUS_BUSY, STATUS_UNAVAILABLE
+ @IntDef(prefix = { "STATUS_" }, value = {
+ STATUS_IDLE,
+ STATUS_BUSY,
+ STATUS_UNAVAILABLE
})
@Retention(RetentionPolicy.SOURCE)
public @interface Status {
}
+
/** Printer status: the printer is idle and ready to print. */
public static final int STATUS_IDLE = PrinterInfoProto.STATUS_IDLE;
diff --git a/core/java/android/privacy/DifferentialPrivacyConfig.java b/core/java/android/privacy/DifferentialPrivacyConfig.java
new file mode 100644
index 0000000..e14893e
--- /dev/null
+++ b/core/java/android/privacy/DifferentialPrivacyConfig.java
@@ -0,0 +1,34 @@
+/*
+ * 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.privacy;
+
+/**
+ * An interface for differential privacy configuration.
+ * {@link DifferentialPrivacyEncoder} will apply this configuration to do differential privacy
+ * encoding.
+ *
+ * @hide
+ */
+public interface DifferentialPrivacyConfig {
+
+ /**
+ * Returns the name of the algorithm used in differential privacy config.
+ *
+ * @return The name of the algorithm
+ */
+ String getAlgorithm();
+}
diff --git a/core/java/android/privacy/DifferentialPrivacyEncoder.java b/core/java/android/privacy/DifferentialPrivacyEncoder.java
new file mode 100644
index 0000000..9355d6a
--- /dev/null
+++ b/core/java/android/privacy/DifferentialPrivacyEncoder.java
@@ -0,0 +1,78 @@
+/*
+ * 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.privacy;
+
+/**
+ * An interface for differential privacy encoder.
+ * Applications can use it to convert privacy sensitive data to privacy protected report.
+ * There is no decoder implemented in Android as it is not possible decode a single report by
+ * design.
+ *
+ * <p>Each type of log should have its own encoder, otherwise it may leak
+ * some information about Permanent Randomized Response(PRR, is used to create a “noisy”
+ * answer which is memoized by the client and permanently reused in place of the real answer).
+ *
+ * <p>Some encoders may not support all encoding methods, and it will throw {@link
+ * UnsupportedOperationException} if you call unsupported encoding method.
+ *
+ * <p><b>WARNING:</b> Privacy protection works only when encoder uses a suitable DP configuration,
+ * and the configuration and algorithm that is suitable is highly dependent on the use case.
+ * If the configuration is not suitable for the use case, it may hurt privacy or utility or both.
+ *
+ * @hide
+ */
+public interface DifferentialPrivacyEncoder {
+
+ /**
+ * Apply differential privacy to encode a string.
+ *
+ * @param original An arbitrary string
+ * @return Differential privacy encoded bytes derived from the string
+ */
+ byte[] encodeString(String original);
+
+ /**
+ * Apply differential privacy to encode a boolean.
+ *
+ * @param original An arbitrary boolean.
+ * @return Differential privacy encoded bytes derived from the boolean
+ */
+ byte[] encodeBoolean(boolean original);
+
+ /**
+ * Apply differential privacy to encode sequence of bytes.
+ *
+ * @param original An arbitrary byte array.
+ * @return Differential privacy encoded bytes derived from the bytes
+ */
+ byte[] encodeBits(byte[] original);
+
+ /**
+ * Returns the configuration that this encoder is using.
+ */
+ DifferentialPrivacyConfig getConfig();
+
+ /**
+ * Return True if the output from encoder is NOT securely randomized, otherwise encoder should
+ * be secure to randomize input.
+ *
+ * <b> A non-secure encoder is intended only for testing only and must not be used to process
+ * real data.
+ * </b>
+ */
+ boolean isInsecureEncoderForTest();
+}
diff --git a/core/java/android/privacy/internal/longitudinalreporting/LongitudinalReportingConfig.java b/core/java/android/privacy/internal/longitudinalreporting/LongitudinalReportingConfig.java
new file mode 100644
index 0000000..ee910fc
--- /dev/null
+++ b/core/java/android/privacy/internal/longitudinalreporting/LongitudinalReportingConfig.java
@@ -0,0 +1,107 @@
+/*
+ * 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.privacy.internal.longitudinalreporting;
+
+import android.privacy.DifferentialPrivacyConfig;
+import android.privacy.internal.rappor.RapporConfig;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class to store {@link LongitudinalReportingEncoder} configuration.
+ *
+ * <ul>
+ * <li> f is probability to flip input value, used in IRR.
+ * <li> p is probability to override input value, used in PRR1.
+ * <li> q is probability to set input value as 1 when result of PRR(p) is true, used in PRR2.
+ * </ul>
+ *
+ * @hide
+ */
+public class LongitudinalReportingConfig implements DifferentialPrivacyConfig {
+
+ private static final String ALGORITHM_NAME = "LongitudinalReporting";
+
+ // Probability to flip input value.
+ private final double mProbabilityF;
+
+ // Probability to override original value.
+ private final double mProbabilityP;
+ // Probability to override value with 1.
+ private final double mProbabilityQ;
+
+ // IRR config to randomize original value
+ private final RapporConfig mIRRConfig;
+
+ private final String mEncoderId;
+
+ /**
+ * Constructor to create {@link LongitudinalReportingConfig} used for {@link
+ * LongitudinalReportingEncoder}
+ *
+ * @param encoderId Unique encoder id.
+ * @param probabilityF Probability F used in Longitudinal Reporting algorithm.
+ * @param probabilityP Probability P used in Longitudinal Reporting algorithm. This will be
+ * quantized to the nearest 1/256.
+ * @param probabilityQ Probability Q used in Longitudinal Reporting algorithm. This will be
+ * quantized to the nearest 1/256.
+ */
+ public LongitudinalReportingConfig(String encoderId, double probabilityF,
+ double probabilityP, double probabilityQ) {
+ Preconditions.checkArgument(probabilityF >= 0 && probabilityF <= 1,
+ "probabilityF must be in range [0.0, 1.0]");
+ this.mProbabilityF = probabilityF;
+ Preconditions.checkArgument(probabilityP >= 0 && probabilityP <= 1,
+ "probabilityP must be in range [0.0, 1.0]");
+ this.mProbabilityP = probabilityP;
+ Preconditions.checkArgument(probabilityQ >= 0 && probabilityQ <= 1,
+ "probabilityQ must be in range [0.0, 1.0]");
+ this.mProbabilityQ = probabilityQ;
+ Preconditions.checkArgument(!TextUtils.isEmpty(encoderId), "encoderId cannot be empty");
+ mEncoderId = encoderId;
+ mIRRConfig = new RapporConfig(encoderId, 1, 0.0, probabilityF, 1 - probabilityF, 1, 1);
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return ALGORITHM_NAME;
+ }
+
+ RapporConfig getIRRConfig() {
+ return mIRRConfig;
+ }
+
+ double getProbabilityP() {
+ return mProbabilityP;
+ }
+
+ double getProbabilityQ() {
+ return mProbabilityQ;
+ }
+
+ String getEncoderId() {
+ return mEncoderId;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("EncoderId: %s, ProbabilityF: %.3f, ProbabilityP: %.3f"
+ + ", ProbabilityQ: %.3f",
+ mEncoderId, mProbabilityF, mProbabilityP, mProbabilityQ);
+ }
+}
diff --git a/core/java/android/privacy/internal/longitudinalreporting/LongitudinalReportingEncoder.java b/core/java/android/privacy/internal/longitudinalreporting/LongitudinalReportingEncoder.java
new file mode 100644
index 0000000..219868d
--- /dev/null
+++ b/core/java/android/privacy/internal/longitudinalreporting/LongitudinalReportingEncoder.java
@@ -0,0 +1,170 @@
+/*
+ * 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.privacy.internal.longitudinalreporting;
+
+import android.privacy.DifferentialPrivacyEncoder;
+import android.privacy.internal.rappor.RapporConfig;
+import android.privacy.internal.rappor.RapporEncoder;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Differential privacy encoder by using Longitudinal Reporting algorithm.
+ *
+ * <b>
+ * Notes: It supports encodeBoolean() only for now.
+ * </b>
+ *
+ * <p>
+ * Definition:
+ * PRR = Permanent Randomized Response
+ * IRR = Instantaneous Randomized response
+ *
+ * Algorithm:
+ * Step 1: Create long-term secrets x(ignoreOriginalInput)=Ber(P), y=Ber(Q), where Ber denotes
+ * Bernoulli distribution on {0, 1}, and we use it as a long-term secret, we implement Ber(x) by
+ * using PRR(2x, 0) when x < 1/2, PRR(2(1-x), 1) when x >= 1/2.
+ * Step 2: If x is 0, report IRR(F, original), otherwise report IRR(F, y)
+ * </p>
+ *
+ * Reference: go/bit-reporting-with-longitudinal-privacy
+ * TODO: Add a public blog / site to explain how it works.
+ *
+ * @hide
+ */
+public class LongitudinalReportingEncoder implements DifferentialPrivacyEncoder {
+
+ // Suffix that will be added to Rappor's encoder id. There's a (relatively) small risk some
+ // other Rappor encoder may re-use the same encoder id.
+ private static final String PRR1_ENCODER_ID = "prr1_encoder_id";
+ private static final String PRR2_ENCODER_ID = "prr2_encoder_id";
+
+ private final LongitudinalReportingConfig mConfig;
+
+ // IRR encoder to encode input value.
+ private final RapporEncoder mIRREncoder;
+
+ // A value that used to replace original value as input, so there's always a chance we are
+ // doing IRR on a fake value not actual original value.
+ // Null if original value does not need to be replaced.
+ private final Boolean mFakeValue;
+
+ // True if encoder is securely randomized.
+ private final boolean mIsSecure;
+
+ /**
+ * Create {@link LongitudinalReportingEncoder} with
+ * {@link LongitudinalReportingConfig} provided.
+ *
+ * @param config Longitudinal Reporting parameters to encode input
+ * @param userSecret User generated secret that used to generate PRR
+ * @return {@link LongitudinalReportingEncoder} instance
+ */
+ public static LongitudinalReportingEncoder createEncoder(LongitudinalReportingConfig config,
+ byte[] userSecret) {
+ return new LongitudinalReportingEncoder(config, true, userSecret);
+ }
+
+ /**
+ * Create <strong>insecure</strong> {@link LongitudinalReportingEncoder} with
+ * {@link LongitudinalReportingConfig} provided.
+ * Should not use it to process sensitive data.
+ *
+ * @param config Rappor parameters to encode input.
+ * @return {@link LongitudinalReportingEncoder} instance.
+ */
+ @VisibleForTesting
+ public static LongitudinalReportingEncoder createInsecureEncoderForTest(
+ LongitudinalReportingConfig config) {
+ return new LongitudinalReportingEncoder(config, false, null);
+ }
+
+ private LongitudinalReportingEncoder(LongitudinalReportingConfig config,
+ boolean secureEncoder, byte[] userSecret) {
+ mConfig = config;
+ mIsSecure = secureEncoder;
+ final boolean ignoreOriginalInput = getLongTermRandomizedResult(config.getProbabilityP(),
+ secureEncoder, userSecret, config.getEncoderId() + PRR1_ENCODER_ID);
+
+ if (ignoreOriginalInput) {
+ mFakeValue = getLongTermRandomizedResult(config.getProbabilityQ(),
+ secureEncoder, userSecret, config.getEncoderId() + PRR2_ENCODER_ID);
+ } else {
+ // Not using fake value, so IRR will be processed on real input value.
+ mFakeValue = null;
+ }
+
+ final RapporConfig irrConfig = config.getIRRConfig();
+ mIRREncoder = secureEncoder
+ ? RapporEncoder.createEncoder(irrConfig, userSecret)
+ : RapporEncoder.createInsecureEncoderForTest(irrConfig);
+ }
+
+ @Override
+ public byte[] encodeString(String original) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public byte[] encodeBoolean(boolean original) {
+ if (mFakeValue != null) {
+ // Use the fake value generated in PRR.
+ original = mFakeValue.booleanValue();
+ }
+ return mIRREncoder.encodeBoolean(original);
+ }
+
+ @Override
+ public byte[] encodeBits(byte[] bits) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public LongitudinalReportingConfig getConfig() {
+ return mConfig;
+ }
+
+ @Override
+ public boolean isInsecureEncoderForTest() {
+ return !mIsSecure;
+ }
+
+ /**
+ * Get PRR result that with probability p is 1, probability 1-p is 0.
+ */
+ @VisibleForTesting
+ public static boolean getLongTermRandomizedResult(double p, boolean secureEncoder,
+ byte[] userSecret, String encoderId) {
+ // Use Rappor to get PRR result. Rappor's P and Q are set to 0 and 1 so IRR will not be
+ // effective.
+ // As Rappor has rapporF/2 chance returns 0, rapporF/2 chance returns 1, and 1-rapporF
+ // chance returns original input.
+ // If p < 0.5, setting rapporF=2p and input=0 will make Rappor has p chance to return 1
+ // P(output=1 | input=0) = rapporF/2 = 2p/2 = p.
+ // If p >= 0.5, setting rapporF=2(1-p) and input=1 will make Rappor has p chance
+ // to return 1.
+ // P(output=1 | input=1) = rapporF/2 + (1 - rapporF) = 2(1-p)/2 + (1 - 2(1-p)) = p.
+ final double effectiveF = p < 0.5f ? p * 2 : (1 - p) * 2;
+ final boolean prrInput = p < 0.5f ? false : true;
+ final RapporConfig prrConfig = new RapporConfig(encoderId, 1, effectiveF,
+ 0, 1, 1, 1);
+ final RapporEncoder encoder = secureEncoder
+ ? RapporEncoder.createEncoder(prrConfig, userSecret)
+ : RapporEncoder.createInsecureEncoderForTest(prrConfig);
+ return encoder.encodeBoolean(prrInput)[0] > 0;
+ }
+}
diff --git a/core/java/android/privacy/internal/rappor/RapporConfig.java b/core/java/android/privacy/internal/rappor/RapporConfig.java
new file mode 100644
index 0000000..221999b
--- /dev/null
+++ b/core/java/android/privacy/internal/rappor/RapporConfig.java
@@ -0,0 +1,87 @@
+/*
+ * 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.privacy.internal.rappor;
+
+import android.privacy.DifferentialPrivacyConfig;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class to store {@link RapporEncoder} config.
+ *
+ * @hide
+ */
+public class RapporConfig implements DifferentialPrivacyConfig {
+
+ private static final String ALGORITHM_NAME = "Rappor";
+
+ final String mEncoderId;
+ final int mNumBits;
+ final double mProbabilityF;
+ final double mProbabilityP;
+ final double mProbabilityQ;
+ final int mNumCohorts;
+ final int mNumBloomHashes;
+
+ /**
+ * Constructor for {@link RapporConfig}.
+ *
+ * @param encoderId Unique id for encoder.
+ * @param numBits Number of bits to be encoded in Rappor algorithm.
+ * @param probabilityF Probability F that used in Rappor algorithm. This will be
+ * quantized to the nearest 1/128.
+ * @param probabilityP Probability P that used in Rappor algorithm.
+ * @param probabilityQ Probability Q that used in Rappor algorithm.
+ * @param numCohorts Number of cohorts that used in Rappor algorithm.
+ * @param numBloomHashes Number of bloom hashes that used in Rappor algorithm.
+ */
+ public RapporConfig(String encoderId, int numBits, double probabilityF,
+ double probabilityP, double probabilityQ, int numCohorts, int numBloomHashes) {
+ Preconditions.checkArgument(!TextUtils.isEmpty(encoderId), "encoderId cannot be empty");
+ this.mEncoderId = encoderId;
+ Preconditions.checkArgument(numBits > 0, "numBits needs to be > 0");
+ this.mNumBits = numBits;
+ Preconditions.checkArgument(probabilityF >= 0 && probabilityF <= 1,
+ "probabilityF must be in range [0.0, 1.0]");
+ this.mProbabilityF = probabilityF;
+ Preconditions.checkArgument(probabilityP >= 0 && probabilityP <= 1,
+ "probabilityP must be in range [0.0, 1.0]");
+ this.mProbabilityP = probabilityP;
+ Preconditions.checkArgument(probabilityQ >= 0 && probabilityQ <= 1,
+ "probabilityQ must be in range [0.0, 1.0]");
+ this.mProbabilityQ = probabilityQ;
+ Preconditions.checkArgument(numCohorts > 0, "numCohorts needs to be > 0");
+ this.mNumCohorts = numCohorts;
+ Preconditions.checkArgument(numBloomHashes > 0, "numBloomHashes needs to be > 0");
+ this.mNumBloomHashes = numBloomHashes;
+ }
+
+ @Override
+ public String getAlgorithm() {
+ return ALGORITHM_NAME;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "EncoderId: %s, NumBits: %d, ProbabilityF: %.3f, ProbabilityP: %.3f"
+ + ", ProbabilityQ: %.3f, NumCohorts: %d, NumBloomHashes: %d",
+ mEncoderId, mNumBits, mProbabilityF, mProbabilityP, mProbabilityQ,
+ mNumCohorts, mNumBloomHashes);
+ }
+}
diff --git a/core/java/android/privacy/internal/rappor/RapporEncoder.java b/core/java/android/privacy/internal/rappor/RapporEncoder.java
new file mode 100644
index 0000000..2eca4c98
--- /dev/null
+++ b/core/java/android/privacy/internal/rappor/RapporEncoder.java
@@ -0,0 +1,125 @@
+/*
+ * 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.privacy.internal.rappor;
+
+import android.privacy.DifferentialPrivacyEncoder;
+
+import com.google.android.rappor.Encoder;
+
+import java.security.SecureRandom;
+import java.util.Random;
+
+/**
+ * Differential privacy encoder by using
+ * <a href="https://research.google.com/pubs/pub42852.html">RAPPOR</a>
+ * algorithm.
+ *
+ * @hide
+ */
+public class RapporEncoder implements DifferentialPrivacyEncoder {
+
+ // Hard-coded seed and secret for insecure encoder
+ private static final long INSECURE_RANDOM_SEED = 0x12345678L;
+ private static final byte[] INSECURE_SECRET = new byte[]{
+ (byte) 0xD7, (byte) 0x68, (byte) 0x99, (byte) 0x93,
+ (byte) 0x94, (byte) 0x13, (byte) 0x53, (byte) 0x54,
+ (byte) 0xFE, (byte) 0xD0, (byte) 0x7E, (byte) 0x54,
+ (byte) 0xFE, (byte) 0xD0, (byte) 0x7E, (byte) 0x54,
+ (byte) 0xD7, (byte) 0x68, (byte) 0x99, (byte) 0x93,
+ (byte) 0x94, (byte) 0x13, (byte) 0x53, (byte) 0x54,
+ (byte) 0xFE, (byte) 0xD0, (byte) 0x7E, (byte) 0x54,
+ (byte) 0xFE, (byte) 0xD0, (byte) 0x7E, (byte) 0x54,
+ (byte) 0xD7, (byte) 0x68, (byte) 0x99, (byte) 0x93,
+ (byte) 0x94, (byte) 0x13, (byte) 0x53, (byte) 0x54,
+ (byte) 0xFE, (byte) 0xD0, (byte) 0x7E, (byte) 0x54,
+ (byte) 0xFE, (byte) 0xD0, (byte) 0x7E, (byte) 0x54
+ };
+ private static final SecureRandom sSecureRandom = new SecureRandom();
+
+ private final RapporConfig mConfig;
+
+ // Rappor encoder
+ private final Encoder mEncoder;
+ // True if encoder is secure (seed is securely randomized)
+ private final boolean mIsSecure;
+
+
+ private RapporEncoder(RapporConfig config, boolean secureEncoder, byte[] userSecret) {
+ mConfig = config;
+ mIsSecure = secureEncoder;
+ final Random random;
+ if (secureEncoder) {
+ // Use SecureRandom as random generator.
+ random = sSecureRandom;
+ } else {
+ // Hard-coded random generator, to have deterministic result.
+ random = new Random(INSECURE_RANDOM_SEED);
+ userSecret = INSECURE_SECRET;
+ }
+ mEncoder = new Encoder(random, null, null,
+ userSecret, config.mEncoderId, config.mNumBits,
+ config.mProbabilityF, config.mProbabilityP, config.mProbabilityQ,
+ config.mNumCohorts, config.mNumBloomHashes);
+ }
+
+ /**
+ * Create {@link RapporEncoder} with {@link RapporConfig} and user secret provided.
+ *
+ * @param config Rappor parameters to encode input.
+ * @param userSecret Per device unique secret key.
+ * @return {@link RapporEncoder} instance.
+ */
+ public static RapporEncoder createEncoder(RapporConfig config, byte[] userSecret) {
+ return new RapporEncoder(config, true, userSecret);
+ }
+
+ /**
+ * Create <strong>insecure</strong> {@link RapporEncoder} with {@link RapporConfig} provided.
+ * Should not use it to process sensitive data.
+ *
+ * @param config Rappor parameters to encode input.
+ * @return {@link RapporEncoder} instance.
+ */
+ public static RapporEncoder createInsecureEncoderForTest(RapporConfig config) {
+ return new RapporEncoder(config, false, null);
+ }
+
+ @Override
+ public byte[] encodeString(String original) {
+ return mEncoder.encodeString(original);
+ }
+
+ @Override
+ public byte[] encodeBoolean(boolean original) {
+ return mEncoder.encodeBoolean(original);
+ }
+
+ @Override
+ public byte[] encodeBits(byte[] bits) {
+ return mEncoder.encodeBits(bits);
+ }
+
+ @Override
+ public RapporConfig getConfig() {
+ return mConfig;
+ }
+
+ @Override
+ public boolean isInsecureEncoderForTest() {
+ return !mIsSecure;
+ }
+}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index ec5b1c6..c94da9a 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -4244,19 +4244,39 @@
/**
* The flattened {@link android.content.ComponentName} of a {@link
* android.telecom.PhoneAccountHandle} that is the preferred {@code PhoneAccountHandle} to
- * call the contact with. Used by {@link CommonDataKinds.Phone}.
+ * call the contact with.
*
+ * <p> On a multi-SIM device this field can be used in a {@link CommonDataKinds.Phone} row
+ * to indicate the {@link PhoneAccountHandle} to call the number with, instead of using
+ * {@link android.telecom.TelecomManager#getDefaultOutgoingPhoneAccount(String)} or asking
+ * every time.
+ *
+ * <p>{@link android.telecom.TelecomManager#placeCall(Uri, android.os.Bundle)}
+ * should be called with {@link android.telecom.TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE}
+ * set to the {@link PhoneAccountHandle} using the {@link ComponentName} from this field.
+ *
+ * @see #PREFERRED_PHONE_ACCOUNT_ID
* @see PhoneAccountHandle#getComponentName()
* @see ComponentName#flattenToString()
*/
String PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME = "preferred_phone_account_component_name";
/**
- * The ID of a {@link
+ * The ID of a {@link
* android.telecom.PhoneAccountHandle} that is the preferred {@code PhoneAccountHandle} to
* call the contact with. Used by {@link CommonDataKinds.Phone}.
*
- * @see PhoneAccountHandle#getId() ()
+ * <p> On a multi-SIM device this field can be used in a {@link CommonDataKinds.Phone} row
+ * to indicate the {@link PhoneAccountHandle} to call the number with, instead of using
+ * {@link android.telecom.TelecomManager#getDefaultOutgoingPhoneAccount(String)} or asking
+ * every time.
+ *
+ * <p>{@link android.telecom.TelecomManager#placeCall(Uri, android.os.Bundle)}
+ * should be called with {@link android.telecom.TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE}
+ * set to the {@link PhoneAccountHandle} using the id from this field.
+ *
+ * @see #PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME
+ * @see PhoneAccountHandle#getId()
*/
String PREFERRED_PHONE_ACCOUNT_ID = "preferred_phone_account_id";
}
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index d8540ff..a1d1c57 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -283,7 +283,11 @@
public static final int STATUS_REJECTED = 3;
/** @hide */
- @IntDef({STATUS_OK, STATUS_WRONG_CERTIFICATES, STATUS_UNEXPECTED_DATA_PROVIDED})
+ @IntDef(prefix = { "STATUS_" }, value = {
+ STATUS_OK,
+ STATUS_WRONG_CERTIFICATES,
+ STATUS_UNEXPECTED_DATA_PROVIDED
+ })
@Retention(RetentionPolicy.SOURCE)
@interface FontResultStatus {}
@@ -438,9 +442,13 @@
public static final int FAIL_REASON_MALFORMED_QUERY = Columns.RESULT_CODE_MALFORMED_QUERY;
/** @hide */
- @IntDef({ FAIL_REASON_PROVIDER_NOT_FOUND, FAIL_REASON_FONT_LOAD_ERROR,
- FAIL_REASON_FONT_NOT_FOUND, FAIL_REASON_FONT_UNAVAILABLE,
- FAIL_REASON_MALFORMED_QUERY })
+ @IntDef(prefix = { "FAIL_" }, value = {
+ FAIL_REASON_PROVIDER_NOT_FOUND,
+ FAIL_REASON_FONT_LOAD_ERROR,
+ FAIL_REASON_FONT_NOT_FOUND,
+ FAIL_REASON_FONT_UNAVAILABLE,
+ FAIL_REASON_MALFORMED_QUERY
+ })
@Retention(RetentionPolicy.SOURCE)
@interface FontRequestFailReason {}
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 32d68cd..d9808a3 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -63,15 +63,6 @@
private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/";
- /**
- * Broadcast Action: A broadcast to indicate the end of an MTP session with the host.
- * This broadcast is only sent if MTP activity has modified the media database during the
- * most recent MTP session.
- *
- * @hide
- */
- public static final String ACTION_MTP_SESSION_END = "android.provider.action.MTP_SESSION_END";
-
/**
* The method name used by the media scanner and mtp to tell the media provider to
* rescan and reclassify that have become unhidden because of renaming folders or
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6b1632a..d118219 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1689,7 +1689,7 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({
+ @IntDef(prefix = { "RESET_MODE_" }, value = {
RESET_MODE_PACKAGE_DEFAULTS,
RESET_MODE_UNTRUSTED_DEFAULTS,
RESET_MODE_UNTRUSTED_CHANGES,
@@ -5328,48 +5328,51 @@
public static final String AUTOFILL_SERVICE = "autofill_service";
/**
- * Experimental autofill feature.
+ * Boolean indicating if Autofill supports field classification.
*
- * <p>TODO(b/67867469): document (or remove) once feature is finished
+ * @see android.service.autofill.AutofillService
+ *
* @hide
*/
+ @SystemApi
@TestApi
public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION =
"autofill_field_classification";
/**
- * Experimental autofill feature.
+ * Defines value returned by {@link android.service.autofill.UserData#getMaxUserDataSize()}.
*
- * <p>TODO(b/67867469): document (or remove) once feature is finished
* @hide
*/
+ @SystemApi
public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE =
"autofill_user_data_max_user_data_size";
/**
- * Experimental autofill feature.
+ * Defines value returned by
+ * {@link android.service.autofill.UserData#getMaxFieldClassificationIdsSize()}.
*
- * <p>TODO(b/67867469): document (or remove) once feature is finished
* @hide
*/
+ @SystemApi
public static final String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE =
"autofill_user_data_max_field_classification_size";
/**
- * Experimental autofill feature.
+ * Defines value returned by {@link android.service.autofill.UserData#getMaxValueLength()}.
*
- * <p>TODO(b/67867469): document (or remove) once feature is finished
* @hide
*/
+ @SystemApi
public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH =
"autofill_user_data_max_value_length";
/**
- * Experimental autofill feature.
+ * Defines value returned by {@link android.service.autofill.UserData#getMinValueLength()}.
*
- * <p>TODO(b/67867469): document (or remove) once feature is finished
* @hide
*/
+ @SystemApi
public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH =
"autofill_user_data_min_value_length";
@@ -9463,6 +9466,7 @@
* service_min_restart_time_between (long)
* service_max_inactivity (long)
* service_bg_start_timeout (long)
+ * process_start_async (boolean)
* </pre>
*
* <p>
@@ -9584,9 +9588,10 @@
/**
* App standby (app idle) specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
- *
+ * <p>
* "idle_duration=5000,parole_interval=4500"
- *
+ * <p>
+ * All durations are in millis.
* The following keys are supported:
*
* <pre>
@@ -9736,6 +9741,15 @@
public static final String TEXT_CLASSIFIER_CONSTANTS = "text_classifier_constants";
/**
+ * Whether or not App Standby feature is enabled. This controls throttling of apps
+ * based on usage patterns and predictions.
+ * Type: int (0 for false, 1 for true)
+ * Default: 1
+ * @hide
+ */
+ public static final java.lang.String APP_STANDBY_ENABLED = "app_standby_enabled";
+
+ /**
* Get the key that retrieves a bluetooth headset's priority.
* @hide
*/
@@ -11143,6 +11157,28 @@
*/
public static final String NOTIFICATION_SNOOZE_OPTIONS =
"notification_snooze_options";
+
+ /**
+ * Configuration flags for SQLite Compatibility WAL. Encoded as a key-value list, separated
+ * by commas. E.g.: compatibility_wal_supported=true, wal_syncmode=OFF
+ *
+ * Supported keys:
+ * compatibility_wal_supported (boolean)
+ * wal_syncmode (String)
+ *
+ * @hide
+ */
+ public static final String SQLITE_COMPATIBILITY_WAL_FLAGS =
+ "sqlite_compatibility_wal_flags";
+
+ /**
+ * Enable GNSS Raw Measurements Full Tracking?
+ * 0 = no
+ * 1 = yes
+ * @hide
+ */
+ public static final String ENABLE_GNSS_RAW_MEAS_FULL_TRACKING =
+ "enable_gnss_raw_meas_full_tracking";
}
/**
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index 6be0e76..6a3c55e 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -297,7 +297,7 @@
* {@link ContentResolver#applyBatch}), and if the {@link ContentValues} doesn't contain
* this column, the voicemail provider implicitly sets it to 0 if the calling package is
* the {@link #SOURCE_PACKAGE} or to 1 otherwise. To prevent this behavior, explicitly set
- * {@link #DIRTY_RETAIN} to this column in the {@link ContentValues}.
+ * {@link #DIRTY_RETAIN} to DIRTY in the {@link ContentValues}.
*
* <P>Type: INTEGER (boolean)</P>
*
diff --git a/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl b/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl
new file mode 100644
index 0000000..fe13179
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.security.recoverablekeystore;
+
+/* @hide */
+parcelable KeyDerivationParameters;
diff --git a/core/java/android/security/recoverablekeystore/KeyDerivationParameters.java b/core/java/android/security/recoverablekeystore/KeyDerivationParameters.java
new file mode 100644
index 0000000..2205c41
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/KeyDerivationParameters.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.recoverablekeystore;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Collection of parameters which define a key derivation function.
+ * Supports
+ *
+ * <ul>
+ * <li>SHA256
+ * <li>Argon2id
+ * </ul>
+ * @hide
+ */
+public final class KeyDerivationParameters implements Parcelable {
+ private final int mAlgorithm;
+ private byte[] mSalt;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ALGORITHM_SHA256, ALGORITHM_ARGON2ID})
+ public @interface KeyDerivationAlgorithm {
+ }
+
+ /**
+ * Salted SHA256
+ */
+ public static final int ALGORITHM_SHA256 = 1;
+
+ /**
+ * Argon2ID
+ */
+ // TODO: add Argon2ID support.
+ public static final int ALGORITHM_ARGON2ID = 2;
+
+ /**
+ * Creates instance of the class to to derive key using salted SHA256 hash.
+ */
+ public KeyDerivationParameters createSHA256Parameters(@NonNull byte[] salt) {
+ return new KeyDerivationParameters(ALGORITHM_SHA256, salt);
+ }
+
+ private KeyDerivationParameters(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
+ mAlgorithm = algorithm;
+ mSalt = Preconditions.checkNotNull(salt);
+ }
+
+ /**
+ * Gets algorithm.
+ */
+ public @KeyDerivationAlgorithm int getAlgorithm() {
+ return mAlgorithm;
+ }
+
+ /**
+ * Gets salt.
+ */
+ public @NonNull byte[] getSalt() {
+ return mSalt;
+ }
+
+ public static final Parcelable.Creator<KeyDerivationParameters> CREATOR =
+ new Parcelable.Creator<KeyDerivationParameters>() {
+ public KeyDerivationParameters createFromParcel(Parcel in) {
+ return new KeyDerivationParameters(in);
+ }
+
+ public KeyDerivationParameters[] newArray(int length) {
+ return new KeyDerivationParameters[length];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mAlgorithm);
+ out.writeByteArray(mSalt);
+ }
+
+ protected KeyDerivationParameters(Parcel in) {
+ mAlgorithm = in.readInt();
+ mSalt = in.createByteArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl
new file mode 100644
index 0000000..1674e51
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.security.recoverablekeystore;
+
+/* @hide */
+parcelable KeyEntryRecoveryData;
diff --git a/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java
new file mode 100644
index 0000000..80f5aa7
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java
@@ -0,0 +1,90 @@
+/*
+ * 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.security.recoverablekeystore;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+
+/**
+ * Helper class with data necessary recover a single application key, given a recovery key.
+ *
+ * <ul>
+ * <li>Alias - Keystore alias of the key.
+ * <li>Encrypted key material.
+ * </ul>
+ *
+ * Note that Application info is not included. Recovery Agent can only make its own keys
+ * recoverable.
+ *
+ * @hide
+ */
+public final class KeyEntryRecoveryData implements Parcelable {
+ private final byte[] mAlias;
+ // The only supported format is AES-256 symmetric key.
+ private final byte[] mEncryptedKeyMaterial;
+
+ public KeyEntryRecoveryData(@NonNull byte[] alias, @NonNull byte[] encryptedKeyMaterial) {
+ mAlias = Preconditions.checkNotNull(alias);
+ mEncryptedKeyMaterial = Preconditions.checkNotNull(encryptedKeyMaterial);
+ }
+
+ /**
+ * Application-specific alias of the key.
+ * @see java.security.KeyStore.aliases
+ */
+ public @NonNull byte[] getAlias() {
+ return mAlias;
+ }
+
+ /**
+ * Encrypted key material encrypted by recovery key.
+ */
+ public @NonNull byte[] getEncryptedKeyMaterial() {
+ return mEncryptedKeyMaterial;
+ }
+
+ public static final Parcelable.Creator<KeyEntryRecoveryData> CREATOR =
+ new Parcelable.Creator<KeyEntryRecoveryData>() {
+ public KeyEntryRecoveryData createFromParcel(Parcel in) {
+ return new KeyEntryRecoveryData(in);
+ }
+
+ public KeyEntryRecoveryData[] newArray(int length) {
+ return new KeyEntryRecoveryData[length];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeByteArray(mAlias);
+ out.writeByteArray(mEncryptedKeyMaterial);
+ }
+
+ protected KeyEntryRecoveryData(Parcel in) {
+ mAlias = in.createByteArray();
+ mEncryptedKeyMaterial = in.createByteArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl
new file mode 100644
index 0000000..bd76051
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.security.recoverablekeystore;
+
+/* @hide */
+parcelable KeyStoreRecoveryData;
diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.java b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.java
new file mode 100644
index 0000000..087f7a2
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.java
@@ -0,0 +1,115 @@
+/*
+ * 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.security.recoverablekeystore;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Helper class which returns data necessary to recover keys.
+ * Contains
+ *
+ * <ul>
+ * <li>Snapshot version.
+ * <li>Recovery metadata with UI and key derivation parameters.
+ * <li>List of application keys encrypted by recovery key.
+ * <li>Encrypted recovery key.
+ * </ul>
+ *
+ * @hide
+ */
+public final class KeyStoreRecoveryData implements Parcelable {
+ private final int mSnapshotVersion;
+ private final List<KeyStoreRecoveryMetadata> mRecoveryMetadata;
+ private final List<KeyEntryRecoveryData> mApplicationKeyBlobs;
+ private final byte[] mEncryptedRecoveryKeyBlob;
+
+ public KeyStoreRecoveryData(int snapshotVersion, @NonNull List<KeyStoreRecoveryMetadata>
+ recoveryMetadata, @NonNull List<KeyEntryRecoveryData> applicationKeyBlobs,
+ @NonNull byte[] encryptedRecoveryKeyBlob) {
+ mSnapshotVersion = snapshotVersion;
+ mRecoveryMetadata = Preconditions.checkNotNull(recoveryMetadata);
+ mApplicationKeyBlobs = Preconditions.checkNotNull(applicationKeyBlobs);
+ mEncryptedRecoveryKeyBlob = Preconditions.checkNotNull(encryptedRecoveryKeyBlob);
+ }
+
+ /**
+ * Snapshot version for given account. It is incremented when user secret or list of application
+ * keys changes.
+ */
+ public int getSnapshotVersion() {
+ return mSnapshotVersion;
+ }
+
+ /**
+ * UI and key derivation parameters. Note that combination of secrets may be used.
+ */
+ public @NonNull List<KeyStoreRecoveryMetadata> getRecoveryMetadata() {
+ return mRecoveryMetadata;
+ }
+
+ /**
+ * List of application keys, with key material encrypted by
+ * the recovery key ({@link #getEncryptedRecoveryKeyBlob}).
+ */
+ public @NonNull List<KeyEntryRecoveryData> getApplicationKeyBlobs() {
+ return mApplicationKeyBlobs;
+ }
+
+ /**
+ * Recovery key blob, encrypted by user secret and recovery service public key.
+ */
+ public @NonNull byte[] getEncryptedRecoveryKeyBlob() {
+ return mEncryptedRecoveryKeyBlob;
+ }
+
+ public static final Parcelable.Creator<KeyStoreRecoveryData> CREATOR =
+ new Parcelable.Creator<KeyStoreRecoveryData>() {
+ public KeyStoreRecoveryData createFromParcel(Parcel in) {
+ return new KeyStoreRecoveryData(in);
+ }
+
+ public KeyStoreRecoveryData[] newArray(int length) {
+ return new KeyStoreRecoveryData[length];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mSnapshotVersion);
+ out.writeTypedList(mRecoveryMetadata);
+ out.writeByteArray(mEncryptedRecoveryKeyBlob);
+ out.writeTypedList(mApplicationKeyBlobs);
+ }
+
+ protected KeyStoreRecoveryData(Parcel in) {
+ mSnapshotVersion = in.readInt();
+ mRecoveryMetadata = in.createTypedArrayList(KeyStoreRecoveryMetadata.CREATOR);
+ mEncryptedRecoveryKeyBlob = in.createByteArray();
+ mApplicationKeyBlobs = in.createTypedArrayList(KeyEntryRecoveryData.CREATOR);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.aidl b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.aidl
new file mode 100644
index 0000000..e1d49de
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.security.recoverablekeystore;
+
+/* @hide */
+parcelable KeyStoreRecoveryMetadata;
diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.java b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.java
new file mode 100644
index 0000000..43f9c80
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.java
@@ -0,0 +1,180 @@
+/*
+ * 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.security.recoverablekeystore;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+
+/**
+ * Helper class with data necessary to recover Keystore on a new device.
+ * It defines UI shown to the user and a way to derive a cryptographic key from user output.
+ *
+ * @hide
+ */
+public final class KeyStoreRecoveryMetadata implements Parcelable {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({TYPE_LOCKSCREEN, TYPE_CUSTOM_PASSWORD})
+ public @interface UserSecretType {
+ }
+
+ /**
+ * Lockscreen secret is required to recover KeyStore.
+ */
+ public static final int TYPE_LOCKSCREEN = 1;
+
+ /**
+ * Custom passphrase, unrelated to lock screen, is required to recover KeyStore.
+ */
+ public static final int TYPE_CUSTOM_PASSWORD = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({TYPE_PIN, TYPE_PASSWORD, TYPE_PATTERN})
+ public @interface LockScreenUiFormat {
+ }
+
+ /**
+ * Pin with digits only.
+ */
+ public static final int TYPE_PIN = 1;
+
+ /**
+ * Password. String with latin-1 characters only.
+ */
+ public static final int TYPE_PASSWORD = 2;
+
+ /**
+ * Pattern with 3 by 3 grid.
+ */
+ public static final int TYPE_PATTERN = 3;
+
+ @UserSecretType
+ private final int mUserSecretType;
+
+ @LockScreenUiFormat
+ private final int mLockScreenUiFormat;
+
+ /**
+ * Parameters of key derivation function, including algorithm, difficulty, salt.
+ */
+ private KeyDerivationParameters mKeyDerivationParameters;
+ private byte[] mSecret; // Derived from user secret. The field must have limited visibility.
+
+ /**
+ * @param secret Constructor creates a reference to the secret. Caller must use
+ * @link {#clearSecret} to overwrite its value in memory.
+ */
+ public KeyStoreRecoveryMetadata(@UserSecretType int userSecretType,
+ @LockScreenUiFormat int lockScreenUiFormat,
+ @NonNull KeyDerivationParameters keyDerivationParameters, @NonNull byte[] secret) {
+ mUserSecretType = userSecretType;
+ mLockScreenUiFormat = lockScreenUiFormat;
+ mKeyDerivationParameters = Preconditions.checkNotNull(keyDerivationParameters);
+ mSecret = Preconditions.checkNotNull(secret);
+ }
+
+ /**
+ * Specifies UX shown to user during recovery.
+ *
+ * @see KeyStore.TYPE_PIN
+ * @see KeyStore.TYPE_PASSWORD
+ * @see KeyStore.TYPE_PATTERN
+ */
+ public @LockScreenUiFormat int getLockScreenUiFormat() {
+ return mLockScreenUiFormat;
+ }
+
+ /**
+ * Specifies function used to derive symmetric key from user input
+ * Format is defined in separate util class.
+ */
+ public @NonNull KeyDerivationParameters getKeyDerivationParameters() {
+ return mKeyDerivationParameters;
+ }
+
+ /**
+ * Secret string derived from user input.
+ */
+ public @NonNull byte[] getSecret() {
+ return mSecret;
+ }
+
+ /**
+ * @see KeyStore.TYPE_LOCKSCREEN
+ * @see KeyStore.TYPE_CUSTOM_PASSWORD
+ */
+ public @UserSecretType int getUserSecretType() {
+ return mUserSecretType;
+ }
+
+ /**
+ * Removes secret from memory than object is no longer used.
+ * Since finalizer call is not reliable, please use @link {#clearSecret} directly.
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ clearSecret();
+ super.finalize();
+ }
+
+ /**
+ * Fills mSecret with zeroes.
+ */
+ public void clearSecret() {
+ Arrays.fill(mSecret, (byte) 0);
+ }
+
+ public static final Parcelable.Creator<KeyStoreRecoveryMetadata> CREATOR =
+ new Parcelable.Creator<KeyStoreRecoveryMetadata>() {
+ public KeyStoreRecoveryMetadata createFromParcel(Parcel in) {
+ return new KeyStoreRecoveryMetadata(in);
+ }
+
+ public KeyStoreRecoveryMetadata[] newArray(int length) {
+ return new KeyStoreRecoveryMetadata[length];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mUserSecretType);
+ out.writeInt(mLockScreenUiFormat);
+ out.writeTypedObject(mKeyDerivationParameters, flags);
+ out.writeByteArray(mSecret);
+ }
+
+ protected KeyStoreRecoveryMetadata(Parcel in) {
+ mUserSecretType = in.readInt();
+ mLockScreenUiFormat = in.readInt();
+ mKeyDerivationParameters = in.readTypedObject(KeyDerivationParameters.CREATOR);
+ mSecret = in.createByteArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyGenerator.java b/core/java/android/security/recoverablekeystore/RecoverableKeyGenerator.java
deleted file mode 100644
index 4125f0ba..0000000
--- a/core/java/android/security/recoverablekeystore/RecoverableKeyGenerator.java
+++ /dev/null
@@ -1,133 +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.security.recoverablekeystore;
-
-import android.security.keystore.AndroidKeyStoreSecretKey;
-import android.security.keystore.KeyProperties;
-import android.security.keystore.KeyProtection;
-import android.util.Log;
-
-import java.io.IOException;
-import java.security.InvalidKeyException;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableEntryException;
-
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-import javax.security.auth.DestroyFailedException;
-
-/**
- * Generates keys and stores them both in AndroidKeyStore and on disk, in wrapped form.
- *
- * <p>Generates 256-bit AES keys, which can be used for encrypt / decrypt with AES/GCM/NoPadding.
- * They are synced to disk wrapped by a platform key. This allows them to be exported to a remote
- * service.
- *
- * @hide
- */
-public class RecoverableKeyGenerator {
- private static final String TAG = "RecoverableKeyGenerator";
- private static final String KEY_GENERATOR_ALGORITHM = "AES";
- private static final int KEY_SIZE_BITS = 256;
-
- /**
- * A new {@link RecoverableKeyGenerator} instance.
- *
- * @param platformKey Secret key used to wrap generated keys before persisting to disk.
- * @param recoverableKeyStorage Class that manages persisting wrapped keys to disk.
- * @throws NoSuchAlgorithmException if "AES" key generation or "AES/GCM/NoPadding" cipher is
- * unavailable. Should never happen.
- *
- * @hide
- */
- public static RecoverableKeyGenerator newInstance(
- AndroidKeyStoreSecretKey platformKey, RecoverableKeyStorage recoverableKeyStorage)
- throws NoSuchAlgorithmException {
- // NB: This cannot use AndroidKeyStore as the provider, as we need access to the raw key
- // material, so that it can be synced to disk in encrypted form.
- KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_GENERATOR_ALGORITHM);
- return new RecoverableKeyGenerator(keyGenerator, platformKey, recoverableKeyStorage);
- }
-
- private final KeyGenerator mKeyGenerator;
- private final RecoverableKeyStorage mRecoverableKeyStorage;
- private final AndroidKeyStoreSecretKey mPlatformKey;
-
- private RecoverableKeyGenerator(
- KeyGenerator keyGenerator,
- AndroidKeyStoreSecretKey platformKey,
- RecoverableKeyStorage recoverableKeyStorage) {
- mKeyGenerator = keyGenerator;
- mRecoverableKeyStorage = recoverableKeyStorage;
- mPlatformKey = platformKey;
- }
-
- /**
- * Generates a 256-bit AES key with the given alias.
- *
- * <p>Stores in the AndroidKeyStore, as well as persisting in wrapped form to disk. It is
- * persisted to disk so that it can be synced remotely, and then recovered on another device.
- * The generated key allows encrypt/decrypt only using AES/GCM/NoPadding.
- *
- * <p>The key handle returned to the caller is a reference to the AndroidKeyStore key,
- * meaning that the caller is never able to access the raw, unencrypted key.
- *
- * @param alias The alias by which the key will be known in AndroidKeyStore.
- * @throws InvalidKeyException if the platform key cannot be used to wrap keys.
- * @throws IOException if there was an issue writing the wrapped key to the wrapped key store.
- * @throws UnrecoverableEntryException if could not retrieve key after putting it in
- * AndroidKeyStore. This should not happen.
- * @return A handle to the AndroidKeyStore key.
- *
- * @hide
- */
- public SecretKey generateAndStoreKey(String alias) throws KeyStoreException,
- InvalidKeyException, IOException, UnrecoverableEntryException {
- mKeyGenerator.init(KEY_SIZE_BITS);
- SecretKey key = mKeyGenerator.generateKey();
-
- mRecoverableKeyStorage.importIntoAndroidKeyStore(
- alias,
- key,
- new KeyProtection.Builder(
- KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
- .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
- .build());
- WrappedKey wrappedKey = WrappedKey.fromSecretKey(mPlatformKey, key);
-
- try {
- // Keep raw key material in memory for minimum possible time.
- key.destroy();
- } catch (DestroyFailedException e) {
- Log.w(TAG, "Could not destroy SecretKey.");
- }
-
- mRecoverableKeyStorage.persistToDisk(alias, wrappedKey);
-
- try {
- // Reload from the keystore, so that the caller is only provided with the handle of the
- // key, not the raw key material.
- return mRecoverableKeyStorage.loadFromAndroidKeyStore(alias);
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(
- "Impossible: NoSuchAlgorithmException when attempting to retrieve a key "
- + "that has only just been stored in AndroidKeyStore.", e);
- }
- }
-}
diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyStorage.java b/core/java/android/security/recoverablekeystore/RecoverableKeyStorage.java
deleted file mode 100644
index c239e00..0000000
--- a/core/java/android/security/recoverablekeystore/RecoverableKeyStorage.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.recoverablekeystore;
-
-import android.security.keystore.KeyProtection;
-
-import java.io.IOException;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableEntryException;
-
-import javax.crypto.SecretKey;
-
-/**
- * Stores wrapped keys to disk, so they can be synced on the next screen unlock event.
- *
- * @hide
- */
-public interface RecoverableKeyStorage {
-
- /**
- * Writes {@code wrappedKey} to disk, keyed by the application's uid and the {@code alias}.
- *
- * @throws IOException if an error occurred writing to disk.
- *
- * @hide
- */
- void persistToDisk(String alias, WrappedKey wrappedKey) throws IOException;
-
- /**
- * Imports {@code key} into AndroidKeyStore, keyed by the application's uid and
- * the {@code alias}.
- *
- * @param alias The alias of the key.
- * @param key The key.
- * @param keyProtection Protection params denoting what the key can be used for. (e.g., what
- * Cipher modes, whether for encrpyt/decrypt or signing, etc.)
- * @throws KeyStoreException if an error occurred loading the key into the AndroidKeyStore.
- *
- * @hide
- */
- void importIntoAndroidKeyStore(String alias, SecretKey key, KeyProtection keyProtection) throws
- KeyStoreException;
-
- /**
- * Loads a key handle from AndroidKeyStore.
- *
- * @param alias Alias of the key to load.
- * @return The key handle.
- * @throws KeyStoreException if an error occurred loading the key from AndroidKeyStore.
- *
- * @hide
- */
- SecretKey loadFromAndroidKeyStore(String alias) throws KeyStoreException,
- NoSuchAlgorithmException,
- UnrecoverableEntryException;
-}
diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyStorageImpl.java b/core/java/android/security/recoverablekeystore/RecoverableKeyStorageImpl.java
deleted file mode 100644
index b9926dd..0000000
--- a/core/java/android/security/recoverablekeystore/RecoverableKeyStorageImpl.java
+++ /dev/null
@@ -1,110 +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.security.recoverablekeystore;
-
-import android.security.keystore.KeyProtection;
-
-import java.io.IOException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableEntryException;
-import java.security.cert.CertificateException;
-
-import javax.crypto.SecretKey;
-
-/**
- * Implementation of {@link RecoverableKeyStorage}.
- *
- * <p>Persists wrapped keys to disk, and loads raw keys into AndroidKeyStore.
- *
- * @hide
- */
-public class RecoverableKeyStorageImpl implements RecoverableKeyStorage {
- private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
-
- private final KeyStore mKeyStore;
-
- /**
- * A new instance.
- *
- * @throws KeyStoreException if unable to load AndroidKeyStore.
- *
- * @hide
- */
- public static RecoverableKeyStorageImpl newInstance() 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 new RecoverableKeyStorageImpl(keyStore);
- }
-
- private RecoverableKeyStorageImpl(KeyStore keyStore) {
- mKeyStore = keyStore;
- }
-
- /**
- * Writes {@code wrappedKey} to disk, keyed by the application's uid and the {@code alias}.
- *
- * @throws IOException if an error occurred writing to disk.
- *
- * @hide
- */
- @Override
- public void persistToDisk(String alias, WrappedKey wrappedKey) throws IOException {
- // TODO(robertberry) Add implementation.
- throw new UnsupportedOperationException();
- }
-
- /**
- * Imports {@code key} into AndroidKeyStore, keyed by the application's uid and the
- * {@code alias}.
- *
- * @param alias The alias of the key.
- * @param key The key.
- * @param keyProtection Protection params denoting what the key can be used for. (e.g., what
- * Cipher modes, whether for encrpyt/decrypt or signing, etc.)
- * @throws KeyStoreException if an error occurred loading the key into the AndroidKeyStore.
- *
- * @hide
- */
- @Override
- public void importIntoAndroidKeyStore(String alias, SecretKey key, KeyProtection keyProtection)
- throws KeyStoreException {
- mKeyStore.setEntry(alias, new KeyStore.SecretKeyEntry(key), keyProtection);
- }
-
- /**
- * Loads a key handle from AndroidKeyStore.
- *
- * @param alias Alias of the key to load.
- * @return The key handle.
- * @throws KeyStoreException if an error occurred loading the key from AndroidKeyStore.
- *
- * @hide
- */
- @Override
- public SecretKey loadFromAndroidKeyStore(String alias)
- throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException {
- return ((KeyStore.SecretKeyEntry) mKeyStore.getEntry(alias, /*protParam=*/ null))
- .getSecretKey();
- }
-}
diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java b/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java
new file mode 100644
index 0000000..f2f225d
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java
@@ -0,0 +1,353 @@
+/*
+ * 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.security.recoverablekeystore;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+import android.os.UserHandle;
+import android.security.KeyStore;
+import android.util.AndroidException;
+
+import com.android.internal.widget.ILockSettings;
+
+import java.util.List;
+
+/**
+ * A wrapper around KeyStore which lets key be exported to trusted hardware on server side and
+ * recovered later.
+ *
+ * @hide
+ */
+public class RecoverableKeyStoreLoader {
+
+ public static final String PERMISSION_RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE";
+
+ public static final int NO_ERROR = KeyStore.NO_ERROR;
+ public static final int SYSTEM_ERROR = KeyStore.SYSTEM_ERROR;
+ public static final int UNINITIALIZED_RECOVERY_PUBLIC_KEY = 20;
+ // Too many updates to recovery public key or server parameters.
+ public static final int RATE_LIMIT_EXCEEDED = 21;
+
+ private final ILockSettings mBinder;
+
+ private RecoverableKeyStoreLoader(ILockSettings binder) {
+ mBinder = binder;
+ }
+
+ /** @hide */
+ public static RecoverableKeyStoreLoader getInstance() {
+ ILockSettings lockSettings =
+ ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
+ return new RecoverableKeyStoreLoader(lockSettings);
+ }
+
+ /**
+ * Exceptions returned by {@link RecoverableKeyStoreLoader}.
+ *
+ * @hide
+ */
+ public static class RecoverableKeyStoreLoaderException extends AndroidException {
+ private int mErrorCode;
+
+ /**
+ * Creates new {@link #RecoverableKeyStoreLoaderException} instance from the error code.
+ *
+ * @param errorCode
+ * @hide
+ */
+ public static RecoverableKeyStoreLoaderException fromErrorCode(int errorCode) {
+ return new RecoverableKeyStoreLoaderException(
+ errorCode, getMessageFromErrorCode(errorCode));
+ }
+
+ /**
+ * Creates new {@link #RecoverableKeyStoreLoaderException} from {@link
+ * ServiceSpecificException}.
+ *
+ * @param e exception thrown on service side.
+ * @hide
+ */
+ static RecoverableKeyStoreLoaderException fromServiceSpecificException(
+ ServiceSpecificException e) throws RecoverableKeyStoreLoaderException {
+ throw RecoverableKeyStoreLoaderException.fromErrorCode(e.errorCode);
+ }
+
+ private RecoverableKeyStoreLoaderException(int errorCode, String message) {
+ super(message);
+ }
+
+ /** Returns errorCode. */
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+
+ /** @hide */
+ private static String getMessageFromErrorCode(int errorCode) {
+ switch (errorCode) {
+ case NO_ERROR:
+ return "OK";
+ case SYSTEM_ERROR:
+ return "System error";
+ case UNINITIALIZED_RECOVERY_PUBLIC_KEY:
+ return "Recovery service is not initialized";
+ case RATE_LIMIT_EXCEEDED:
+ return "Rate limit exceeded";
+ default:
+ return String.valueOf("Unknown error code " + errorCode);
+ }
+ }
+ }
+
+ /**
+ * Initializes key recovery service for the calling application. RecoverableKeyStoreLoader
+ * randomly chooses one of the keys from the list and keeps it to use for future key export
+ * operations. Collection of all keys in the list must be signed by the provided {@code
+ * rootCertificateAlias}, which must also be present in the list of root certificates
+ * preinstalled on the device. The random selection allows RecoverableKeyStoreLoader to select
+ * which of a set of remote recovery service devices will be used.
+ *
+ * <p>In addition, RecoverableKeyStoreLoader enforces a delay of three months between
+ * consecutive initialization attempts, to limit the ability of an attacker to often switch
+ * remote recovery devices and significantly increase number of recovery attempts.
+ *
+ * @param rootCertificateAlias alias of a root certificate preinstalled on the device
+ * @param signedPublicKeyList binary blob a list of X509 certificates and signature
+ * @throws RecoverableKeyStoreLoaderException if signature is invalid, or key rotation was rate
+ * limited.
+ * @hide
+ */
+ public void initRecoveryService(
+ @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList)
+ throws RecoverableKeyStoreLoaderException {
+ try {
+ mBinder.initRecoveryService(
+ rootCertificateAlias, signedPublicKeyList, UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Returns data necessary to store all recoverable keys for given account. Key material is
+ * encrypted with user secret and recovery public key.
+ *
+ * @param account specific to Recovery agent.
+ * @return Data necessary to recover keystore.
+ * @hide
+ */
+ public KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account)
+ throws RecoverableKeyStoreLoaderException {
+ try {
+ KeyStoreRecoveryData recoveryData =
+ mBinder.getRecoveryData(account, UserHandle.getCallingUserId());
+ return recoveryData;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Server parameters used to generate new recovery key blobs. This value will be included in
+ * {@code KeyStoreRecoveryData.getEncryptedRecoveryKeyBlob()}. The same value must be included
+ * in vaultParams {@link startRecoverySession}
+ *
+ * @param serverParameters included in recovery key blob.
+ * @see #getRecoveryData
+ * @throws RecoverableKeyStoreLoaderException If parameters rotation is rate limited.
+ * @hide
+ */
+ public void setServerParameters(long serverParameters)
+ throws RecoverableKeyStoreLoaderException {
+ try {
+ mBinder.setServerParameters(serverParameters, UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Updates recovery status for given keys. It is used to notify keystore that key was
+ * successfully stored on the server or there were an error. Returned as a part of KeyInfo data
+ * structure.
+ *
+ * @param packageName Application whose recoverable keys' statuses are to be updated.
+ * @param aliases List of application-specific key aliases. If the array is empty, updates the
+ * status for all existing recoverable keys.
+ * @param status Status specific to recovery agent.
+ */
+ public void setRecoveryStatus(
+ @NonNull String packageName, @Nullable String[] aliases, int status)
+ throws NameNotFoundException, RecoverableKeyStoreLoaderException {
+ try {
+ mBinder.setRecoveryStatus(packageName, aliases, status, UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Specifies a set of secret types used for end-to-end keystore encryption. Knowing all of them
+ * is necessary to recover data.
+ *
+ * @param secretTypes {@link KeyStoreRecoveryMetadata#TYPE_LOCKSCREEN} or {@link
+ * KeyStoreRecoveryMetadata#TYPE_CUSTOM_PASSWORD}
+ */
+ public void setRecoverySecretTypes(
+ @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] secretTypes)
+ throws RecoverableKeyStoreLoaderException {
+ try {
+ mBinder.setRecoverySecretTypes(secretTypes, UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Defines a set of secret types used for end-to-end keystore encryption. Knowing all of them is
+ * necessary to generate KeyStoreRecoveryData.
+ *
+ * @return list of recovery secret types
+ * @see KeyStoreRecoveryData
+ */
+ public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getRecoverySecretTypes()
+ throws RecoverableKeyStoreLoaderException {
+ try {
+ return mBinder.getRecoverySecretTypes(UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Returns a list of recovery secret types, necessary to create a pending recovery snapshot.
+ * When user enters a secret of a pending type {@link #recoverySecretAvailable} should be
+ * called.
+ *
+ * @return list of recovery secret types
+ */
+ public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getPendingRecoverySecretTypes()
+ throws RecoverableKeyStoreLoaderException {
+ try {
+ return mBinder.getPendingRecoverySecretTypes(UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Method notifies KeyStore that a user-generated secret is available. This method generates a
+ * symmetric session key which a trusted remote device can use to return a recovery key. Caller
+ * should use {@link KeyStoreRecoveryMetadata#clearSecret} to override the secret value in
+ * memory.
+ *
+ * @param recoverySecret user generated secret together with parameters necessary to regenerate
+ * it on a new device.
+ */
+ public void recoverySecretAvailable(@NonNull KeyStoreRecoveryMetadata recoverySecret)
+ throws RecoverableKeyStoreLoaderException {
+ try {
+ mBinder.recoverySecretAvailable(recoverySecret, UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Initializes recovery session and returns a blob with proof of recovery secrets possession.
+ * The method generates symmetric key for a session, which trusted remote device can use to
+ * return recovery key.
+ *
+ * @param sessionId ID for recovery session.
+ * @param verifierPublicKey Certificate with Public key used to create the recovery blob on the
+ * source device. Keystore will verify the certificate using root of trust.
+ * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
+ * Used to limit number of guesses.
+ * @param vaultChallenge Data passed from server for this recovery session and used to prevent
+ * replay attacks
+ * @param secrets Secrets provided by user, the method only uses type and secret fields.
+ * @return Binary blob with recovery claim. It is encrypted with verifierPublicKey and contains
+ * a proof of user secrets, session symmetric key and parameters necessary to identify the
+ * counter with the number of failed recovery attempts.
+ */
+ public @NonNull byte[] startRecoverySession(
+ @NonNull String sessionId,
+ @NonNull byte[] verifierPublicKey,
+ @NonNull byte[] vaultParams,
+ @NonNull byte[] vaultChallenge,
+ @NonNull List<KeyStoreRecoveryMetadata> secrets)
+ throws RecoverableKeyStoreLoaderException {
+ try {
+ byte[] recoveryClaim =
+ mBinder.startRecoverySession(
+ sessionId,
+ verifierPublicKey,
+ vaultParams,
+ vaultChallenge,
+ secrets,
+ UserHandle.getCallingUserId());
+ return recoveryClaim;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
+ }
+
+ /**
+ * Imports keys.
+ *
+ * @param sessionId Id for recovery session, same as in = {@link startRecoverySession}.
+ * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
+ * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
+ * and session. KeyStore only uses package names from the application info in {@link
+ * KeyEntryRecoveryData}. Caller is responsibility to perform certificates check.
+ */
+ public void recoverKeys(
+ @NonNull String sessionId,
+ @NonNull byte[] recoveryKeyBlob,
+ @NonNull List<KeyEntryRecoveryData> applicationKeys)
+ throws RecoverableKeyStoreLoaderException {
+ try {
+ mBinder.recoverKeys(
+ sessionId, recoveryKeyBlob, applicationKeys, UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
+ }
+}
diff --git a/core/java/android/security/recoverablekeystore/WrappedKey.java b/core/java/android/security/recoverablekeystore/WrappedKey.java
deleted file mode 100644
index 51665ae..0000000
--- a/core/java/android/security/recoverablekeystore/WrappedKey.java
+++ /dev/null
@@ -1,115 +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.security.recoverablekeystore;
-
-import java.security.InvalidKeyException;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-
-/**
- * A {@link javax.crypto.SecretKey} wrapped with AES/GCM/NoPadding.
- *
- * @hide
- */
-public class WrappedKey {
- private static final String KEY_WRAP_CIPHER_ALGORITHM = "AES/GCM/NoPadding";
-
- private final byte[] mNonce;
- private final byte[] mKeyMaterial;
-
- /**
- * Returns a wrapped form of {@code key}, using {@code wrappingKey} to encrypt the key material.
- *
- * @throws InvalidKeyException if {@code wrappingKey} cannot be used to encrypt {@code key}, or
- * if {@code key} does not expose its key material. See
- * {@link android.security.keystore.AndroidKeyStoreKey} for an example of a key that does
- * not expose its key material.
- */
- public static WrappedKey fromSecretKey(
- SecretKey wrappingKey, SecretKey key) throws InvalidKeyException, KeyStoreException {
- if (key.getEncoded() == null) {
- throw new InvalidKeyException(
- "key does not expose encoded material. It cannot be wrapped.");
- }
-
- Cipher cipher;
- try {
- cipher = Cipher.getInstance(KEY_WRAP_CIPHER_ALGORITHM);
- } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
- throw new RuntimeException(
- "Android does not support AES/GCM/NoPadding. This should never happen.");
- }
-
- cipher.init(Cipher.WRAP_MODE, wrappingKey);
- byte[] encryptedKeyMaterial;
- try {
- encryptedKeyMaterial = cipher.wrap(key);
- } catch (IllegalBlockSizeException e) {
- Throwable cause = e.getCause();
- if (cause instanceof KeyStoreException) {
- // If AndroidKeyStore encounters any error here, it throws IllegalBlockSizeException
- // with KeyStoreException as the cause. This is due to there being no better option
- // here, as the Cipher#wrap only checked throws InvalidKeyException or
- // IllegalBlockSizeException. If this is the case, we want to propagate it to the
- // caller, so rethrow the cause.
- throw (KeyStoreException) cause;
- } else {
- throw new RuntimeException(
- "IllegalBlockSizeException should not be thrown by AES/GCM/NoPadding mode.",
- e);
- }
- }
-
- return new WrappedKey(/*mNonce=*/ cipher.getIV(), /*mKeyMaterial=*/ encryptedKeyMaterial);
- }
-
- /**
- * A new instance.
- *
- * @param nonce The nonce with which the key material was encrypted.
- * @param keyMaterial The encrypted bytes of the key material.
- *
- * @hide
- */
- public WrappedKey(byte[] nonce, byte[] keyMaterial) {
- mNonce = nonce;
- mKeyMaterial = keyMaterial;
- }
-
- /**
- * Returns the nonce with which the key material was encrypted.
- *
- * @hide
- */
- public byte[] getNonce() {
- return mNonce;
- }
-
- /**
- * Returns the encrypted key material.
- *
- * @hide
- */
- public byte[] getKeyMaterial() {
- return mKeyMaterial;
- }
-}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 97fdcef..917efa8 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -438,7 +438,7 @@
* AutofillValue password = passwordNode.getAutofillValue().getTextValue().toString();
*
* save(username, password);
- * </pre>
+ * </pre>
*
* <a name="Privacy"></a>
* <h3>Privacy</h3>
@@ -453,13 +453,46 @@
* email address), the service should only use it locally (i.e., in the app's process) for
* heuristics purposes, but it should not be sent to external servers.
*
- * <a name="FieldsClassification"></a>
- * <h3>Metrics and fields classification</h3
+ * <a name="FieldClassification"></a>
+ * <h3>Metrics and field classification</h3
*
- * <p>TODO(b/67867469): document it or remove this section; in particular, document the relationship
- * between set/getUserData(), FillResponse.setFieldClassificationIds(), and
- * FillEventHistory.getFieldsClassification.
+ * <p>The service can call {@link #getFillEventHistory()} to get metrics representing the user
+ * actions, and then use these metrics to improve its heuristics.
+ *
+ * <p>Prior to Android {@link android.os.Build.VERSION_CODES#P}, the metrics covered just the
+ * scenarios where the service knew how to autofill an activity, but Android
+ * {@link android.os.Build.VERSION_CODES#P} introduced a new mechanism called field classification,
+ * which allows the service to dinamically classify the meaning of fields based on the existing user
+ * data known by the service.
+ *
+ * <p>Typically, field classification can be used to detect fields that can be autofilled with
+ * user data that is not associated with a specific app—such as email and physical
+ * address. Once the service identifies that a such field was manually filled by the user, the
+ * service could use this signal to improve its heuristics, either locally (i.e., in the same
+ * device) or globally (i.e., by crowdsourcing the results back to the service's server so it can
+ * be used by other users).
+ *
+ * <p>The field classification workflow involves 4 steps:
+ *
+ * <ol>
+ * <li>Set the user data through {@link AutofillManager#setUserData(UserData)}. This data is
+ * cached until the system restarts (or the service is disabled), so it doesn't need to be set for
+ * all requests.
+ * <li>Identify which fields should be analysed by calling
+ * {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)}.
+ * <li>Verify the results through {@link FillEventHistory.Event#getFieldsClassification()}.
+ * <li>Use the results to dynamically create {@link Dataset} or {@link SaveInfo} objects in future
+ * requests.
+ * </ol>
+ *
+ * <p>The field classification is an expensive operation and should be used carefully, otherwise it
+ * can reach its rate limit and get blocked by the Android System. Ideally, it should be used just
+ * in cases where the service could not determine how an activity can be autofilled, but it has a
+ * 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).
*/
+// TODO(b/70407264): add code snippets above???
public abstract class AutofillService extends Service {
private static final String TAG = "AutofillService";
diff --git a/core/java/android/service/autofill/EditDistanceScorer.java b/core/java/android/service/autofill/EditDistanceScorer.java
index e25cd04..0706b37 100644
--- a/core/java/android/service/autofill/EditDistanceScorer.java
+++ b/core/java/android/service/autofill/EditDistanceScorer.java
@@ -16,7 +16,6 @@
package android.service.autofill;
import android.annotation.NonNull;
-import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.autofill.AutofillValue;
@@ -24,14 +23,8 @@
/**
* Helper used to calculate the classification score between an actual {@link AutofillValue} filled
* by the user and the expected value predicted by an autofill service.
- *
- * TODO(b/67867469):
- * - improve javadoc
- * - document algorithm / copy from InternalScorer
- * - unhide / remove testApi
- * @hide
*/
-@TestApi
+// TODO(b/70291841): explain algorithm once it's fully implemented
public final class EditDistanceScorer extends InternalScorer implements Scorer, Parcelable {
private static final EditDistanceScorer sInstance = new EditDistanceScorer();
@@ -46,10 +39,11 @@
private EditDistanceScorer() {
}
+ /** @hide */
@Override
public float getScore(@NonNull AutofillValue actualValue, @NonNull String userData) {
if (actualValue == null || !actualValue.isText() || userData == null) return 0;
- // TODO(b/67867469): implement edit distance - currently it's returning either 0, 100%, or
+ // TODO(b/70291841): implement edit distance - currently it's returning either 0, 100%, or
// partial match when number of chars match
final String textValue = actualValue.getTextValue().toString();
final int total = textValue.length();
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
index 0a60208..001b291 100644
--- a/core/java/android/service/autofill/FieldClassification.java
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -19,38 +19,40 @@
import static android.view.autofill.Helper.sDebug;
import android.annotation.NonNull;
-import android.annotation.TestApi;
import android.os.Parcel;
-import android.os.Parcelable;
import android.view.autofill.Helper;
import com.android.internal.util.Preconditions;
-import com.google.android.collect.Lists;
-
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
/**
- * Gets the <a href="#FieldsClassification">fields classification</a> results for a given field.
- *
- * TODO(b/67867469):
- * - improve javadoc
- * - unhide / remove testApi
- *
- * @hide
+ * Represents the <a href="AutofillService.html#FieldClassification">field classification</a>
+ * results for a given field.
*/
-@TestApi
-public final class FieldClassification implements Parcelable {
+public final class FieldClassification {
- private final Match mMatch;
+ private final ArrayList<Match> mMatches;
/** @hide */
- public FieldClassification(@NonNull Match match) {
- mMatch = Preconditions.checkNotNull(match);
+ public FieldClassification(@NonNull ArrayList<Match> matches) {
+ mMatches = Preconditions.checkNotNull(matches);
+ Collections.sort(mMatches, new Comparator<Match>() {
+ @Override
+ public int compare(Match o1, Match o2) {
+ if (o1.mScore > o2.mScore) return -1;
+ if (o1.mScore < o2.mScore) return 1;
+ return 0;
+ }}
+ );
}
/**
- * Gets the {@link Match matches} with the highest {@link Match#getScore() scores}.
+ * Gets the {@link Match matches} with the highest {@link Match#getScore() scores} (sorted in
+ * descending order).
*
* <p><b>Note:</b> There's no guarantee of how many matches will be returned. In fact,
* the Android System might return just the top match to minimize the impact of field
@@ -58,55 +60,56 @@
*/
@NonNull
public List<Match> getMatches() {
- return Lists.newArrayList(mMatch);
+ return mMatches;
}
@Override
public String toString() {
if (!sDebug) return super.toString();
- return "FieldClassification: " + mMatch;
+ return "FieldClassification: " + mMatches;
}
- /////////////////////////////////////
- // Parcelable "contract" methods. //
- /////////////////////////////////////
-
- @Override
- public int describeContents() {
- return 0;
+ private void writeToParcel(Parcel parcel) {
+ parcel.writeInt(mMatches.size());
+ for (int i = 0; i < mMatches.size(); i++) {
+ mMatches.get(i).writeToParcel(parcel);
+ }
}
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeParcelable(mMatch, flags);
- }
-
- public static final Parcelable.Creator<FieldClassification> CREATOR =
- new Parcelable.Creator<FieldClassification>() {
-
- @Override
- public FieldClassification createFromParcel(Parcel parcel) {
- return new FieldClassification(parcel.readParcelable(null));
+ private static FieldClassification readFromParcel(Parcel parcel) {
+ final int size = parcel.readInt();
+ final ArrayList<Match> matches = new ArrayList<>();
+ for (int i = 0; i < size; i++) {
+ matches.add(i, Match.readFromParcel(parcel));
}
- @Override
- public FieldClassification[] newArray(int size) {
- return new FieldClassification[size];
+ return new FieldClassification(matches);
+ }
+
+ static FieldClassification[] readArrayFromParcel(Parcel parcel) {
+ final int length = parcel.readInt();
+ final FieldClassification[] fcs = new FieldClassification[length];
+ for (int i = 0; i < length; i++) {
+ fcs[i] = readFromParcel(parcel);
}
- };
+ return fcs;
+ }
+
+ static void writeArrayToParcel(@NonNull Parcel parcel, @NonNull FieldClassification[] fcs) {
+ parcel.writeInt(fcs.length);
+ for (int i = 0; i < fcs.length; i++) {
+ fcs[i].writeToParcel(parcel);
+ }
+ }
/**
- * Gets the score of a {@link UserData} entry for the field.
+ * Represents the score of a {@link UserData} entry for the field.
*
- * TODO(b/67867469):
- * - improve javadoc
- * - unhide / remove testApi
- *
- * @hide
+ * <p>The score is defined by {@link #getScore()} and the entry is identified by
+ * {@link #getRemoteId()}.
*/
- @TestApi
- public static final class Match implements Parcelable {
+ public static final class Match {
private final String mRemoteId;
private final float mScore;
@@ -126,26 +129,19 @@
}
/**
- * Gets a score between the value of this field and the value of the {@link UserData} entry.
+ * Gets a classification score for the value of this field compared to the value of the
+ * {@link UserData} entry.
*
- * <p>The score is based in a case-insensitive comparisson of all characters from both the
- * field value and the user data entry, and it ranges from {@code 0} to {@code 1000000}:
+ * <p>The score is based in a comparison of the field value and the user data entry, and it
+ * ranges from {@code 0.0F} to {@code 1.0F}:
* <ul>
- * <li>{@code 1.0} represents a full match ({@code 100%}).
- * <li>{@code 0.0} represents a full mismatch ({@code 0%}).
+ * <li>{@code 1.0F} represents a full match ({@code 100%}).
+ * <li>{@code 0.0F} represents a full mismatch ({@code 0%}).
* <li>Any other value is a partial match.
* </ul>
*
- * <p>How the score is calculated depends on the algorithm used by the Android System.
- * For example, if the user data is {@code "abc"} and the field value us {@code " abc"},
- * the result could be:
- * <ul>
- * <li>{@code 1.0} if the algorithm trims the values.
- * <li>{@code 0.0} if the algorithm compares the values sequentially.
- * <li>{@code 0.75} if the algorithm consideres that 3/4 (75%) of the characters match.
- * </ul>
- *
- * <p>Currently, the autofill service cannot configure the algorithm.
+ * <p>How the score is calculated depends on the algorithm used by the {@link Scorer}
+ * implementation.
*/
public float getScore() {
return mScore;
@@ -160,33 +156,13 @@
return string.append(", score=").append(mScore).toString();
}
- /////////////////////////////////////
- // Parcelable "contract" methods. //
- /////////////////////////////////////
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
+ private void writeToParcel(@NonNull Parcel parcel) {
parcel.writeString(mRemoteId);
parcel.writeFloat(mScore);
}
- @SuppressWarnings("hiding")
- public static final Parcelable.Creator<Match> CREATOR = new Parcelable.Creator<Match>() {
-
- @Override
- public Match createFromParcel(Parcel parcel) {
- return new Match(parcel.readString(), parcel.readFloat());
- }
-
- @Override
- public Match[] newArray(int size) {
- return new Match[size];
- }
- };
+ private static Match readFromParcel(@NonNull Parcel parcel) {
+ return new Match(parcel.readString(), parcel.readFloat());
+ }
}
}
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index facad2d..df62446 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -21,12 +21,10 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
import android.content.IntentSender;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.service.autofill.FieldClassification.Match;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -158,7 +156,8 @@
final AutofillId[] detectedFields = event.mDetectedFieldIds;
parcel.writeParcelableArray(detectedFields, flags);
if (detectedFields != null) {
- parcel.writeParcelableArray(event.mDetectedMatches, flags);
+ FieldClassification.writeArrayToParcel(parcel,
+ event.mDetectedFieldClassifications);
}
}
}
@@ -208,6 +207,7 @@
* ({@link #getIgnoredDatasetIds()}).
* <li>Which fields in the selected datasets were changed by the user after the dataset
* was selected ({@link #getChangedFields()}.
+ * <li>Which fields match the {@link UserData} set by the service.
* </ul>
*
* <p><b>Note: </b>This event is only generated when:
@@ -222,16 +222,16 @@
* <p>See {@link android.view.autofill.AutofillManager} for more information about autofill
* contexts.
*/
- // TODO(b/67867469): update with field detection behavior
public static final int TYPE_CONTEXT_COMMITTED = 4;
/** @hide */
- @IntDef(
- value = {TYPE_DATASET_SELECTED,
- TYPE_DATASET_AUTHENTICATION_SELECTED,
- TYPE_AUTHENTICATION_SELECTED,
- TYPE_SAVE_SHOWN,
- TYPE_CONTEXT_COMMITTED})
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_DATASET_SELECTED,
+ TYPE_DATASET_AUTHENTICATION_SELECTED,
+ TYPE_AUTHENTICATION_SELECTED,
+ TYPE_SAVE_SHOWN,
+ TYPE_CONTEXT_COMMITTED
+ })
@Retention(RetentionPolicy.SOURCE)
@interface EventIds{}
@@ -251,7 +251,7 @@
@Nullable private final ArrayList<ArrayList<String>> mManuallyFilledDatasetIds;
@Nullable private final AutofillId[] mDetectedFieldIds;
- @Nullable private final Match[] mDetectedMatches;
+ @Nullable private final FieldClassification[] mDetectedFieldClassifications;
/**
* Returns the type of the event.
@@ -355,19 +355,13 @@
}
/**
- * Gets the <a href="#FieldsClassification">fields classification</a> results.
+ * Gets the <a href="AutofillService.html#FieldClassification">field classification</a>
+ * results.
*
* <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}, when the
* service requested {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)
- * fields classification}.
- *
- * TODO(b/67867469):
- * - improve javadoc
- * - unhide / remove testApi
- *
- * @hide
+ * field classification}.
*/
- @TestApi
@NonNull public Map<AutofillId, FieldClassification> getFieldsClassification() {
if (mDetectedFieldIds == null) {
return Collections.emptyMap();
@@ -376,11 +370,11 @@
final ArrayMap<AutofillId, FieldClassification> map = new ArrayMap<>(size);
for (int i = 0; i < size; i++) {
final AutofillId id = mDetectedFieldIds[i];
- final Match match = mDetectedMatches[i];
+ final FieldClassification fc = mDetectedFieldClassifications[i];
if (sVerbose) {
- Log.v(TAG, "getFieldsClassification[" + i + "]: id=" + id + ", match=" + match);
+ Log.v(TAG, "getFieldsClassification[" + i + "]: id=" + id + ", fc=" + fc);
}
- map.put(id, new FieldClassification(match));
+ map.put(id, fc);
}
return map;
}
@@ -461,6 +455,8 @@
* and belonged to datasets.
* @param manuallyFilledDatasetIds The ids of datasets that had values matching the
* respective entry on {@code manuallyFilledFieldIds}.
+ * @param detectedFieldClassifications the field classification matches.
+ *
* @throws IllegalArgumentException If the length of {@code changedFieldIds} and
* {@code changedDatasetIds} doesn't match.
* @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and
@@ -468,7 +464,6 @@
*
* @hide
*/
- // TODO(b/67867469): document field classification parameters once stable
public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState,
@Nullable List<String> selectedDatasetIds,
@Nullable ArraySet<String> ignoredDatasetIds,
@@ -476,7 +471,8 @@
@Nullable ArrayList<String> changedDatasetIds,
@Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
@Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
- @Nullable AutofillId[] detectedFieldIds, @Nullable Match[] detectedMaches) {
+ @Nullable AutofillId[] detectedFieldIds,
+ @Nullable FieldClassification[] detectedFieldClassifications) {
mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_CONTEXT_COMMITTED,
"eventType");
mDatasetId = datasetId;
@@ -501,7 +497,7 @@
mManuallyFilledDatasetIds = manuallyFilledDatasetIds;
mDetectedFieldIds = detectedFieldIds;
- mDetectedMatches = detectedMaches;
+ mDetectedFieldClassifications = detectedFieldClassifications;
}
@Override
@@ -515,7 +511,8 @@
+ ", manuallyFilledFieldIds=" + mManuallyFilledFieldIds
+ ", manuallyFilledDatasetIds=" + mManuallyFilledDatasetIds
+ ", detectedFieldIds=" + Arrays.toString(mDetectedFieldIds)
- + ", detectedMaches =" + Arrays.toString(mDetectedMatches)
+ + ", detectedFieldClassifications ="
+ + Arrays.toString(mDetectedFieldClassifications)
+ "]";
}
}
@@ -553,15 +550,16 @@
}
final AutofillId[] detectedFieldIds = parcel.readParcelableArray(null,
AutofillId.class);
- final Match[] detectedMatches = (detectedFieldIds != null)
- ? parcel.readParcelableArray(null, Match.class)
+ final FieldClassification[] detectedFieldClassifications =
+ (detectedFieldIds != null)
+ ? FieldClassification.readArrayFromParcel(parcel)
: null;
selection.addEvent(new Event(eventType, datasetId, clientState,
selectedDatasetIds, ignoredDatasets,
changedFieldIds, changedDatasetIds,
manuallyFilledFieldIds, manuallyFilledDatasetIds,
- detectedFieldIds, detectedMatches));
+ detectedFieldIds, detectedFieldClassifications));
}
return selection;
}
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index ddc92f6..33619ac 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -69,9 +69,9 @@
public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE;
/** @hide */
- @IntDef(
- flag = true,
- value = {FLAG_MANUAL_REQUEST})
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+ FLAG_MANUAL_REQUEST
+ })
@Retention(RetentionPolicy.SOURCE)
@interface RequestFlags{}
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 04db698..3a4b6bb 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -41,7 +41,7 @@
import java.util.List;
/**
- * Response for a {@link
+ * Response for an {@link
* AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}.
*
* <p>See the main {@link AutofillService} documentation for more details and examples.
@@ -63,7 +63,7 @@
public static final int FLAG_DISABLE_ACTIVITY_ONLY = 0x2;
/** @hide */
- @IntDef(flag = true, value = {
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_TRACK_CONTEXT_COMMITED,
FLAG_DISABLE_ACTIVITY_ONLY
})
@@ -156,6 +156,7 @@
}
/** @hide */
+ @TestApi
public int getFlags() {
return mFlags;
}
@@ -352,22 +353,18 @@
}
/**
- * Sets which fields are used for <a href="#FieldsClassification">fields classification</a>
+ * Sets which fields are used for
+ * <a href="AutofillService.html#FieldClassification">field classification</a>
*
+ * <p><b>Note:</b> This method automatically adds the
+ * {@link FillResponse#FLAG_TRACK_CONTEXT_COMMITED} to the {@link #setFlags(int) flags}.
+
* @throws IllegalArgumentException is length of {@code ids} args is more than
* {@link UserData#getMaxFieldClassificationIdsSize()}.
* @throws IllegalStateException if {@link #build()} or {@link #disableAutofill(long)} was
* already called.
* @throws NullPointerException if {@code ids} or any element on it is {@code null}.
- *
- * TODO(b/67867469):
- * - improve javadoc: explain relationship with UserData and how to check results
- * - unhide / remove testApi
- * - implement multiple ids
- *
- * @hide
*/
- @TestApi
public Builder setFieldClassificationIds(@NonNull AutofillId... ids) {
throwIfDestroyed();
throwIfDisableAutofillCalled();
@@ -375,6 +372,7 @@
Preconditions.checkArgumentInRange(ids.length, 1,
UserData.getMaxFieldClassificationIdsSize(), "ids length");
mFieldClassificationIds = ids;
+ mFlags |= FLAG_TRACK_CONTEXT_COMMITED;
return this;
}
@@ -423,9 +421,8 @@
* @throws IllegalStateException if either {@link #addDataset(Dataset)},
* {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
* {@link #setSaveInfo(SaveInfo)}, {@link #setClientState(Bundle)}, or
- * {link #setFieldClassificationIds(AutofillId...)} was already called.
+ * {@link #setFieldClassificationIds(AutofillId...)} was already called.
*/
- // TODO(b/67867469): add @ to {link setFieldClassificationIds} once it's public
public Builder disableAutofill(long duration) {
throwIfDestroyed();
if (duration <= 0) {
@@ -506,14 +503,13 @@
* {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
* {@link #setSaveInfo(SaveInfo)}, {@link #disableAutofill(long)},
* {@link #setClientState(Bundle)},
- * or {link #setFieldClassificationIds(AutofillId...)}.
+ * or {@link #setFieldClassificationIds(AutofillId...)}.
* <li>{@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} is called
* without any previous calls to {@link #addDataset(Dataset)}.
* </ol>
*
* @return A built response.
*/
- // TODO(b/67867469): add @ to {link setFieldClassificationIds} once it's public
public FillResponse build() {
throwIfDestroyed();
if (mAuthentication == null && mDatasets == null && mSaveInfo == null
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 0b50f07..a5a6177 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -198,23 +198,22 @@
public static final int NEGATIVE_BUTTON_STYLE_REJECT = 1;
/** @hide */
- @IntDef(
- value = {
- NEGATIVE_BUTTON_STYLE_CANCEL,
- NEGATIVE_BUTTON_STYLE_REJECT})
+ @IntDef(prefix = { "NEGATIVE_BUTTON_STYLE_" }, value = {
+ NEGATIVE_BUTTON_STYLE_CANCEL,
+ NEGATIVE_BUTTON_STYLE_REJECT
+ })
@Retention(RetentionPolicy.SOURCE)
@interface NegativeButtonStyle{}
/** @hide */
- @IntDef(
- flag = true,
- value = {
- SAVE_DATA_TYPE_GENERIC,
- SAVE_DATA_TYPE_PASSWORD,
- SAVE_DATA_TYPE_ADDRESS,
- SAVE_DATA_TYPE_CREDIT_CARD,
- SAVE_DATA_TYPE_USERNAME,
- SAVE_DATA_TYPE_EMAIL_ADDRESS})
+ @IntDef(flag = true, prefix = { "SAVE_DATA_TYPE_" }, value = {
+ SAVE_DATA_TYPE_GENERIC,
+ SAVE_DATA_TYPE_PASSWORD,
+ SAVE_DATA_TYPE_ADDRESS,
+ SAVE_DATA_TYPE_CREDIT_CARD,
+ SAVE_DATA_TYPE_USERNAME,
+ SAVE_DATA_TYPE_EMAIL_ADDRESS
+ })
@Retention(RetentionPolicy.SOURCE)
@interface SaveDataType{}
@@ -235,9 +234,10 @@
public static final int FLAG_DONT_SAVE_ON_FINISH = 0x2;
/** @hide */
- @IntDef(
- flag = true,
- value = {FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE, FLAG_DONT_SAVE_ON_FINISH})
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+ FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE,
+ FLAG_DONT_SAVE_ON_FINISH
+ })
@Retention(RetentionPolicy.SOURCE)
@interface SaveInfoFlags{}
diff --git a/core/java/android/service/autofill/Scorer.java b/core/java/android/service/autofill/Scorer.java
index f6a802a..c401855 100644
--- a/core/java/android/service/autofill/Scorer.java
+++ b/core/java/android/service/autofill/Scorer.java
@@ -15,21 +15,14 @@
*/
package android.service.autofill;
-import android.annotation.TestApi;
-
/**
* Helper class used to calculate a score.
*
- * <p>Typically used to calculate the field classification score between an actual
- * {@link android.view.autofill.AutofillValue} filled by the user and the expected value predicted
- * by an autofill service.
- *
- * TODO(b/67867469):
- * - improve javadoc
- * - unhide / remove testApi
- * @hide
+ * <p>Typically used to calculate the
+ * <a href="AutofillService.html#FieldClassification">field classification</a> score between an
+ * actual {@link android.view.autofill.AutofillValue} filled by the user and the expected value
+ * predicted by an autofill service.
*/
-@TestApi
public interface Scorer {
}
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 0d37815..f0cc360 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -23,7 +23,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
import android.app.ActivityThread;
import android.content.ContentResolver;
import android.os.Parcel;
@@ -38,15 +37,9 @@
import java.util.ArrayList;
/**
- * Class used by service to improve autofillable fields detection by tracking the meaning of fields
- * manually edited by the user (when they match values provided by the service).
- *
- * TODO(b/67867469):
- * - improve javadoc / add link to section on AutofillService
- * - unhide / remove testApi
- * @hide
+ * Defines the user data used for
+ * <a href="AutofillService.html#FieldClassification">field classification</a>.
*/
-@TestApi
public final class UserData implements Parcelable {
private static final String TAG = "UserData";
@@ -110,12 +103,7 @@
/**
* A builder for {@link UserData} objects.
- *
- * TODO(b/67867469): unhide / remove testApi
- *
- * @hide
*/
- @TestApi
public static final class Builder {
private final InternalScorer mScorer;
private final ArrayList<String> mRemoteIds;
@@ -123,7 +111,7 @@
private boolean mDestroyed;
/**
- * Creates a new builder for the user data used for <a href="#FieldsClassification">fields
+ * Creates a new builder for the user data used for <a href="#FieldClassification">field
* classification</a>.
*
* @throws IllegalArgumentException if any of the following occurs:
@@ -288,14 +276,16 @@
}
/**
- * Gets the minimum length of values passed to {@link Builder#Builder(Scorer, String, String)}.
+ * Gets the minimum length of values passed to the builder's constructor or
+ * or {@link Builder#add(String, String)}.
*/
public static int getMinValueLength() {
return getInt(AUTOFILL_USER_DATA_MIN_VALUE_LENGTH, DEFAULT_MIN_VALUE_LENGTH);
}
/**
- * Gets the maximum length of values passed to {@link Builder#Builder(Scorer, String, String)}.
+ * Gets the maximum length of values passed to the builder's constructor or
+ * or {@link Builder#add(String, String)}.
*/
public static int getMaxValueLength() {
return getInt(AUTOFILL_USER_DATA_MAX_VALUE_LENGTH, DEFAULT_MAX_VALUE_LENGTH);
diff --git a/core/java/android/service/carrier/CarrierService.java b/core/java/android/service/carrier/CarrierService.java
index 2707f14..b94ccf9 100644
--- a/core/java/android/service/carrier/CarrierService.java
+++ b/core/java/android/service/carrier/CarrierService.java
@@ -33,8 +33,8 @@
* To extend this class, you must declare the service in your manifest file to require the
* {@link android.Manifest.permission#BIND_CARRIER_SERVICES} permission and include an intent
* filter with the {@link #CARRIER_SERVICE_INTERFACE}. If the service should have a long-lived
- * binding, set android.service.carrier.LONG_LIVED_BINDING to true in the service's metadata.
- * For example:
+ * binding, set <code>android.service.carrier.LONG_LIVED_BINDING</code> to <code>true</code> in the
+ * service's metadata. For example:
* </p>
*
* <pre>{@code
diff --git a/core/java/android/service/dreams/OWNERS b/core/java/android/service/dreams/OWNERS
new file mode 100644
index 0000000..3c9bbf8
--- /dev/null
+++ b/core/java/android/service/dreams/OWNERS
@@ -0,0 +1,3 @@
+dsandler@google.com
+michaelwr@google.com
+roosa@google.com
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 447afe6..2a352ad 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -39,7 +39,12 @@
public static final String SCHEME = "condition";
/** @hide */
- @IntDef({STATE_FALSE, STATE_TRUE, STATE_TRUE, STATE_ERROR})
+ @IntDef(prefix = { "STATE_" }, value = {
+ STATE_FALSE,
+ STATE_TRUE,
+ STATE_UNKNOWN,
+ STATE_ERROR
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface State {}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index dac663e7..18d4a1e 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -229,8 +229,11 @@
/** @hide */
- @IntDef({NOTIFICATION_CHANNEL_OR_GROUP_ADDED, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED,
- NOTIFICATION_CHANNEL_OR_GROUP_DELETED})
+ @IntDef(prefix = { "NOTIFICATION_CHANNEL_OR_GROUP_" }, value = {
+ NOTIFICATION_CHANNEL_OR_GROUP_ADDED,
+ NOTIFICATION_CHANNEL_OR_GROUP_UPDATED,
+ NOTIFICATION_CHANNEL_OR_GROUP_DELETED
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface ChannelOrGroupModificationTypes {}
diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
index 9332a5b..0bf68b7 100644
--- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java
+++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
@@ -65,10 +65,10 @@
*/
public static final int FLASH_LOCK_LOCKED = 1;
- @IntDef({
- FLASH_LOCK_UNKNOWN,
- FLASH_LOCK_LOCKED,
- FLASH_LOCK_UNLOCKED,
+ @IntDef(prefix = { "FLASH_LOCK_" }, value = {
+ FLASH_LOCK_UNKNOWN,
+ FLASH_LOCK_LOCKED,
+ FLASH_LOCK_UNLOCKED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface FlashLockState {}
diff --git a/core/java/android/service/settings/suggestions/Suggestion.java b/core/java/android/service/settings/suggestions/Suggestion.java
index cfeb7fc..11e1e67 100644
--- a/core/java/android/service/settings/suggestions/Suggestion.java
+++ b/core/java/android/service/settings/suggestions/Suggestion.java
@@ -38,7 +38,7 @@
/**
* @hide
*/
- @IntDef(flag = true, value = {
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_HAS_BUTTON,
})
@Retention(RetentionPolicy.SOURCE)
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 5ef934e..4bade9f 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -114,11 +114,10 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- value = {
- FLAG_GRANT_TRUST_INITIATED_BY_USER,
- FLAG_GRANT_TRUST_DISMISS_KEYGUARD,
- })
+ @IntDef(flag = true, prefix = { "FLAG_GRANT_TRUST_" }, value = {
+ FLAG_GRANT_TRUST_INITIATED_BY_USER,
+ FLAG_GRANT_TRUST_DISMISS_KEYGUARD,
+ })
public @interface GrantTrustFlags {}
@@ -138,11 +137,10 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- value = {
- TOKEN_STATE_ACTIVE,
- TOKEN_STATE_INACTIVE,
- })
+ @IntDef(flag = true, prefix = { "TOKEN_STATE_" }, value = {
+ TOKEN_STATE_ACTIVE,
+ TOKEN_STATE_INACTIVE,
+ })
public @interface TokenState {}
private static final int MSG_UNLOCK_ATTEMPT = 1;
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 9464a87..76d89ef 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -87,11 +87,11 @@
// Keyphrase management actions. Used in getManageIntent() ----//
@Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {
- MANAGE_ACTION_ENROLL,
- MANAGE_ACTION_RE_ENROLL,
- MANAGE_ACTION_UN_ENROLL
- })
+ @IntDef(prefix = { "MANAGE_ACTION_" }, value = {
+ MANAGE_ACTION_ENROLL,
+ MANAGE_ACTION_RE_ENROLL,
+ MANAGE_ACTION_UN_ENROLL
+ })
private @interface ManageActions {}
/**
@@ -116,12 +116,11 @@
//-- Flags for startRecognition ----//
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- value = {
- RECOGNITION_FLAG_NONE,
- RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO,
- RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS
- })
+ @IntDef(flag = true, prefix = { "RECOGNITION_FLAG_" }, value = {
+ RECOGNITION_FLAG_NONE,
+ RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO,
+ RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS
+ })
public @interface RecognitionFlags {}
/**
@@ -150,11 +149,10 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- value = {
- RECOGNITION_MODE_VOICE_TRIGGER,
- RECOGNITION_MODE_USER_IDENTIFICATION,
- })
+ @IntDef(flag = true, prefix = { "RECOGNITION_MODE_" }, value = {
+ RECOGNITION_MODE_VOICE_TRIGGER,
+ RECOGNITION_MODE_USER_IDENTIFICATION,
+ })
public @interface RecognitionModes {}
/**
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
index fb6f637..b8f2191 100644
--- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -27,6 +27,7 @@
void setDesiredSize(int width, int height);
void setDisplayPadding(in Rect padding);
void setVisibility(boolean visible);
+ void setInAmbientMode(boolean inAmbientDisplay);
void dispatchPointer(in MotionEvent event);
void dispatchWallpaperCommand(String action, int x, int y,
int z, in Bundle extras);
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index e5ab3e1..595bfb7 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -102,6 +102,7 @@
private static final int DO_DETACH = 20;
private static final int DO_SET_DESIRED_SIZE = 30;
private static final int DO_SET_DISPLAY_PADDING = 40;
+ private static final int DO_IN_AMBIENT_MODE = 50;
private static final int MSG_UPDATE_SURFACE = 10000;
private static final int MSG_VISIBILITY_CHANGED = 10010;
@@ -195,6 +196,7 @@
float mPendingYOffsetStep;
boolean mPendingSync;
MotionEvent mPendingMove;
+ boolean mIsInAmbientMode;
// Needed for throttling onComputeColors.
private long mLastColorInvalidation;
@@ -431,6 +433,15 @@
public boolean isPreview() {
return mIWallpaperEngine.mIsPreview;
}
+
+ /**
+ * Returns true if this engine is running in ambient mode -- that is,
+ * it is being shown in low power mode, in always on display.
+ * @hide
+ */
+ public boolean isInAmbientMode() {
+ return mIsInAmbientMode;
+ }
/**
* Control whether this wallpaper will receive raw touch events
@@ -549,6 +560,15 @@
}
/**
+ * Called when the device enters or exits ambient mode.
+ *
+ * @param inAmbientMode {@code true} if in ambient mode.
+ * @hide
+ */
+ public void onAmbientModeChanged(boolean inAmbientMode) {
+ }
+
+ /**
* Called when an application has changed the desired virtual size of
* the wallpaper.
*/
@@ -631,6 +651,16 @@
return null;
}
+ /**
+ * Sets internal engine state. Only for testing.
+ * @param created {@code true} or {@code false}.
+ * @hide
+ */
+ @VisibleForTesting
+ public void setCreated(boolean created) {
+ mCreated = created;
+ }
+
protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
out.print(prefix); out.print("mInitializing="); out.print(mInitializing);
out.print(" mDestroyed="); out.println(mDestroyed);
@@ -683,7 +713,8 @@
}
Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
mCaller.sendMessage(msg);
- } else {event.recycle();
+ } else {
+ event.recycle();
}
}
@@ -986,6 +1017,26 @@
updateSurface(false, false, false);
}
+ /**
+ * Executes life cycle event and updates internal ambient mode state based on
+ * message sent from handler.
+ *
+ * @param inAmbientMode True if in ambient mode.
+ * @hide
+ */
+ @VisibleForTesting
+ public void doAmbientModeChanged(boolean inAmbientMode) {
+ if (!mDestroyed) {
+ if (DEBUG) {
+ Log.v(TAG, "onAmbientModeChanged(" + inAmbientMode + "): " + this);
+ }
+ mIsInAmbientMode = inAmbientMode;
+ if (mCreated) {
+ onAmbientModeChanged(inAmbientMode);
+ }
+ }
+ }
+
void doDesiredSizeChanged(int desiredWidth, int desiredHeight) {
if (!mDestroyed) {
if (DEBUG) Log.v(TAG, "onDesiredSizeChanged("
@@ -1226,6 +1277,12 @@
mCaller.sendMessage(msg);
}
+ @Override
+ public void setInAmbientMode(boolean inAmbientDisplay) throws RemoteException {
+ Message msg = mCaller.obtainMessageI(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0);
+ mCaller.sendMessage(msg);
+ }
+
public void dispatchPointer(MotionEvent event) {
if (mEngine != null) {
mEngine.dispatchPointer(event);
@@ -1263,6 +1320,7 @@
mCaller.sendMessage(msg);
}
+ @Override
public void executeMessage(Message message) {
switch (message.what) {
case DO_ATTACH: {
@@ -1289,6 +1347,11 @@
}
case DO_SET_DISPLAY_PADDING: {
mEngine.doDisplayPaddingChanged((Rect) message.obj);
+ return;
+ }
+ case DO_IN_AMBIENT_MODE: {
+ mEngine.doAmbientModeChanged(message.arg1 != 0);
+ return;
}
case MSG_UPDATE_SURFACE:
mEngine.updateSurface(true, false, false);
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 763ea2c..01562b3 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -80,8 +80,15 @@
public static final int STOPPED = -2;
/** @hide */
- @IntDef({ERROR_SYNTHESIS, ERROR_SERVICE, ERROR_OUTPUT, ERROR_NETWORK, ERROR_NETWORK_TIMEOUT,
- ERROR_INVALID_REQUEST, ERROR_NOT_INSTALLED_YET})
+ @IntDef(prefix = { "ERROR_" }, value = {
+ ERROR_SYNTHESIS,
+ ERROR_SERVICE,
+ ERROR_OUTPUT,
+ ERROR_NETWORK,
+ ERROR_NETWORK_TIMEOUT,
+ ERROR_INVALID_REQUEST,
+ ERROR_NOT_INSTALLED_YET
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface Error {}
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index fba358c..6bca37a 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -42,8 +42,7 @@
* {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint)
* Canvas.drawText()} directly.</p>
*/
-public class DynamicLayout extends Layout
-{
+public class DynamicLayout extends Layout {
private static final int PRIORITY = 128;
private static final int BLOCK_MINIMUM_CHARACTER_LENGTH = 400;
@@ -303,8 +302,9 @@
}
/**
- * Make a layout for the specified text that will be updated as the text is changed.
+ * @deprecated Use {@link Builder} instead.
*/
+ @Deprecated
public DynamicLayout(@NonNull CharSequence base,
@NonNull TextPaint paint,
@IntRange(from = 0) int width, @NonNull Alignment align,
@@ -315,9 +315,9 @@
}
/**
- * Make a layout for the transformed text (password transformation being the primary example of
- * a transformation) that will be updated as the base text is changed.
+ * @deprecated Use {@link Builder} instead.
*/
+ @Deprecated
public DynamicLayout(@NonNull CharSequence base, @NonNull CharSequence display,
@NonNull TextPaint paint,
@IntRange(from = 0) int width, @NonNull Alignment align,
@@ -328,10 +328,9 @@
}
/**
- * Make a layout for the transformed text (password transformation being the primary example of
- * a transformation) that will be updated as the base text is changed. If ellipsize is non-null,
- * the Layout will ellipsize the text down to ellipsizedWidth.
+ * @deprecated Use {@link Builder} instead.
*/
+ @Deprecated
public DynamicLayout(@NonNull CharSequence base, @NonNull CharSequence display,
@NonNull TextPaint paint,
@IntRange(from = 0) int width, @NonNull Alignment align,
@@ -351,7 +350,9 @@
* the Layout will ellipsize the text down to ellipsizedWidth.
*
* @hide
+ * @deprecated Use {@link Builder} instead.
*/
+ @Deprecated
public DynamicLayout(@NonNull CharSequence base, @NonNull CharSequence display,
@NonNull TextPaint paint,
@IntRange(from = 0) int width,
@@ -492,7 +493,9 @@
}
}
- private void reflow(CharSequence s, int where, int before, int after) {
+ /** @hide */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void reflow(CharSequence s, int where, int before, int after) {
if (s != mBase)
return;
@@ -805,8 +808,8 @@
return;
}
- int firstBlock = -1;
- int lastBlock = -1;
+ /*final*/ int firstBlock = -1;
+ /*final*/ int lastBlock = -1;
for (int i = 0; i < mNumberOfBlocks; i++) {
if (mBlockEndLines[i] >= startLine) {
firstBlock = i;
@@ -821,10 +824,10 @@
}
final int lastBlockEndLine = mBlockEndLines[lastBlock];
- boolean createBlockBefore = startLine > (firstBlock == 0 ? 0 :
+ final boolean createBlockBefore = startLine > (firstBlock == 0 ? 0 :
mBlockEndLines[firstBlock - 1] + 1);
- boolean createBlock = newLineCount > 0;
- boolean createBlockAfter = endLine < mBlockEndLines[lastBlock];
+ final boolean createBlock = newLineCount > 0;
+ final boolean createBlockAfter = endLine < mBlockEndLines[lastBlock];
int numAddedBlocks = 0;
if (createBlockBefore) numAddedBlocks++;
@@ -863,12 +866,18 @@
if (numAddedBlocks + numRemovedBlocks != 0 && mBlocksAlwaysNeedToBeRedrawn != null) {
final ArraySet<Integer> set = new ArraySet<>();
+ final int changedBlockCount = numAddedBlocks - numRemovedBlocks;
for (int i = 0; i < mBlocksAlwaysNeedToBeRedrawn.size(); i++) {
Integer block = mBlocksAlwaysNeedToBeRedrawn.valueAt(i);
- if (block > firstBlock) {
- block += numAddedBlocks - numRemovedBlocks;
+ if (block < firstBlock) {
+ // block index is before firstBlock add it since it did not change
+ set.add(block);
}
- set.add(block);
+ if (block > lastBlock) {
+ // block index is after lastBlock, the index reduced to += changedBlockCount
+ block += changedBlockCount;
+ set.add(block);
+ }
}
mBlocksAlwaysNeedToBeRedrawn = set;
}
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 4654e83..7386e3e 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -179,7 +179,11 @@
/** @hide */
@Retention(SOURCE)
- @IntDef({VARIANT_DEFAULT, VARIANT_COMPACT, VARIANT_ELEGANT})
+ @IntDef(prefix = { "VARIANT_" }, value = {
+ VARIANT_DEFAULT,
+ VARIANT_COMPACT,
+ VARIANT_ELEGANT
+ })
public @interface Variant {}
/**
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 2a693a1..bf4b6ac 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -48,7 +48,11 @@
*/
public abstract class Layout {
/** @hide */
- @IntDef({BREAK_STRATEGY_SIMPLE, BREAK_STRATEGY_HIGH_QUALITY, BREAK_STRATEGY_BALANCED})
+ @IntDef(prefix = { "BREAK_STRATEGY_" }, value = {
+ BREAK_STRATEGY_SIMPLE,
+ BREAK_STRATEGY_HIGH_QUALITY,
+ BREAK_STRATEGY_BALANCED
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface BreakStrategy {}
@@ -73,8 +77,11 @@
public static final int BREAK_STRATEGY_BALANCED = 2;
/** @hide */
- @IntDef({HYPHENATION_FREQUENCY_NORMAL, HYPHENATION_FREQUENCY_FULL,
- HYPHENATION_FREQUENCY_NONE})
+ @IntDef(prefix = { "HYPHENATION_FREQUENCY_" }, value = {
+ HYPHENATION_FREQUENCY_NORMAL,
+ HYPHENATION_FREQUENCY_FULL,
+ HYPHENATION_FREQUENCY_NONE
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface HyphenationFrequency {}
@@ -105,7 +112,10 @@
ArrayUtils.emptyArray(ParagraphStyle.class);
/** @hide */
- @IntDef({JUSTIFICATION_MODE_NONE, JUSTIFICATION_MODE_INTER_WORD})
+ @IntDef(prefix = { "JUSTIFICATION_MODE_" }, value = {
+ JUSTIFICATION_MODE_NONE,
+ JUSTIFICATION_MODE_INTER_WORD
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface JustificationMode {}
@@ -2267,7 +2277,10 @@
private int mJustificationMode;
/** @hide */
- @IntDef({DIR_LEFT_TO_RIGHT, DIR_RIGHT_TO_LEFT})
+ @IntDef(prefix = { "DIR_" }, value = {
+ DIR_LEFT_TO_RIGHT,
+ DIR_RIGHT_TO_LEFT
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface Direction {}
@@ -2308,7 +2321,10 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({TEXT_SELECTION_LAYOUT_RIGHT_TO_LEFT, TEXT_SELECTION_LAYOUT_LEFT_TO_RIGHT})
+ @IntDef(prefix = { "TEXT_SELECTION_LAYOUT_" }, value = {
+ TEXT_SELECTION_LAYOUT_RIGHT_TO_LEFT,
+ TEXT_SELECTION_LAYOUT_LEFT_TO_RIGHT
+ })
public @interface TextSelectionLayout {}
/** @hide */
diff --git a/core/java/android/text/OWNERS b/core/java/android/text/OWNERS
new file mode 100644
index 0000000..0f85e1f
--- /dev/null
+++ b/core/java/android/text/OWNERS
@@ -0,0 +1,4 @@
+siyamed@google.com
+nona@google.com
+clarabayarri@google.com
+toki@google.com
diff --git a/core/java/android/text/PremeasuredText.java b/core/java/android/text/PremeasuredText.java
new file mode 100644
index 0000000..465314d
--- /dev/null
+++ b/core/java/android/text/PremeasuredText.java
@@ -0,0 +1,272 @@
+/*
+ * 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.text;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.util.IntArray;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+
+/**
+ * A text which has already been measured.
+ *
+ * TODO: Rename to better name? e.g. MeasuredText, FrozenText etc.
+ */
+public class PremeasuredText implements Spanned {
+ private static final char LINE_FEED = '\n';
+
+ // The original text.
+ private final @NonNull CharSequence mText;
+
+ // The inclusive start offset of the measuring target.
+ private final @IntRange(from = 0) int mStart;
+
+ // The exclusive end offset of the measuring target.
+ private final @IntRange(from = 0) int mEnd;
+
+ // The TextPaint used for measurement.
+ private final @NonNull TextPaint mPaint;
+
+ // The requested text direction.
+ private final @NonNull TextDirectionHeuristic mTextDir;
+
+ // The measured paragraph texts.
+ private final @NonNull MeasuredText[] mMeasuredTexts;
+
+ // The sorted paragraph end offsets.
+ private final @NonNull int[] mParagraphBreakPoints;
+
+ /**
+ * Build PremeasuredText from the text.
+ *
+ * @param text The text to be measured.
+ * @param paint The paint to be used for drawing.
+ * @param textDir The text direction.
+ * @return The measured text.
+ */
+ public static @NonNull PremeasuredText build(@NonNull CharSequence text,
+ @NonNull TextPaint paint,
+ @NonNull TextDirectionHeuristic textDir) {
+ return PremeasuredText.build(text, paint, textDir, 0, text.length());
+ }
+
+ /**
+ * Build PremeasuredText from the specific range of the text..
+ *
+ * @param text The text to be measured.
+ * @param paint The paint to be used for drawing.
+ * @param textDir The text direction.
+ * @param start The inclusive start offset of the text.
+ * @param end The exclusive start offset of the text.
+ * @return The measured text.
+ */
+ public static @NonNull PremeasuredText build(@NonNull CharSequence text,
+ @NonNull TextPaint paint,
+ @NonNull TextDirectionHeuristic textDir,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end) {
+ Preconditions.checkNotNull(text);
+ Preconditions.checkNotNull(paint);
+ Preconditions.checkNotNull(textDir);
+ Preconditions.checkArgumentInRange(start, 0, text.length(), "start");
+ Preconditions.checkArgumentInRange(end, 0, text.length(), "end");
+
+ final IntArray paragraphEnds = new IntArray();
+ final ArrayList<MeasuredText> measuredTexts = new ArrayList<>();
+
+ int paraEnd = 0;
+ for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
+ paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end);
+ if (paraEnd < 0) {
+ // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph end.
+ paraEnd = end;
+ } else {
+ paraEnd++; // Includes LINE_FEED(U+000A) to the prev paragraph.
+ }
+
+ paragraphEnds.add(paraEnd);
+ measuredTexts.add(MeasuredText.buildForStaticLayout(
+ paint, text, paraStart, paraEnd, textDir, null /* no recycle */));
+ }
+
+ return new PremeasuredText(text, start, end, paint, textDir,
+ measuredTexts.toArray(new MeasuredText[measuredTexts.size()]),
+ paragraphEnds.toArray());
+ }
+
+ // Use PremeasuredText.build instead.
+ private PremeasuredText(@NonNull CharSequence text,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @NonNull TextPaint paint,
+ @NonNull TextDirectionHeuristic textDir,
+ @NonNull MeasuredText[] measuredTexts,
+ @NonNull int[] paragraphBreakPoints) {
+ mText = text;
+ mStart = start;
+ mEnd = end;
+ mPaint = paint;
+ mMeasuredTexts = measuredTexts;
+ mParagraphBreakPoints = paragraphBreakPoints;
+ mTextDir = textDir;
+ }
+
+ /**
+ * Return the underlying text.
+ */
+ public @NonNull CharSequence getText() {
+ return mText;
+ }
+
+ /**
+ * Returns the inclusive start offset of measured region.
+ */
+ public @IntRange(from = 0) int getStart() {
+ return mStart;
+ }
+
+ /**
+ * Returns the exclusive end offset of measured region.
+ */
+ public @IntRange(from = 0) int getEnd() {
+ return mEnd;
+ }
+
+ /**
+ * Returns the text direction associated with char sequence.
+ */
+ public @NonNull TextDirectionHeuristic getTextDir() {
+ return mTextDir;
+ }
+
+ /**
+ * Returns the paint used to measure this text.
+ */
+ public @NonNull TextPaint getPaint() {
+ return mPaint;
+ }
+
+ /**
+ * Returns the length of the paragraph of this text.
+ */
+ public @IntRange(from = 0) int getParagraphCount() {
+ return mParagraphBreakPoints.length;
+ }
+
+ /**
+ * Returns the paragraph start offset of the text.
+ */
+ public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
+ Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
+ return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
+ }
+
+ /**
+ * Returns the paragraph end offset of the text.
+ */
+ public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
+ Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
+ return mParagraphBreakPoints[paraIndex];
+ }
+
+ /** @hide */
+ public @NonNull MeasuredText getMeasuredText(@IntRange(from = 0) int paraIndex) {
+ return mMeasuredTexts[paraIndex];
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Spanned overrides
+ //
+ // Just proxy for underlying mText if appropriate.
+
+ @Override
+ public <T> T[] getSpans(int start, int end, Class<T> type) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).getSpans(start, end, type);
+ } else {
+ return ArrayUtils.emptyArray(type);
+ }
+ }
+
+ @Override
+ public int getSpanStart(Object tag) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).getSpanStart(tag);
+ } else {
+ return -1;
+ }
+ }
+
+ @Override
+ public int getSpanEnd(Object tag) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).getSpanEnd(tag);
+ } else {
+ return -1;
+ }
+ }
+
+ @Override
+ public int getSpanFlags(Object tag) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).getSpanFlags(tag);
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public int nextSpanTransition(int start, int limit, Class type) {
+ if (mText instanceof Spanned) {
+ return ((Spanned) mText).nextSpanTransition(start, limit, type);
+ } else {
+ return mText.length();
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // CharSequence overrides.
+ //
+ // Just proxy for underlying mText.
+
+ @Override
+ public int length() {
+ return mText.length();
+ }
+
+ @Override
+ public char charAt(int index) {
+ // TODO: Should this be index + mStart ?
+ return mText.charAt(index);
+ }
+
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ // TODO: return PremeasuredText.
+ // TODO: Should this be index + mStart, end + mStart ?
+ return mText.subSequence(start, end);
+ }
+
+ @Override
+ public String toString() {
+ return mText.toString();
+ }
+}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 400b075..d69b119 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -454,6 +454,10 @@
private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<>(3);
}
+ /**
+ * @deprecated Use {@link Builder} instead.
+ */
+ @Deprecated
public StaticLayout(CharSequence source, TextPaint paint,
int width,
Alignment align, float spacingmult, float spacingadd,
@@ -463,16 +467,9 @@
}
/**
- * @hide
+ * @deprecated Use {@link Builder} instead.
*/
- public StaticLayout(CharSequence source, TextPaint paint,
- int width, Alignment align, TextDirectionHeuristic textDir,
- float spacingmult, float spacingadd,
- boolean includepad) {
- this(source, 0, source.length(), paint, width, align, textDir,
- spacingmult, spacingadd, includepad);
- }
-
+ @Deprecated
public StaticLayout(CharSequence source, int bufstart, int bufend,
TextPaint paint, int outerwidth,
Alignment align,
@@ -483,17 +480,9 @@
}
/**
- * @hide
+ * @deprecated Use {@link Builder} instead.
*/
- public StaticLayout(CharSequence source, int bufstart, int bufend,
- TextPaint paint, int outerwidth,
- Alignment align, TextDirectionHeuristic textDir,
- float spacingmult, float spacingadd,
- boolean includepad) {
- this(source, bufstart, bufend, paint, outerwidth, align, textDir,
- spacingmult, spacingadd, includepad, null, 0, Integer.MAX_VALUE);
-}
-
+ @Deprecated
public StaticLayout(CharSequence source, int bufstart, int bufend,
TextPaint paint, int outerwidth,
Alignment align,
@@ -507,7 +496,9 @@
/**
* @hide
+ * @deprecated Use {@link Builder} instead.
*/
+ @Deprecated
public StaticLayout(CharSequence source, int bufstart, int bufend,
TextPaint paint, int outerwidth,
Alignment align, TextDirectionHeuristic textDir,
@@ -565,6 +556,9 @@
Builder.recycle(b);
}
+ /**
+ * Used by DynamicLayout.
+ */
/* package */ StaticLayout(@Nullable CharSequence text) {
super(text, null, 0, null, 0, 0);
@@ -610,8 +604,8 @@
/* package */ void generate(Builder b, boolean includepad, boolean trackpad) {
final CharSequence source = b.mText;
- int bufStart = b.mStart;
- int bufEnd = b.mEnd;
+ final int bufStart = b.mStart;
+ final int bufEnd = b.mEnd;
TextPaint paint = b.mPaint;
int outerWidth = b.mWidth;
TextDirectionHeuristic textDir = b.mTextDir;
@@ -634,10 +628,6 @@
Paint.FontMetricsInt fm = b.mFontMetricsInt;
int[] chooseHtv = null;
- Spanned spanned = null;
- if (source instanceof Spanned)
- spanned = (Spanned) source;
-
final int[] indents;
if (mLeftIndents != null || mRightIndents != null) {
final int leftLen = mLeftIndents == null ? 0 : mLeftIndents.length;
@@ -660,16 +650,34 @@
b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
indents, mLeftPaddings, mRightPaddings);
- MeasuredText measured = null;
+ PremeasuredText premeasured = null;
+ final Spanned spanned;
+ if (source instanceof PremeasuredText) {
+ premeasured = (PremeasuredText) source;
+
+ final CharSequence original = premeasured.getText();
+ spanned = (original instanceof Spanned) ? (Spanned) original : null;
+
+ if (bufStart != premeasured.getStart() || bufEnd != premeasured.getEnd()) {
+ // The buffer position has changed. Re-measure here.
+ premeasured = PremeasuredText.build(original, paint, textDir, bufStart, bufEnd);
+ } else {
+ // We can use premeasured information.
+
+ // Overwrite with the one when premeasured.
+ // TODO: Give an option for developer not to overwrite and measure again here?
+ textDir = premeasured.getTextDir();
+ paint = premeasured.getPaint();
+ }
+ } else {
+ premeasured = PremeasuredText.build(source, paint, textDir, bufStart, bufEnd);
+ spanned = (source instanceof Spanned) ? (Spanned) source : null;
+ }
+
try {
- int paraEnd;
- for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
- paraEnd = TextUtils.indexOf(source, CHAR_NEW_LINE, paraStart, bufEnd);
- if (paraEnd < 0) {
- paraEnd = bufEnd;
- } else {
- paraEnd++;
- }
+ for (int paraIndex = 0; paraIndex < premeasured.getParagraphCount(); paraIndex++) {
+ final int paraStart = premeasured.getParagraphStart(paraIndex);
+ final int paraEnd = premeasured.getParagraphEnd(paraIndex);
int firstWidthLineCount = 1;
int firstWidth = outerWidth;
@@ -735,8 +743,7 @@
}
}
- measured = MeasuredText.buildForStaticLayout(
- paint, source, paraStart, paraEnd, textDir, measured);
+ final MeasuredText measured = premeasured.getMeasuredText(paraIndex);
final char[] chs = measured.getChars();
final int[] spanEndCache = measured.getSpanEndCache().getRawArray();
final int[] fmCache = measured.getFontMetrics().getRawArray();
@@ -887,6 +894,8 @@
if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE)
&& mLineCount < mMaximumVisibleLineCount) {
+ final MeasuredText measured =
+ MeasuredText.buildForBidi(source, bufEnd, bufEnd, textDir, null);
paint.getFontMetricsInt(fm);
v = out(source,
bufEnd, bufEnd, fm.ascent, fm.descent,
@@ -894,15 +903,12 @@
v,
spacingmult, spacingadd, null,
null, fm, 0,
- needMultiply, null, bufEnd,
+ needMultiply, measured, bufEnd,
includepad, trackpad, addLastLineSpacing, null,
null, bufStart, ellipsize,
ellipsizedWidth, 0, paint, false);
}
} finally {
- if (measured != null) {
- measured.recycle();
- }
nFinish(nativePtr);
}
}
@@ -912,7 +918,7 @@
private int out(final CharSequence text, final int start, final int end, int above, int below,
int top, int bottom, int v, final float spacingmult, final float spacingadd,
final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm,
- final int flags, final boolean needMultiply, @Nullable final MeasuredText measured,
+ final int flags, final boolean needMultiply, @NonNull final MeasuredText measured,
final int bufEnd, final boolean includePad, final boolean trackPad,
final boolean addLastLineLineSpacing, final char[] chs, final float[] widths,
final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth,
@@ -921,7 +927,7 @@
final int off = j * mColumns;
final int want = off + mColumns + TOP;
int[] lines = mLines;
- final int dir = (start == end) ? Layout.DIR_LEFT_TO_RIGHT : measured.getParagraphDir();
+ final int dir = measured.getParagraphDir();
if (want >= lines.length) {
final int[] grow = ArrayUtils.newUnpaddedIntArray(GrowingArrayUtils.growSize(want));
@@ -948,11 +954,7 @@
lines[off + TAB] |= flags & TAB_MASK;
lines[off + HYPHEN] = flags;
lines[off + DIR] |= dir << DIR_SHIFT;
- if (start == end) {
- mLineDirections[j] = Layout.DIRS_ALL_LEFT_TO_RIGHT;
- } else {
- mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart);
- }
+ mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart);
final boolean firstLine = (j == 0);
final boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index 5b851ca..f0838a1 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -52,7 +52,10 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag=true, value={MODE_IN, MODE_OUT})
+ @IntDef(flag = true, prefix = { "MODE_" }, value = {
+ MODE_IN,
+ MODE_OUT
+ })
@interface VisibilityMode {}
/**
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 54b48b6..b7d33599 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -41,7 +41,7 @@
DEFAULT_FLAGS.put("new_settings_suggestion", "true");
DEFAULT_FLAGS.put("settings_search_v2", "false");
DEFAULT_FLAGS.put("settings_app_info_v2", "false");
- DEFAULT_FLAGS.put("settings_connected_device_v2", "false");
+ DEFAULT_FLAGS.put("settings_connected_device_v2", "true");
DEFAULT_FLAGS.put("settings_battery_v2", "false");
DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
}
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
new file mode 100644
index 0000000..4805318
--- /dev/null
+++ b/core/java/android/util/StatsLog.java
@@ -0,0 +1,70 @@
+/*
+ * 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.util;
+
+/**
+ * StatsLog provides an API for developers to send events to statsd. The events can be used to
+ * define custom metrics inside statsd. We will rate-limit how often the calls can be made inside
+ * statsd.
+ */
+public final class StatsLog extends StatsLogInternal {
+ private static final String TAG = "StatsManager";
+
+ private StatsLog() {}
+
+ /**
+ * Logs a start event.
+ *
+ * @param label developer-chosen label that is from [0, 16).
+ * @return True if the log request was sent to statsd.
+ */
+ public static boolean logStart(int label) {
+ if (label >= 0 && label < 16) {
+ StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__START);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Logs a stop event.
+ *
+ * @param label developer-chosen label that is from [0, 16).
+ * @return True if the log request was sent to statsd.
+ */
+ public static boolean logStop(int label) {
+ if (label >= 0 && label < 16) {
+ StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__STOP);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Logs an event that does not represent a start or stop boundary.
+ *
+ * @param label developer-chosen label that is from [0, 16).
+ * @return True if the log request was sent to statsd.
+ */
+ public static boolean logEvent(int label) {
+ if (label >= 0 && label < 16) {
+ StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__UNSPECIFIED);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 1808123..a74a882 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -917,18 +917,6 @@
}
}
- public static class SignatureNotFoundException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public SignatureNotFoundException(String message) {
- super(message);
- }
-
- public SignatureNotFoundException(String message, Throwable cause) {
- super(message, cause);
- }
- }
-
/**
* {@link DataDigester} that updates multiple {@link MessageDigest}s whenever data is feeded.
*/
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
new file mode 100644
index 0000000..75c1000
--- /dev/null
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -0,0 +1,261 @@
+/*
+ * 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.util.apk;
+
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.Signature;
+import android.os.Trace;
+import android.util.jar.StrictJarFile;
+
+import com.android.internal.util.ArrayUtils;
+
+import libcore.io.IoUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.zip.ZipEntry;
+
+/**
+ * Facade class that takes care of the details of APK verification on
+ * behalf of PackageParser.
+ *
+ * @hide for internal use only.
+ */
+public class ApkSignatureVerifier {
+
+ public static final int VERSION_JAR_SIGNATURE_SCHEME = 1;
+ public static final int VERSION_APK_SIGNATURE_SCHEME_V2 = 2;
+
+ private static final AtomicReference<byte[]> sBuffer = new AtomicReference<>();
+
+ /**
+ * Verifies the provided APK and returns the certificates associated with each signer. Also
+ * ensures that the provided APK contains an AndroidManifest.xml file.
+ *
+ * @param systemDir systemDir apk contents are already trusted, so we don't need to enforce
+ * v2 stripping rollback protection, or verify integrity of the APK.
+ *
+ * @throws PackageParserException if the APK's signature failed to verify.
+ */
+ public static Result verify(String apkPath, int minSignatureSchemeVersion, boolean systemDir)
+ throws PackageParserException {
+
+ // first try v2
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV2");
+ try {
+ Certificate[][] signerCerts;
+ signerCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
+ Signature[] signerSigs = convertToSignatures(signerCerts);
+
+ // sanity check - must have an AndroidManifest file
+ StrictJarFile jarFile = null;
+ try {
+ jarFile = new StrictJarFile(apkPath, false, false);
+ final ZipEntry manifestEntry =
+ jarFile.findEntry(PackageParser.ANDROID_MANIFEST_FILENAME);
+ if (manifestEntry == null) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Package " + apkPath + " has no manifest");
+ }
+ } finally {
+ closeQuietly(jarFile);
+ }
+ return new Result(signerCerts, signerSigs, VERSION_APK_SIGNATURE_SCHEME_V2);
+ } catch (SignatureNotFoundException e) {
+ // not signed with v2, try older if allowed
+ if (minSignatureSchemeVersion >= VERSION_APK_SIGNATURE_SCHEME_V2) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ "No APK Signature Scheme v2 signature in package " + apkPath, e);
+ }
+ } catch (PackageParserException e) {
+ // preserve any new exceptions explicitly thrown here
+ throw e;
+ } catch (Exception e) {
+ // APK Signature Scheme v2 signature found but did not verify
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ "Failed to collect certificates from " + apkPath
+ + " using APK Signature Scheme v2", e);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ // v2 didn't work, try jarsigner
+ return verifyV1Signature(apkPath, systemDir);
+ }
+
+ private static Result verifyV1Signature(String apkPath, boolean systemDir)
+ throws PackageParserException {
+ StrictJarFile jarFile = null;
+
+ try {
+ final Certificate[][] lastCerts;
+ final Signature[] lastSigs;
+
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
+ jarFile = new StrictJarFile(apkPath, true, !systemDir);
+ final List<ZipEntry> toVerify = new ArrayList<>();
+
+ // Always verify manifest, regardless of source
+ final ZipEntry manifestEntry = jarFile.findEntry(
+ PackageParser.ANDROID_MANIFEST_FILENAME);
+ if (manifestEntry == null) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Package " + apkPath + " has no manifest");
+ }
+ lastCerts = loadCertificates(jarFile, manifestEntry);
+ if (ArrayUtils.isEmpty(lastCerts)) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Package "
+ + apkPath + " has no certificates at entry "
+ + PackageParser.ANDROID_MANIFEST_FILENAME);
+ }
+ lastSigs = convertToSignatures(lastCerts);
+
+ // don't waste time on already-trusted packages
+ if (!systemDir) {
+ final Iterator<ZipEntry> i = jarFile.iterator();
+ while (i.hasNext()) {
+ final ZipEntry entry = i.next();
+ if (entry.isDirectory()) continue;
+
+ final String entryName = entry.getName();
+ if (entryName.startsWith("META-INF/")) continue;
+ if (entryName.equals(PackageParser.ANDROID_MANIFEST_FILENAME)) continue;
+
+ toVerify.add(entry);
+ }
+
+ // Verify that entries are signed consistently with the first entry
+ // we encountered. Note that for splits, certificates may have
+ // already been populated during an earlier parse of a base APK.;
+ for (ZipEntry entry : toVerify) {
+ final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
+ if (ArrayUtils.isEmpty(entryCerts)) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ "Package " + apkPath + " has no certificates at entry "
+ + entry.getName());
+ }
+
+ // make sure all entries use the same signing certs
+ final Signature[] entrySigs = convertToSignatures(entryCerts);
+ if (!Signature.areExactMatch(lastSigs, entrySigs)) {
+ throw new PackageParserException(
+ INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+ "Package " + apkPath + " has mismatched certificates at entry "
+ + entry.getName());
+ }
+ }
+ }
+ return new Result(lastCerts, lastSigs, VERSION_JAR_SIGNATURE_SCHEME);
+ } catch (GeneralSecurityException e) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
+ "Failed to collect certificates from " + apkPath, e);
+ } catch (IOException | RuntimeException e) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ "Failed to collect certificates from " + apkPath, e);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ closeQuietly(jarFile);
+ }
+ }
+
+ private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry entry)
+ throws PackageParserException {
+ InputStream is = null;
+ try {
+ // We must read the stream for the JarEntry to retrieve
+ // its certificates.
+ is = jarFile.getInputStream(entry);
+ readFullyIgnoringContents(is);
+ return jarFile.getCertificateChains(entry);
+ } catch (IOException | RuntimeException e) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+ "Failed reading " + entry.getName() + " in " + jarFile, e);
+ } finally {
+ IoUtils.closeQuietly(is);
+ }
+ }
+
+ private static void readFullyIgnoringContents(InputStream in) throws IOException {
+ byte[] buffer = sBuffer.getAndSet(null);
+ if (buffer == null) {
+ buffer = new byte[4096];
+ }
+
+ int n = 0;
+ int count = 0;
+ while ((n = in.read(buffer, 0, buffer.length)) != -1) {
+ count += n;
+ }
+
+ sBuffer.set(buffer);
+ return;
+ }
+
+ /**
+ * Converts an array of certificate chains into the {@code Signature} equivalent used by the
+ * PackageManager.
+ *
+ * @throws CertificateEncodingException if it is unable to create a Signature object.
+ */
+ public static Signature[] convertToSignatures(Certificate[][] certs)
+ throws CertificateEncodingException {
+ final Signature[] res = new Signature[certs.length];
+ for (int i = 0; i < certs.length; i++) {
+ res[i] = new Signature(certs[i]);
+ }
+ return res;
+ }
+
+ private static void closeQuietly(StrictJarFile jarFile) {
+ if (jarFile != null) {
+ try {
+ jarFile.close();
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ /**
+ * Result of a successful APK verification operation.
+ */
+ public static class Result {
+ public final Certificate[][] certs;
+ public final Signature[] sigs;
+ public final int signatureSchemeVersion;
+
+ public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) {
+ this.certs = certs;
+ this.sigs = sigs;
+ this.signatureSchemeVersion = signingVersion;
+ }
+ }
+}
diff --git a/core/java/android/util/apk/SignatureNotFoundException.java b/core/java/android/util/apk/SignatureNotFoundException.java
new file mode 100644
index 0000000..9c7c760
--- /dev/null
+++ b/core/java/android/util/apk/SignatureNotFoundException.java
@@ -0,0 +1,34 @@
+/*
+ * 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.util.apk;
+
+/**
+ * Indicates that the APK is missing a signature.
+ *
+ * @hide
+ */
+public class SignatureNotFoundException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public SignatureNotFoundException(String message) {
+ super(message);
+ }
+
+ public SignatureNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 9673be0..5bd7446 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1323,10 +1323,10 @@
public static final int HDR_TYPE_HLG = 3;
/** @hide */
- @IntDef({
- HDR_TYPE_DOLBY_VISION,
- HDR_TYPE_HDR10,
- HDR_TYPE_HLG,
+ @IntDef(prefix = { "HDR_TYPE_" }, value = {
+ HDR_TYPE_DOLBY_VISION,
+ HDR_TYPE_HDR10,
+ HDR_TYPE_HLG,
})
@Retention(RetentionPolicy.SOURCE)
public @interface HdrType {}
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 19cd42e..e448f14 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -21,40 +21,37 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
-import android.annotation.NonNull;
+import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
-import java.util.ArrayList;
import java.util.List;
/**
* Represents a part of the display that is not functional for displaying content.
*
* <p>{@code DisplayCutout} is immutable.
- *
- * @hide will become API
*/
public final class DisplayCutout {
- private static final Rect ZERO_RECT = new Rect(0, 0, 0, 0);
- private static final ArrayList<Point> EMPTY_LIST = new ArrayList<>();
+ private static final Rect ZERO_RECT = new Rect();
+ private static final Region EMPTY_REGION = new Region();
/**
- * An instance where {@link #hasCutout()} returns {@code false}.
+ * An instance where {@link #isEmpty()} returns {@code true}.
*
* @hide
*/
- public static final DisplayCutout NO_CUTOUT =
- new DisplayCutout(ZERO_RECT, ZERO_RECT, EMPTY_LIST);
+ public static final DisplayCutout NO_CUTOUT = new DisplayCutout(ZERO_RECT, EMPTY_REGION);
private final Rect mSafeInsets;
- private final Rect mBoundingRect;
- private final List<Point> mBoundingPolygon;
+ private final Region mBounds;
/**
* Creates a DisplayCutout instance.
@@ -64,22 +61,18 @@
* @hide
*/
@VisibleForTesting
- public DisplayCutout(Rect safeInsets, Rect boundingRect, List<Point> boundingPolygon) {
+ public DisplayCutout(Rect safeInsets, Region bounds) {
mSafeInsets = safeInsets != null ? safeInsets : ZERO_RECT;
- mBoundingRect = boundingRect != null ? boundingRect : ZERO_RECT;
- mBoundingPolygon = boundingPolygon != null ? boundingPolygon : EMPTY_LIST;
+ mBounds = bounds != null ? bounds : Region.obtain();
}
/**
- * Returns whether there is a cutout.
+ * Returns true if there is no cutout or it is outside of the content view.
*
- * If false, the safe insets will all return zero, and the bounding box or polygon will be
- * empty or outside the content view.
- *
- * @return {@code true} if there is a cutout, {@code false} otherwise
+ * @hide
*/
- public boolean hasCutout() {
- return !mSafeInsets.equals(ZERO_RECT);
+ public boolean isEmpty() {
+ return mSafeInsets.equals(ZERO_RECT);
}
/** Returns the inset from the top which avoids the display cutout. */
@@ -103,44 +96,41 @@
}
/**
- * Obtains the safe insets in a rect.
+ * Returns the safe insets in a rect.
*
- * @param out a rect which is set to the safe insets.
+ * @return a rect which is set to the safe insets.
* @hide
*/
- public void getSafeInsets(@NonNull Rect out) {
- out.set(mSafeInsets);
+ public Rect getSafeInsets() {
+ return new Rect(mSafeInsets);
}
/**
- * Obtains the bounding rect of the cutout.
+ * Returns the bounding region of the cutout.
*
- * @param outRect is filled with the bounding rect of the cutout. Coordinates are relative
+ * @return the bounding region of the cutout. Coordinates are relative
* to the top-left corner of the content view.
*/
- public void getBoundingRect(@NonNull Rect outRect) {
- outRect.set(mBoundingRect);
+ public Region getBounds() {
+ return Region.obtain(mBounds);
}
/**
- * Obtains the bounding polygon of the cutout.
+ * Returns the bounding rect of the cutout.
*
- * @param outPolygon is filled with a list of points representing the corners of a convex
- * polygon which covers the cutout. Coordinates are relative to the
- * top-left corner of the content view.
+ * @return the bounding rect of the cutout. Coordinates are relative
+ * to the top-left corner of the content view.
+ * @hide
*/
- public void getBoundingPolygon(List<Point> outPolygon) {
- outPolygon.clear();
- for (int i = 0; i < mBoundingPolygon.size(); i++) {
- outPolygon.add(new Point(mBoundingPolygon.get(i)));
- }
+ public Rect getBoundingRect() {
+ // TODO(roosa): Inline.
+ return mBounds.getBounds();
}
@Override
public int hashCode() {
int result = mSafeInsets.hashCode();
- result = result * 31 + mBoundingRect.hashCode();
- result = result * 31 + mBoundingPolygon.hashCode();
+ result = result * 31 + mBounds.getBounds().hashCode();
return result;
}
@@ -152,8 +142,7 @@
if (o instanceof DisplayCutout) {
DisplayCutout c = (DisplayCutout) o;
return mSafeInsets.equals(c.mSafeInsets)
- && mBoundingRect.equals(c.mBoundingRect)
- && mBoundingPolygon.equals(c.mBoundingPolygon);
+ && mBounds.equals(c.mBounds);
}
return false;
}
@@ -161,7 +150,7 @@
@Override
public String toString() {
return "DisplayCutout{insets=" + mSafeInsets
- + " bounding=" + mBoundingRect
+ + " bounds=" + mBounds
+ "}";
}
@@ -172,15 +161,13 @@
* @hide
*/
public DisplayCutout inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
- if (mBoundingRect.isEmpty()
+ if (mBounds.isEmpty()
|| insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0) {
return this;
}
Rect safeInsets = new Rect(mSafeInsets);
- Rect boundingRect = new Rect(mBoundingRect);
- ArrayList<Point> boundingPolygon = new ArrayList<>();
- getBoundingPolygon(boundingPolygon);
+ Region bounds = Region.obtain(mBounds);
// Note: it's not really well defined what happens when the inset is negative, because we
// don't know if the safe inset needs to expand in general.
@@ -197,10 +184,9 @@
safeInsets.right = atLeastZero(safeInsets.right - insetRight);
}
- boundingRect.offset(-insetLeft, -insetTop);
- offset(boundingPolygon, -insetLeft, -insetTop);
+ bounds.translate(-insetLeft, -insetTop);
- return new DisplayCutout(safeInsets, boundingRect, boundingPolygon);
+ return new DisplayCutout(safeInsets, bounds);
}
/**
@@ -210,20 +196,17 @@
* @hide
*/
public DisplayCutout calculateRelativeTo(Rect frame) {
- if (mBoundingRect.isEmpty() || !Rect.intersects(frame, mBoundingRect)) {
+ if (mBounds.isEmpty() || !Rect.intersects(frame, mBounds.getBounds())) {
return NO_CUTOUT;
}
- Rect boundingRect = new Rect(mBoundingRect);
- ArrayList<Point> boundingPolygon = new ArrayList<>();
- getBoundingPolygon(boundingPolygon);
-
- return DisplayCutout.calculateRelativeTo(frame, boundingRect, boundingPolygon);
+ return DisplayCutout.calculateRelativeTo(frame, Region.obtain(mBounds));
}
- private static DisplayCutout calculateRelativeTo(Rect frame, Rect boundingRect,
- ArrayList<Point> boundingPolygon) {
+ private static DisplayCutout calculateRelativeTo(Rect frame, Region bounds) {
+ Rect boundingRect = bounds.getBounds();
Rect safeRect = new Rect();
+
int bestArea = 0;
int bestVariant = 0;
for (int variant = ROTATION_0; variant <= ROTATION_270; variant++) {
@@ -247,10 +230,9 @@
Math.max(0, frame.bottom - safeRect.bottom));
}
- boundingRect.offset(-frame.left, -frame.top);
- offset(boundingPolygon, -frame.left, -frame.top);
+ bounds.translate(-frame.left, -frame.top);
- return new DisplayCutout(safeRect, boundingRect, boundingPolygon);
+ return new DisplayCutout(safeRect, bounds);
}
private static int calculateInsetVariantArea(Rect frame, Rect boundingRect, int variant,
@@ -277,11 +259,6 @@
return value < 0 ? 0 : value;
}
- private static void offset(ArrayList<Point> points, int dx, int dy) {
- for (int i = 0; i < points.size(); i++) {
- points.get(i).offset(dx, dy);
- }
- }
/**
* Creates an instance from a bounding polygon.
@@ -289,20 +266,28 @@
* @hide
*/
public static DisplayCutout fromBoundingPolygon(List<Point> points) {
- Rect boundingRect = new Rect(Integer.MAX_VALUE, Integer.MAX_VALUE,
- Integer.MIN_VALUE, Integer.MIN_VALUE);
- ArrayList<Point> boundingPolygon = new ArrayList<>();
+ Region bounds = Region.obtain();
+ Path path = new Path();
+ path.reset();
for (int i = 0; i < points.size(); i++) {
Point point = points.get(i);
- boundingRect.left = Math.min(boundingRect.left, point.x);
- boundingRect.right = Math.max(boundingRect.right, point.x);
- boundingRect.top = Math.min(boundingRect.top, point.y);
- boundingRect.bottom = Math.max(boundingRect.bottom, point.y);
- boundingPolygon.add(new Point(point));
+ if (i == 0) {
+ path.moveTo(point.x, point.y);
+ } else {
+ path.lineTo(point.x, point.y);
+ }
}
+ path.close();
- return new DisplayCutout(ZERO_RECT, boundingRect, boundingPolygon);
+ RectF clipRect = new RectF();
+ path.computeBounds(clipRect, false /* unused */);
+ Region clipRegion = Region.obtain();
+ clipRegion.set((int) clipRect.left, (int) clipRect.top,
+ (int) clipRect.right, (int) clipRect.bottom);
+
+ bounds.setPath(path, clipRegion);
+ return new DisplayCutout(ZERO_RECT, bounds);
}
/**
@@ -336,8 +321,7 @@
} else {
out.writeInt(1);
out.writeTypedObject(mInner.mSafeInsets, flags);
- out.writeTypedObject(mInner.mBoundingRect, flags);
- out.writeTypedList(mInner.mBoundingPolygon, flags);
+ out.writeTypedObject(mInner.mBounds, flags);
}
}
@@ -368,13 +352,10 @@
return NO_CUTOUT;
}
- ArrayList<Point> boundingPolygon = new ArrayList<>();
-
Rect safeInsets = in.readTypedObject(Rect.CREATOR);
- Rect boundingRect = in.readTypedObject(Rect.CREATOR);
- in.readTypedList(boundingPolygon, Point.CREATOR);
+ Region bounds = in.readTypedObject(Region.CREATOR);
- return new DisplayCutout(safeInsets, boundingRect, boundingPolygon);
+ return new DisplayCutout(safeInsets, bounds);
}
public DisplayCutout get() {
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 52e53b0..bc2953e 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -520,162 +520,163 @@
boolean handled = false;
switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_POINTER_DOWN:
- mDownFocusX = mLastFocusX = focusX;
- mDownFocusY = mLastFocusY = focusY;
- // Cancel long press and taps
- cancelTaps();
- break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ mDownFocusX = mLastFocusX = focusX;
+ mDownFocusY = mLastFocusY = focusY;
+ // Cancel long press and taps
+ cancelTaps();
+ break;
- case MotionEvent.ACTION_POINTER_UP:
- mDownFocusX = mLastFocusX = focusX;
- mDownFocusY = mLastFocusY = focusY;
+ case MotionEvent.ACTION_POINTER_UP:
+ mDownFocusX = mLastFocusX = focusX;
+ mDownFocusY = mLastFocusY = focusY;
- // Check the dot product of current velocities.
- // If the pointer that left was opposing another velocity vector, clear.
- mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
- final int upIndex = ev.getActionIndex();
- final int id1 = ev.getPointerId(upIndex);
- final float x1 = mVelocityTracker.getXVelocity(id1);
- final float y1 = mVelocityTracker.getYVelocity(id1);
- for (int i = 0; i < count; i++) {
- if (i == upIndex) continue;
+ // Check the dot product of current velocities.
+ // If the pointer that left was opposing another velocity vector, clear.
+ mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
+ final int upIndex = ev.getActionIndex();
+ final int id1 = ev.getPointerId(upIndex);
+ final float x1 = mVelocityTracker.getXVelocity(id1);
+ final float y1 = mVelocityTracker.getYVelocity(id1);
+ for (int i = 0; i < count; i++) {
+ if (i == upIndex) continue;
- final int id2 = ev.getPointerId(i);
- final float x = x1 * mVelocityTracker.getXVelocity(id2);
- final float y = y1 * mVelocityTracker.getYVelocity(id2);
+ final int id2 = ev.getPointerId(i);
+ final float x = x1 * mVelocityTracker.getXVelocity(id2);
+ final float y = y1 * mVelocityTracker.getYVelocity(id2);
- final float dot = x + y;
- if (dot < 0) {
- mVelocityTracker.clear();
+ final float dot = x + y;
+ if (dot < 0) {
+ mVelocityTracker.clear();
+ break;
+ }
+ }
+ break;
+
+ case MotionEvent.ACTION_DOWN:
+ if (mDoubleTapListener != null) {
+ boolean hadTapMessage = mHandler.hasMessages(TAP);
+ if (hadTapMessage) mHandler.removeMessages(TAP);
+ if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null)
+ && hadTapMessage
+ && isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
+ // This is a second tap
+ mIsDoubleTapping = true;
+ // Give a callback with the first tap of the double-tap
+ handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
+ // Give a callback with down event of the double-tap
+ handled |= mDoubleTapListener.onDoubleTapEvent(ev);
+ } else {
+ // This is a first tap
+ mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
+ }
+ }
+
+ mDownFocusX = mLastFocusX = focusX;
+ mDownFocusY = mLastFocusY = focusY;
+ if (mCurrentDownEvent != null) {
+ mCurrentDownEvent.recycle();
+ }
+ mCurrentDownEvent = MotionEvent.obtain(ev);
+ mAlwaysInTapRegion = true;
+ mAlwaysInBiggerTapRegion = true;
+ mStillDown = true;
+ mInLongPress = false;
+ mDeferConfirmSingleTap = false;
+
+ if (mIsLongpressEnabled) {
+ mHandler.removeMessages(LONG_PRESS);
+ mHandler.sendEmptyMessageAtTime(LONG_PRESS,
+ mCurrentDownEvent.getDownTime() + LONGPRESS_TIMEOUT);
+ }
+ mHandler.sendEmptyMessageAtTime(SHOW_PRESS,
+ mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
+ handled |= mListener.onDown(ev);
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (mInLongPress || mInContextClick) {
break;
}
- }
- break;
-
- case MotionEvent.ACTION_DOWN:
- if (mDoubleTapListener != null) {
- boolean hadTapMessage = mHandler.hasMessages(TAP);
- if (hadTapMessage) mHandler.removeMessages(TAP);
- if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
- isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
- // This is a second tap
- mIsDoubleTapping = true;
- // Give a callback with the first tap of the double-tap
- handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
- // Give a callback with down event of the double-tap
+ final float scrollX = mLastFocusX - focusX;
+ final float scrollY = mLastFocusY - focusY;
+ if (mIsDoubleTapping) {
+ // Give the move events of the double-tap
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
- } else {
- // This is a first tap
- mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
- }
- }
-
- mDownFocusX = mLastFocusX = focusX;
- mDownFocusY = mLastFocusY = focusY;
- if (mCurrentDownEvent != null) {
- mCurrentDownEvent.recycle();
- }
- mCurrentDownEvent = MotionEvent.obtain(ev);
- mAlwaysInTapRegion = true;
- mAlwaysInBiggerTapRegion = true;
- mStillDown = true;
- mInLongPress = false;
- mDeferConfirmSingleTap = false;
-
- if (mIsLongpressEnabled) {
- mHandler.removeMessages(LONG_PRESS);
- mHandler.sendEmptyMessageAtTime(LONG_PRESS,
- mCurrentDownEvent.getDownTime() + LONGPRESS_TIMEOUT);
- }
- mHandler.sendEmptyMessageAtTime(SHOW_PRESS,
- mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
- handled |= mListener.onDown(ev);
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (mInLongPress || mInContextClick) {
- break;
- }
- final float scrollX = mLastFocusX - focusX;
- final float scrollY = mLastFocusY - focusY;
- if (mIsDoubleTapping) {
- // Give the move events of the double-tap
- handled |= mDoubleTapListener.onDoubleTapEvent(ev);
- } else if (mAlwaysInTapRegion) {
- final int deltaX = (int) (focusX - mDownFocusX);
- final int deltaY = (int) (focusY - mDownFocusY);
- int distance = (deltaX * deltaX) + (deltaY * deltaY);
- int slopSquare = isGeneratedGesture ? 0 : mTouchSlopSquare;
- if (distance > slopSquare) {
+ } else if (mAlwaysInTapRegion) {
+ final int deltaX = (int) (focusX - mDownFocusX);
+ final int deltaY = (int) (focusY - mDownFocusY);
+ int distance = (deltaX * deltaX) + (deltaY * deltaY);
+ int slopSquare = isGeneratedGesture ? 0 : mTouchSlopSquare;
+ if (distance > slopSquare) {
+ handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
+ mLastFocusX = focusX;
+ mLastFocusY = focusY;
+ mAlwaysInTapRegion = false;
+ mHandler.removeMessages(TAP);
+ mHandler.removeMessages(SHOW_PRESS);
+ mHandler.removeMessages(LONG_PRESS);
+ }
+ int doubleTapSlopSquare = isGeneratedGesture ? 0 : mDoubleTapTouchSlopSquare;
+ if (distance > doubleTapSlopSquare) {
+ mAlwaysInBiggerTapRegion = false;
+ }
+ } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mLastFocusX = focusX;
mLastFocusY = focusY;
- mAlwaysInTapRegion = false;
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ mStillDown = false;
+ MotionEvent currentUpEvent = MotionEvent.obtain(ev);
+ if (mIsDoubleTapping) {
+ // Finally, give the up event of the double-tap
+ handled |= mDoubleTapListener.onDoubleTapEvent(ev);
+ } else if (mInLongPress) {
mHandler.removeMessages(TAP);
- mHandler.removeMessages(SHOW_PRESS);
- mHandler.removeMessages(LONG_PRESS);
- }
- int doubleTapSlopSquare = isGeneratedGesture ? 0 : mDoubleTapTouchSlopSquare;
- if (distance > doubleTapSlopSquare) {
- mAlwaysInBiggerTapRegion = false;
- }
- } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
- handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
- mLastFocusX = focusX;
- mLastFocusY = focusY;
- }
- break;
+ mInLongPress = false;
+ } else if (mAlwaysInTapRegion && !mIgnoreNextUpEvent) {
+ handled = mListener.onSingleTapUp(ev);
+ if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
+ mDoubleTapListener.onSingleTapConfirmed(ev);
+ }
+ } else if (!mIgnoreNextUpEvent) {
- case MotionEvent.ACTION_UP:
- mStillDown = false;
- MotionEvent currentUpEvent = MotionEvent.obtain(ev);
- if (mIsDoubleTapping) {
- // Finally, give the up event of the double-tap
- handled |= mDoubleTapListener.onDoubleTapEvent(ev);
- } else if (mInLongPress) {
- mHandler.removeMessages(TAP);
- mInLongPress = false;
- } else if (mAlwaysInTapRegion && !mIgnoreNextUpEvent) {
- handled = mListener.onSingleTapUp(ev);
- if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
- mDoubleTapListener.onSingleTapConfirmed(ev);
+ // A fling must travel the minimum tap distance
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ final int pointerId = ev.getPointerId(0);
+ velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
+ final float velocityY = velocityTracker.getYVelocity(pointerId);
+ final float velocityX = velocityTracker.getXVelocity(pointerId);
+
+ if ((Math.abs(velocityY) > mMinimumFlingVelocity)
+ || (Math.abs(velocityX) > mMinimumFlingVelocity)) {
+ handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);
+ }
}
- } else if (!mIgnoreNextUpEvent) {
-
- // A fling must travel the minimum tap distance
- final VelocityTracker velocityTracker = mVelocityTracker;
- final int pointerId = ev.getPointerId(0);
- velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
- final float velocityY = velocityTracker.getYVelocity(pointerId);
- final float velocityX = velocityTracker.getXVelocity(pointerId);
-
- if ((Math.abs(velocityY) > mMinimumFlingVelocity)
- || (Math.abs(velocityX) > mMinimumFlingVelocity)){
- handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);
+ if (mPreviousUpEvent != null) {
+ mPreviousUpEvent.recycle();
}
- }
- if (mPreviousUpEvent != null) {
- mPreviousUpEvent.recycle();
- }
- // Hold the event we obtained above - listeners may have changed the original.
- mPreviousUpEvent = currentUpEvent;
- if (mVelocityTracker != null) {
- // This may have been cleared when we called out to the
- // application above.
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- mIsDoubleTapping = false;
- mDeferConfirmSingleTap = false;
- mIgnoreNextUpEvent = false;
- mHandler.removeMessages(SHOW_PRESS);
- mHandler.removeMessages(LONG_PRESS);
- break;
+ // Hold the event we obtained above - listeners may have changed the original.
+ mPreviousUpEvent = currentUpEvent;
+ if (mVelocityTracker != null) {
+ // This may have been cleared when we called out to the
+ // application above.
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ mIsDoubleTapping = false;
+ mDeferConfirmSingleTap = false;
+ mIgnoreNextUpEvent = false;
+ mHandler.removeMessages(SHOW_PRESS);
+ mHandler.removeMessages(LONG_PRESS);
+ break;
- case MotionEvent.ACTION_CANCEL:
- cancel();
- break;
+ case MotionEvent.ACTION_CANCEL:
+ cancel();
+ break;
}
if (!handled && mInputEventConsistencyVerifier != null) {
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index ddced6c..a417a4a 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -121,8 +121,12 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({SCALING_MODE_FREEZE, SCALING_MODE_SCALE_TO_WINDOW,
- SCALING_MODE_SCALE_CROP, SCALING_MODE_NO_SCALE_CROP})
+ @IntDef(prefix = { "SCALING_MODE_" }, value = {
+ SCALING_MODE_FREEZE,
+ SCALING_MODE_SCALE_TO_WINDOW,
+ SCALING_MODE_SCALE_CROP,
+ SCALING_MODE_NO_SCALE_CROP
+ })
public @interface ScalingMode {}
// From system/window.h
/** @hide */
@@ -135,7 +139,12 @@
public static final int SCALING_MODE_NO_SCALE_CROP = 3;
/** @hide */
- @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
+ @IntDef(prefix = { "ROTATION_" }, value = {
+ ROTATION_0,
+ ROTATION_90,
+ ROTATION_180,
+ ROTATION_270
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface Rotation {}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 8a7b441..268e460 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -30,6 +30,7 @@
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.IBinder;
@@ -37,8 +38,12 @@
import android.os.Parcelable;
import android.os.Process;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.Log;
import android.view.Surface.OutOfResourcesException;
+
+import com.android.internal.annotations.GuardedBy;
+
import dalvik.system.CloseGuard;
import libcore.util.NativeAllocationRegistry;
@@ -153,6 +158,13 @@
private final String mName;
long mNativeObject; // package visibility only for Surface.java access
+ // TODO: Move this to native.
+ private final Object mSizeLock = new Object();
+ @GuardedBy("mSizeLock")
+ private int mWidth;
+ @GuardedBy("mSizeLock")
+ private int mHeight;
+
static Transaction sGlobalTransaction;
static long sTransactionNestCount = 0;
@@ -567,6 +579,8 @@
}
mName = name;
+ mWidth = w;
+ mHeight = h;
mNativeObject = nativeCreate(session, name, w, h, format, flags,
parent != null ? parent.mNativeObject : 0, windowType, ownerUid);
if (mNativeObject == 0) {
@@ -582,6 +596,8 @@
// event logging.
public SurfaceControl(SurfaceControl other) {
mName = other.mName;
+ mWidth = other.mWidth;
+ mHeight = other.mHeight;
mNativeObject = other.mNativeObject;
other.mCloseGuard.close();
other.mNativeObject = 0;
@@ -590,6 +606,8 @@
private SurfaceControl(Parcel in) {
mName = in.readString();
+ mWidth = in.readInt();
+ mHeight = in.readInt();
mNativeObject = nativeReadFromParcel(in);
if (mNativeObject == 0) {
throw new IllegalArgumentException("Couldn't read SurfaceControl from parcel=" + in);
@@ -605,6 +623,8 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mName);
+ dest.writeInt(mWidth);
+ dest.writeInt(mHeight);
nativeWriteToParcel(mNativeObject, dest);
}
@@ -922,6 +942,18 @@
}
}
+ public int getWidth() {
+ synchronized (mSizeLock) {
+ return mWidth;
+ }
+ }
+
+ public int getHeight() {
+ synchronized (mSizeLock) {
+ return mHeight;
+ }
+ }
+
@Override
public String toString() {
return "Surface(name=" + mName + ")/@0x" +
@@ -1149,7 +1181,9 @@
}
/**
- * Copy the current screen contents into a bitmap and return it.
+ * Copy the current screen contents into a hardware bitmap and return it.
+ * Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap into
+ * a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)}
*
* CAVEAT: Versions of screenshot that return a {@link Bitmap} can
* be extremely slow; avoid use unless absolutely necessary; prefer
@@ -1174,7 +1208,7 @@
* screenshots in its native portrait orientation by default, so this is
* useful for returning screenshots that are independent of device
* orientation.
- * @return Returns a Bitmap containing the screen contents, or null
+ * @return Returns a hardware Bitmap containing the screen contents, or null
* if an error occurs. Make sure to call Bitmap.recycle() as soon as
* possible, once its content is not needed anymore.
*/
@@ -1202,7 +1236,7 @@
}
/**
- * Like {@link SurfaceControl#screenshot(int, int, int, int, boolean)} but
+ * Like {@link SurfaceControl#screenshot(Rect, int, int, int, int, boolean, int)} but
* includes all Surfaces in the screenshot. This will also update the orientation so it
* sends the correct coordinates to SF based on the rotation value.
*
@@ -1259,7 +1293,7 @@
}
/**
- * Captures a layer and its children into the provided {@link Surface}.
+ * Captures a layer and its children and returns a {@link GraphicBuffer} with the content.
*
* @param layerHandleToken The root layer to capture.
* @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
@@ -1280,6 +1314,7 @@
nativeGetNativeTransactionFinalizer(), 512);
private long mNativeObject;
+ private final ArrayMap<SurfaceControl, Point> mResizedSurfaces = new ArrayMap<>();
Runnable mFreeNativeResources;
public Transaction() {
@@ -1310,9 +1345,22 @@
* Jankier version of apply. Avoid use (b/28068298).
*/
public void apply(boolean sync) {
+ applyResizedSurfaces();
nativeApplyTransaction(mNativeObject, sync);
}
+ private void applyResizedSurfaces() {
+ for (int i = mResizedSurfaces.size() - 1; i >= 0; i--) {
+ final Point size = mResizedSurfaces.valueAt(i);
+ final SurfaceControl surfaceControl = mResizedSurfaces.keyAt(i);
+ synchronized (surfaceControl.mSizeLock) {
+ surfaceControl.mWidth = size.x;
+ surfaceControl.mHeight = size.y;
+ }
+ }
+ mResizedSurfaces.clear();
+ }
+
public Transaction show(SurfaceControl sc) {
sc.checkNotReleased();
nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SURFACE_HIDDEN);
@@ -1333,6 +1381,7 @@
public Transaction setSize(SurfaceControl sc, int w, int h) {
sc.checkNotReleased();
+ mResizedSurfaces.put(sc, new Point(w, h));
nativeSetSize(mNativeObject, sc.mNativeObject, w, h);
return this;
}
@@ -1565,6 +1614,8 @@
* other transaction as if it had been applied.
*/
public Transaction merge(Transaction other) {
+ mResizedSurfaces.putAll(other.mResizedSurfaces);
+ other.mResizedSurfaces.clear();
nativeMergeTransaction(mNativeObject, other.mNativeObject);
return this;
}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 0a69772..6a8f8b1 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -344,8 +344,10 @@
private static final int FLAG_DUMP_FRAMESTATS = 1 << 0;
private static final int FLAG_DUMP_RESET = 1 << 1;
- @IntDef(flag = true, value = {
- FLAG_DUMP_FRAMESTATS, FLAG_DUMP_RESET })
+ @IntDef(flag = true, prefix = { "FLAG_DUMP_" }, value = {
+ FLAG_DUMP_FRAMESTATS,
+ FLAG_DUMP_RESET
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface DumpFlags {}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 269b4e3..cc63a62 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1178,7 +1178,7 @@
private AutofillId mAutofillId;
/** @hide */
- @IntDef({
+ @IntDef(prefix = { "AUTOFILL_TYPE_" }, value = {
AUTOFILL_TYPE_NONE,
AUTOFILL_TYPE_TEXT,
AUTOFILL_TYPE_TOGGLE,
@@ -1249,7 +1249,7 @@
public static final int AUTOFILL_TYPE_DATE = 4;
/** @hide */
- @IntDef({
+ @IntDef(prefix = { "IMPORTANT_FOR_AUTOFILL_" }, value = {
IMPORTANT_FOR_AUTOFILL_AUTO,
IMPORTANT_FOR_AUTOFILL_YES,
IMPORTANT_FOR_AUTOFILL_NO,
@@ -1300,9 +1300,9 @@
public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 0x8;
/** @hide */
- @IntDef(
- flag = true,
- value = {AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS})
+ @IntDef(flag = true, prefix = { "AUTOFILL_FLAG_" }, value = {
+ AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface AutofillFlags {}
@@ -1452,7 +1452,11 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({DRAWING_CACHE_QUALITY_LOW, DRAWING_CACHE_QUALITY_HIGH, DRAWING_CACHE_QUALITY_AUTO})
+ @IntDef(prefix = { "DRAWING_CACHE_QUALITY_" }, value = {
+ DRAWING_CACHE_QUALITY_LOW,
+ DRAWING_CACHE_QUALITY_HIGH,
+ DRAWING_CACHE_QUALITY_AUTO
+ })
public @interface DrawingCacheQuality {}
/**
@@ -1551,13 +1555,12 @@
*/
static final int CONTEXT_CLICKABLE = 0x00800000;
-
/** @hide */
- @IntDef({
- SCROLLBARS_INSIDE_OVERLAY,
- SCROLLBARS_INSIDE_INSET,
- SCROLLBARS_OUTSIDE_OVERLAY,
- SCROLLBARS_OUTSIDE_INSET
+ @IntDef(prefix = { "SCROLLBARS_" }, value = {
+ SCROLLBARS_INSIDE_OVERLAY,
+ SCROLLBARS_INSIDE_INSET,
+ SCROLLBARS_OUTSIDE_OVERLAY,
+ SCROLLBARS_OUTSIDE_INSET
})
@Retention(RetentionPolicy.SOURCE)
public @interface ScrollBarStyle {}
@@ -1651,11 +1654,10 @@
static final int TOOLTIP = 0x40000000;
/** @hide */
- @IntDef(flag = true,
- value = {
- FOCUSABLES_ALL,
- FOCUSABLES_TOUCH_MODE
- })
+ @IntDef(flag = true, prefix = { "FOCUSABLES_" }, value = {
+ FOCUSABLES_ALL,
+ FOCUSABLES_TOUCH_MODE
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface FocusableMode {}
@@ -1672,7 +1674,7 @@
public static final int FOCUSABLES_TOUCH_MODE = 0x00000001;
/** @hide */
- @IntDef({
+ @IntDef(prefix = { "FOCUS_" }, value = {
FOCUS_BACKWARD,
FOCUS_FORWARD,
FOCUS_LEFT,
@@ -1684,7 +1686,7 @@
public @interface FocusDirection {}
/** @hide */
- @IntDef({
+ @IntDef(prefix = { "FOCUS_" }, value = {
FOCUS_LEFT,
FOCUS_UP,
FOCUS_RIGHT,
@@ -2426,20 +2428,20 @@
static final int PFLAG2_DRAG_HOVERED = 0x00000002;
/** @hide */
- @IntDef({
- LAYOUT_DIRECTION_LTR,
- LAYOUT_DIRECTION_RTL,
- LAYOUT_DIRECTION_INHERIT,
- LAYOUT_DIRECTION_LOCALE
+ @IntDef(prefix = { "LAYOUT_DIRECTION_" }, value = {
+ LAYOUT_DIRECTION_LTR,
+ LAYOUT_DIRECTION_RTL,
+ LAYOUT_DIRECTION_INHERIT,
+ LAYOUT_DIRECTION_LOCALE
})
@Retention(RetentionPolicy.SOURCE)
// Not called LayoutDirection to avoid conflict with android.util.LayoutDirection
public @interface LayoutDir {}
/** @hide */
- @IntDef({
- LAYOUT_DIRECTION_LTR,
- LAYOUT_DIRECTION_RTL
+ @IntDef(prefix = { "LAYOUT_DIRECTION_" }, value = {
+ LAYOUT_DIRECTION_LTR,
+ LAYOUT_DIRECTION_RTL
})
@Retention(RetentionPolicy.SOURCE)
public @interface ResolvedLayoutDir {}
@@ -2645,14 +2647,14 @@
TEXT_DIRECTION_RESOLVED_DEFAULT << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT;
/** @hide */
- @IntDef({
- TEXT_ALIGNMENT_INHERIT,
- TEXT_ALIGNMENT_GRAVITY,
- TEXT_ALIGNMENT_CENTER,
- TEXT_ALIGNMENT_TEXT_START,
- TEXT_ALIGNMENT_TEXT_END,
- TEXT_ALIGNMENT_VIEW_START,
- TEXT_ALIGNMENT_VIEW_END
+ @IntDef(prefix = { "TEXT_ALIGNMENT_" }, value = {
+ TEXT_ALIGNMENT_INHERIT,
+ TEXT_ALIGNMENT_GRAVITY,
+ TEXT_ALIGNMENT_CENTER,
+ TEXT_ALIGNMENT_TEXT_START,
+ TEXT_ALIGNMENT_TEXT_END,
+ TEXT_ALIGNMENT_VIEW_START,
+ TEXT_ALIGNMENT_VIEW_END
})
@Retention(RetentionPolicy.SOURCE)
public @interface TextAlignment {}
@@ -3049,15 +3051,14 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- value = {
- SCROLL_INDICATOR_TOP,
- SCROLL_INDICATOR_BOTTOM,
- SCROLL_INDICATOR_LEFT,
- SCROLL_INDICATOR_RIGHT,
- SCROLL_INDICATOR_START,
- SCROLL_INDICATOR_END,
- })
+ @IntDef(flag = true, prefix = { "SCROLL_INDICATOR_" }, value = {
+ SCROLL_INDICATOR_TOP,
+ SCROLL_INDICATOR_BOTTOM,
+ SCROLL_INDICATOR_LEFT,
+ SCROLL_INDICATOR_RIGHT,
+ SCROLL_INDICATOR_START,
+ SCROLL_INDICATOR_END,
+ })
public @interface ScrollIndicators {}
/**
@@ -3683,8 +3684,10 @@
| SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
/** @hide */
- @IntDef(flag = true,
- value = { FIND_VIEWS_WITH_TEXT, FIND_VIEWS_WITH_CONTENT_DESCRIPTION })
+ @IntDef(flag = true, prefix = { "FIND_VIEWS_" }, value = {
+ FIND_VIEWS_WITH_TEXT,
+ FIND_VIEWS_WITH_CONTENT_DESCRIPTION
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface FindViewFlags {}
@@ -4296,6 +4299,38 @@
*/
Runnable mShowTooltipRunnable;
Runnable mHideTooltipRunnable;
+
+ /**
+ * Hover move is ignored if it is within this distance in pixels from the previous one.
+ */
+ int mHoverSlop;
+
+ /**
+ * Update the anchor position if it significantly (that is by at least mHoverSlop)
+ * different from the previously stored position. Ignoring insignificant changes
+ * filters out the jitter which is typical for such input sources as stylus.
+ *
+ * @return True if the position has been updated.
+ */
+ private boolean updateAnchorPos(MotionEvent event) {
+ final int newAnchorX = (int) event.getX();
+ final int newAnchorY = (int) event.getY();
+ if (Math.abs(newAnchorX - mAnchorX) <= mHoverSlop
+ && Math.abs(newAnchorY - mAnchorY) <= mHoverSlop) {
+ return false;
+ }
+ mAnchorX = newAnchorX;
+ mAnchorY = newAnchorY;
+ return true;
+ }
+
+ /**
+ * Clear the anchor position to ensure that the next change is considered significant.
+ */
+ private void clearAnchorPos() {
+ mAnchorX = Integer.MAX_VALUE;
+ mAnchorY = Integer.MAX_VALUE;
+ }
}
TooltipInfo mTooltipInfo;
@@ -26813,6 +26848,8 @@
mTooltipInfo = new TooltipInfo();
mTooltipInfo.mShowTooltipRunnable = this::showHoverTooltip;
mTooltipInfo.mHideTooltipRunnable = this::hideTooltip;
+ mTooltipInfo.mHoverSlop = ViewConfiguration.get(mContext).getScaledHoverSlop();
+ mTooltipInfo.clearAnchorPos();
}
mTooltipInfo.mTooltipText = tooltipText;
}
@@ -26879,6 +26916,7 @@
mTooltipInfo.mTooltipPopup.hide();
mTooltipInfo.mTooltipPopup = null;
mTooltipInfo.mTooltipFromLongClick = false;
+ mTooltipInfo.clearAnchorPos();
if (mAttachInfo != null) {
mAttachInfo.mTooltipHost = null;
}
@@ -26903,11 +26941,9 @@
if ((mViewFlags & TOOLTIP) != TOOLTIP) {
break;
}
- if (!mTooltipInfo.mTooltipFromLongClick) {
+ if (!mTooltipInfo.mTooltipFromLongClick && mTooltipInfo.updateAnchorPos(event)) {
if (mTooltipInfo.mTooltipPopup == null) {
// Schedule showing the tooltip after a timeout.
- mTooltipInfo.mAnchorX = (int) event.getX();
- mTooltipInfo.mAnchorY = (int) event.getY();
removeCallbacks(mTooltipInfo.mShowTooltipRunnable);
postDelayed(mTooltipInfo.mShowTooltipRunnable,
ViewConfiguration.getHoverTooltipShowTimeout());
@@ -26929,6 +26965,7 @@
return true;
case MotionEvent.ACTION_HOVER_EXIT:
+ mTooltipInfo.clearAnchorPos();
if (!mTooltipInfo.mTooltipFromLongClick) {
hideTooltip();
}
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index c44c8dd..c5a94da 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -290,6 +290,7 @@
private final int mMaximumFlingVelocity;
private final int mScrollbarSize;
private final int mTouchSlop;
+ private final int mHoverSlop;
private final int mMinScrollbarTouchTarget;
private final int mDoubleTapTouchSlop;
private final int mPagingTouchSlop;
@@ -320,6 +321,7 @@
mMaximumFlingVelocity = MAXIMUM_FLING_VELOCITY;
mScrollbarSize = SCROLL_BAR_SIZE;
mTouchSlop = TOUCH_SLOP;
+ mHoverSlop = TOUCH_SLOP / 2;
mMinScrollbarTouchTarget = MIN_SCROLLBAR_TOUCH_TARGET;
mDoubleTapTouchSlop = DOUBLE_TAP_TOUCH_SLOP;
mPagingTouchSlop = PAGING_TOUCH_SLOP;
@@ -407,6 +409,8 @@
com.android.internal.R.bool.config_ui_enableFadingMarquee);
mTouchSlop = res.getDimensionPixelSize(
com.android.internal.R.dimen.config_viewConfigurationTouchSlop);
+ mHoverSlop = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.config_viewConfigurationHoverSlop);
mMinScrollbarTouchTarget = res.getDimensionPixelSize(
com.android.internal.R.dimen.config_minScrollbarTouchTarget);
mPagingTouchSlop = mTouchSlop * 2;
@@ -640,6 +644,14 @@
}
/**
+ * @return Distance in pixels a hover can wander while it is still considered "stationary".
+ *
+ */
+ public int getScaledHoverSlop() {
+ return mHoverSlop;
+ }
+
+ /**
* @return Distance in pixels the first touch can wander before we do not consider this a
* potential double tap event
* @hide
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 129a255..6c5091c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -20,6 +20,7 @@
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
+import static android.view.WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
@@ -323,6 +324,15 @@
final Rect mTempRect; // used in the transaction to not thrash the heap.
final Rect mVisRect; // used to retrieve visible rect of focused view.
+ // This is used to reduce the race between window focus changes being dispatched from
+ // the window manager and input events coming through the input system.
+ @GuardedBy("this")
+ boolean mWindowFocusChanged;
+ @GuardedBy("this")
+ boolean mUpcomingWindowFocus;
+ @GuardedBy("this")
+ boolean mUpcomingInTouchMode;
+
public boolean mTraversalScheduled;
int mTraversalBarrier;
boolean mWillDrawSoon;
@@ -1586,7 +1596,15 @@
}
void dispatchApplyInsets(View host) {
- host.dispatchApplyWindowInsets(getWindowInsets(true /* forceConstruct */));
+ WindowInsets insets = getWindowInsets(true /* forceConstruct */);
+ final boolean layoutInCutout =
+ (mWindowAttributes.flags2 & FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA) != 0;
+ if (!layoutInCutout) {
+ // Window is either not laid out in cutout or the status bar inset takes care of
+ // clearing the cutout, so we don't need to dispatch the cutout to the hierarchy.
+ insets = insets.consumeDisplayCutout();
+ }
+ host.dispatchApplyWindowInsets(insets);
}
private static boolean shouldUseDisplaySize(final WindowManager.LayoutParams lp) {
@@ -2452,6 +2470,93 @@
}
}
+ private void handleWindowFocusChanged() {
+ final boolean hasWindowFocus;
+ final boolean inTouchMode;
+ synchronized (this) {
+ if (!mWindowFocusChanged) {
+ return;
+ }
+ mWindowFocusChanged = false;
+ hasWindowFocus = mUpcomingWindowFocus;
+ inTouchMode = mUpcomingInTouchMode;
+ }
+
+ if (mAdded) {
+ profileRendering(hasWindowFocus);
+
+ if (hasWindowFocus) {
+ ensureTouchModeLocally(inTouchMode);
+ if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
+ mFullRedrawNeeded = true;
+ try {
+ final WindowManager.LayoutParams lp = mWindowAttributes;
+ final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null;
+ mAttachInfo.mThreadedRenderer.initializeIfNeeded(
+ mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
+ } catch (OutOfResourcesException e) {
+ Log.e(mTag, "OutOfResourcesException locking surface", e);
+ try {
+ if (!mWindowSession.outOfMemory(mWindow)) {
+ Slog.w(mTag, "No processes killed for memory;"
+ + " killing self");
+ Process.killProcess(Process.myPid());
+ }
+ } catch (RemoteException ex) {
+ }
+ // Retry in a bit.
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ MSG_WINDOW_FOCUS_CHANGED), 500);
+ return;
+ }
+ }
+ }
+
+ mAttachInfo.mHasWindowFocus = hasWindowFocus;
+
+ mLastWasImTarget = WindowManager.LayoutParams
+ .mayUseInputMethod(mWindowAttributes.flags);
+
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
+ imm.onPreWindowFocus(mView, hasWindowFocus);
+ }
+ if (mView != null) {
+ mAttachInfo.mKeyDispatchState.reset();
+ mView.dispatchWindowFocusChanged(hasWindowFocus);
+ mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
+
+ if (mAttachInfo.mTooltipHost != null) {
+ mAttachInfo.mTooltipHost.hideTooltip();
+ }
+ }
+
+ // Note: must be done after the focus change callbacks,
+ // so all of the view state is set up correctly.
+ if (hasWindowFocus) {
+ if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
+ imm.onPostWindowFocus(mView, mView.findFocus(),
+ mWindowAttributes.softInputMode,
+ !mHasHadWindowFocus, mWindowAttributes.flags);
+ }
+ // Clear the forward bit. We can just do this directly, since
+ // the window manager doesn't care about it.
+ mWindowAttributes.softInputMode &=
+ ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
+ ((WindowManager.LayoutParams) mView.getLayoutParams())
+ .softInputMode &=
+ ~WindowManager.LayoutParams
+ .SOFT_INPUT_IS_FORWARD_NAVIGATION;
+ mHasHadWindowFocus = true;
+ } else {
+ if (mPointerCapture) {
+ handlePointerCaptureChanged(false);
+ }
+ }
+ }
+ mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
+ }
+
private void handleOutOfResourcesException(Surface.OutOfResourcesException e) {
Log.e(mTag, "OutOfResourcesException initializing HW surface", e);
try {
@@ -3900,81 +4005,7 @@
}
break;
case MSG_WINDOW_FOCUS_CHANGED: {
- final boolean hasWindowFocus = msg.arg1 != 0;
- if (mAdded) {
- mAttachInfo.mHasWindowFocus = hasWindowFocus;
-
- profileRendering(hasWindowFocus);
-
- if (hasWindowFocus) {
- boolean inTouchMode = msg.arg2 != 0;
- ensureTouchModeLocally(inTouchMode);
- if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
- mFullRedrawNeeded = true;
- try {
- final WindowManager.LayoutParams lp = mWindowAttributes;
- final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null;
- mAttachInfo.mThreadedRenderer.initializeIfNeeded(
- mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
- } catch (OutOfResourcesException e) {
- Log.e(mTag, "OutOfResourcesException locking surface", e);
- try {
- if (!mWindowSession.outOfMemory(mWindow)) {
- Slog.w(mTag, "No processes killed for memory;"
- + " killing self");
- Process.killProcess(Process.myPid());
- }
- } catch (RemoteException ex) {
- }
- // Retry in a bit.
- sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2),
- 500);
- return;
- }
- }
- }
-
- mLastWasImTarget = WindowManager.LayoutParams
- .mayUseInputMethod(mWindowAttributes.flags);
-
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
- imm.onPreWindowFocus(mView, hasWindowFocus);
- }
- if (mView != null) {
- mAttachInfo.mKeyDispatchState.reset();
- mView.dispatchWindowFocusChanged(hasWindowFocus);
- mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
-
- if (mAttachInfo.mTooltipHost != null) {
- mAttachInfo.mTooltipHost.hideTooltip();
- }
- }
-
- // Note: must be done after the focus change callbacks,
- // so all of the view state is set up correctly.
- if (hasWindowFocus) {
- if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
- imm.onPostWindowFocus(mView, mView.findFocus(),
- mWindowAttributes.softInputMode,
- !mHasHadWindowFocus, mWindowAttributes.flags);
- }
- // Clear the forward bit. We can just do this directly, since
- // the window manager doesn't care about it.
- mWindowAttributes.softInputMode &=
- ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
- ((WindowManager.LayoutParams) mView.getLayoutParams())
- .softInputMode &=
- ~WindowManager.LayoutParams
- .SOFT_INPUT_IS_FORWARD_NAVIGATION;
- mHasHadWindowFocus = true;
- } else {
- if (mPointerCapture) {
- handlePointerCaptureChanged(false);
- }
- }
- }
- mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
+ handleWindowFocusChanged();
} break;
case MSG_DIE:
doDie();
@@ -6845,6 +6876,7 @@
}
if (stage != null) {
+ handleWindowFocusChanged();
stage.deliver(q);
} else {
finishInputEvent(q);
@@ -7150,10 +7182,13 @@
}
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
+ synchronized (this) {
+ mWindowFocusChanged = true;
+ mUpcomingWindowFocus = hasFocus;
+ mUpcomingInTouchMode = inTouchMode;
+ }
Message msg = Message.obtain();
msg.what = MSG_WINDOW_FOCUS_CHANGED;
- msg.arg1 = hasFocus ? 1 : 0;
- msg.arg2 = inTouchMode ? 1 : 0;
mHandler.sendMessage(msg);
}
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index df124ac..e5cbe96 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -17,7 +17,7 @@
package android.view;
-import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Rect;
/**
@@ -49,7 +49,7 @@
private boolean mSystemWindowInsetsConsumed = false;
private boolean mWindowDecorInsetsConsumed = false;
private boolean mStableInsetsConsumed = false;
- private boolean mCutoutConsumed = false;
+ private boolean mDisplayCutoutConsumed = false;
private static final Rect EMPTY_RECT = new Rect(0, 0, 0, 0);
@@ -80,8 +80,9 @@
mIsRound = isRound;
mAlwaysConsumeNavBar = alwaysConsumeNavBar;
- mCutoutConsumed = displayCutout == null;
- mDisplayCutout = mCutoutConsumed ? DisplayCutout.NO_CUTOUT : displayCutout;
+ mDisplayCutoutConsumed = displayCutout == null;
+ mDisplayCutout = (mDisplayCutoutConsumed || displayCutout.isEmpty())
+ ? null : displayCutout;
}
/**
@@ -99,7 +100,7 @@
mIsRound = src.mIsRound;
mAlwaysConsumeNavBar = src.mAlwaysConsumeNavBar;
mDisplayCutout = src.mDisplayCutout;
- mCutoutConsumed = src.mCutoutConsumed;
+ mDisplayCutoutConsumed = src.mDisplayCutoutConsumed;
}
/** @hide */
@@ -269,15 +270,16 @@
*/
public boolean hasInsets() {
return hasSystemWindowInsets() || hasWindowDecorInsets() || hasStableInsets()
- || mDisplayCutout.hasCutout();
+ || mDisplayCutout != null;
}
/**
- * @return the display cutout
+ * Returns the display cutout if there is one.
+ *
+ * @return the display cutout or null if there is none
* @see DisplayCutout
- * @hide pending API
*/
- @NonNull
+ @Nullable
public DisplayCutout getDisplayCutout() {
return mDisplayCutout;
}
@@ -286,12 +288,11 @@
* Returns a copy of this WindowInsets with the cutout fully consumed.
*
* @return A modified copy of this WindowInsets
- * @hide pending API
*/
- public WindowInsets consumeCutout() {
+ public WindowInsets consumeDisplayCutout() {
final WindowInsets result = new WindowInsets(this);
- result.mDisplayCutout = DisplayCutout.NO_CUTOUT;
- result.mCutoutConsumed = true;
+ result.mDisplayCutout = null;
+ result.mDisplayCutoutConsumed = true;
return result;
}
@@ -311,7 +312,7 @@
*/
public boolean isConsumed() {
return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed && mStableInsetsConsumed
- && mCutoutConsumed;
+ && mDisplayCutoutConsumed;
}
/**
@@ -530,7 +531,7 @@
return "WindowInsets{systemWindowInsets=" + mSystemWindowInsets
+ " windowDecorInsets=" + mWindowDecorInsets
+ " stableInsets=" + mStableInsets
- + (mDisplayCutout.hasCutout() ? " cutout=" + mDisplayCutout : "")
+ + (mDisplayCutout != null ? " cutout=" + mDisplayCutout : "")
+ (isRound() ? " round" : "")
+ "}";
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 500701d..cbe012a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1286,7 +1286,6 @@
* The window must correctly position its contents to take the display cutout into account.
*
* @see DisplayCutout
- * @hide for now
*/
public static final long FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA = 0x00000001;
@@ -1294,7 +1293,6 @@
* Various behavioral options/flags. Default is none.
*
* @see #FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA
- * @hide for now
*/
@Flags2 public long flags2;
@@ -1672,12 +1670,20 @@
* Visibility state for {@link #softInputMode}: please show the soft
* input area when normally appropriate (when the user is navigating
* forward to your window).
+ *
+ * <p>Applications that target {@link android.os.Build.VERSION_CODES#P} and later, this flag
+ * is ignored unless there is a focused view that returns {@code true} from
+ * {@link View#isInEditMode()} when the window is focused.</p>
*/
public static final int SOFT_INPUT_STATE_VISIBLE = 4;
/**
* Visibility state for {@link #softInputMode}: please always make the
* soft input area visible when this window receives input focus.
+ *
+ * <p>Applications that target {@link android.os.Build.VERSION_CODES#P} and later, this flag
+ * is ignored unless there is a focused view that returns {@code true} from
+ * {@link View#isInEditMode()} when the window is focused.</p>
*/
public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5;
@@ -1738,7 +1744,7 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = {
+ @IntDef(flag = true, prefix = { "SOFT_INPUT_" }, value = {
SOFT_INPUT_STATE_UNSPECIFIED,
SOFT_INPUT_STATE_UNCHANGED,
SOFT_INPUT_STATE_HIDDEN,
@@ -2241,6 +2247,7 @@
out.writeInt(y);
out.writeInt(type);
out.writeInt(flags);
+ out.writeLong(flags2);
out.writeInt(privateFlags);
out.writeInt(softInputMode);
out.writeInt(gravity);
@@ -2296,6 +2303,7 @@
y = in.readInt();
type = in.readInt();
flags = in.readInt();
+ flags2 = in.readLong();
privateFlags = in.readInt();
softInputMode = in.readInt();
gravity = in.readInt();
@@ -2428,6 +2436,10 @@
flags = o.flags;
changes |= FLAGS_CHANGED;
}
+ if (flags2 != o.flags2) {
+ flags2 = o.flags2;
+ changes |= FLAGS_CHANGED;
+ }
if (privateFlags != o.privateFlags) {
privateFlags = o.privateFlags;
changes |= PRIVATE_FLAGS_CHANGED;
@@ -2681,6 +2693,11 @@
sb.append(System.lineSeparator());
sb.append(prefix).append(" fl=").append(
ViewDebug.flagsToString(LayoutParams.class, "flags", flags));
+ if (flags2 != 0) {
+ sb.append(System.lineSeparator());
+ // TODO(roosa): add a long overload for ViewDebug.flagsToString.
+ sb.append(prefix).append(" fl2=0x").append(Long.toHexString(flags2));
+ }
if (privateFlags != 0) {
sb.append(System.lineSeparator());
sb.append(prefix).append(" pfl=").append(ViewDebug.flagsToString(
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index e146555..72af203 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -790,11 +790,14 @@
if (info != null) {
info.setConnectionId(connectionId);
// Empty array means any package name is Okay
- if (!ArrayUtils.isEmpty(packageNames)
- && !ArrayUtils.contains(packageNames, info.getPackageName().toString())) {
- // If the node package not one of the valid ones, pick the top one - this
- // is one of the packages running in the introspected UID.
- info.setPackageName(packageNames[0]);
+ if (!ArrayUtils.isEmpty(packageNames)) {
+ CharSequence packageName = info.getPackageName();
+ if (packageName == null
+ || !ArrayUtils.contains(packageNames, packageName.toString())) {
+ // If the node package not one of the valid ones, pick the top one - this
+ // is one of the packages running in the introspected UID.
+ info.setPackageName(packageNames[0]);
+ }
}
info.setSealed(true);
if (!bypassCache) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 9c2f6bb..28ef697 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -2325,7 +2325,7 @@
/**
* Returns whether the node is explicitly marked as a focusable unit by a screen reader. Note
* that {@code false} indicates that it is not explicitly marked, not that the node is not
- * a focusable unit. Screen readers should generally used other signals, such as
+ * a focusable unit. Screen readers should generally use other signals, such as
* {@link #isFocusable()}, or the presence of text in a node, to determine what should receive
* focus.
*
diff --git a/core/java/android/view/accessibility/AccessibilityRequestPreparer.java b/core/java/android/view/accessibility/AccessibilityRequestPreparer.java
index 889feb9..25f830a 100644
--- a/core/java/android/view/accessibility/AccessibilityRequestPreparer.java
+++ b/core/java/android/view/accessibility/AccessibilityRequestPreparer.java
@@ -44,10 +44,9 @@
public static final int REQUEST_TYPE_EXTRA_DATA = 0x00000001;
/** @hide */
- @IntDef(flag = true,
- value = {
- REQUEST_TYPE_EXTRA_DATA
- })
+ @IntDef(flag = true, prefix = { "REQUEST_TYPE_" }, value = {
+ REQUEST_TYPE_EXTRA_DATA
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface RequestTypes {}
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index f11767d..ef1a3f3 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -87,6 +87,7 @@
private static final int BOOLEAN_PROPERTY_ACTIVE = 1 << 0;
private static final int BOOLEAN_PROPERTY_FOCUSED = 1 << 1;
private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 1 << 2;
+ private static final int BOOLEAN_PROPERTY_PICTURE_IN_PICTURE = 1 << 3;
// Housekeeping.
private static final int MAX_POOL_SIZE = 10;
@@ -103,8 +104,7 @@
private final Rect mBoundsInScreen = new Rect();
private LongArray mChildIds;
private CharSequence mTitle;
- private int mAnchorId = UNDEFINED_WINDOW_ID;
- private boolean mInPictureInPicture;
+ private long mAnchorId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
private int mConnectionId = UNDEFINED_WINDOW_ID;
@@ -202,7 +202,7 @@
*
* @hide
*/
- public void setAnchorId(int anchorId) {
+ public void setAnchorId(long anchorId) {
mAnchorId = anchorId;
}
@@ -212,7 +212,8 @@
* @return The anchor node, or {@code null} if none exists.
*/
public AccessibilityNodeInfo getAnchor() {
- if ((mConnectionId == UNDEFINED_WINDOW_ID) || (mAnchorId == UNDEFINED_WINDOW_ID)
+ if ((mConnectionId == UNDEFINED_WINDOW_ID)
+ || (mAnchorId == AccessibilityNodeInfo.UNDEFINED_NODE_ID)
|| (mParentId == UNDEFINED_WINDOW_ID)) {
return null;
}
@@ -224,17 +225,7 @@
/** @hide */
public void setPictureInPicture(boolean pictureInPicture) {
- mInPictureInPicture = pictureInPicture;
- }
-
- /**
- * Check if the window is in picture-in-picture mode.
- *
- * @return {@code true} if the window is in picture-in-picture mode, {@code false} otherwise.
- * @removed
- */
- public boolean inPictureInPicture() {
- return isInPictureInPictureMode();
+ setBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE, pictureInPicture);
}
/**
@@ -243,7 +234,7 @@
* @return {@code true} if the window is in picture-in-picture mode, {@code false} otherwise.
*/
public boolean isInPictureInPictureMode() {
- return mInPictureInPicture;
+ return getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE);
}
/**
@@ -463,7 +454,6 @@
infoClone.mBoundsInScreen.set(info.mBoundsInScreen);
infoClone.mTitle = info.mTitle;
infoClone.mAnchorId = info.mAnchorId;
- infoClone.mInPictureInPicture = info.mInPictureInPicture;
if (info.mChildIds != null && info.mChildIds.size() > 0) {
if (infoClone.mChildIds == null) {
@@ -520,8 +510,7 @@
parcel.writeInt(mParentId);
mBoundsInScreen.writeToParcel(parcel, flags);
parcel.writeCharSequence(mTitle);
- parcel.writeInt(mAnchorId);
- parcel.writeInt(mInPictureInPicture ? 1 : 0);
+ parcel.writeLong(mAnchorId);
final LongArray childIds = mChildIds;
if (childIds == null) {
@@ -545,8 +534,7 @@
mParentId = parcel.readInt();
mBoundsInScreen.readFromParcel(parcel);
mTitle = parcel.readCharSequence();
- mAnchorId = parcel.readInt();
- mInPictureInPicture = parcel.readInt() == 1;
+ mAnchorId = parcel.readLong();
final int childCount = parcel.readInt();
if (childCount > 0) {
@@ -593,7 +581,7 @@
builder.append(", bounds=").append(mBoundsInScreen);
builder.append(", focused=").append(isFocused());
builder.append(", active=").append(isActive());
- builder.append(", pictureInPicture=").append(inPictureInPicture());
+ builder.append(", pictureInPicture=").append(isInPictureInPictureMode());
if (DEBUG) {
builder.append(", parent=").append(mParentId);
builder.append(", children=[");
@@ -611,7 +599,8 @@
builder.append(']');
} else {
builder.append(", hasParent=").append(mParentId != UNDEFINED_WINDOW_ID);
- builder.append(", isAnchored=").append(mAnchorId != UNDEFINED_WINDOW_ID);
+ builder.append(", isAnchored=")
+ .append(mAnchorId != AccessibilityNodeInfo.UNDEFINED_NODE_ID);
builder.append(", hasChildren=").append(mChildIds != null
&& mChildIds.size() > 0);
}
@@ -633,8 +622,7 @@
mChildIds.clear();
}
mConnectionId = UNDEFINED_WINDOW_ID;
- mAnchorId = UNDEFINED_WINDOW_ID;
- mInPictureInPicture = false;
+ mAnchorId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
mTitle = null;
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 419aeb3..d0dbff0e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -24,7 +24,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
-import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -1010,20 +1009,14 @@
}
/**
- * Gets the user data used for <a href="#FieldsClassification">fields classification</a>.
+ * Gets the user data used for
+ * <a href="AutofillService.html#FieldClassification">field classification</a>.
*
* <p><b>Note:</b> This method should only be called by an app providing an autofill service.
*
- * TODO(b/67867469):
- * - proper javadoc
- * - unhide / remove testApi
- *
* @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
* reset or if the caller currently does not have an enabled autofill service for the user.
- *
- * @hide
*/
- @TestApi
@Nullable public UserData getUserData() {
try {
return mService.getUserData();
@@ -1034,21 +1027,13 @@
}
/**
- * Sets the user data used for <a href="#FieldsClassification">fields classification</a>.
+ * Sets the user data used for
+ * <a href="AutofillService.html#FieldClassification">field classification</a>
*
* <p><b>Note:</b> This method should only be called by an app providing an autofill service,
* and it's ignored if the caller currently doesn't have an enabled autofill service for
* the user.
- *
- * TODO(b/67867469):
- * - proper javadoc
- * - unhide / remove testApi
- * - add unit tests:
- * - call set / get / verify
- *
- * @hide
*/
- @TestApi
public void setUserData(@Nullable UserData userData) {
try {
mService.setUserData(userData);
@@ -1058,13 +1043,17 @@
}
/**
- * TODO(b/67867469):
- * - proper javadoc
- * - mention this method in other places
- * - unhide / remove testApi
- * @hide
+ * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
+ * enabled.
+ *
+ * <p>As field classification is an expensive operation, it could be disabled, either
+ * temporarily (for example, because the service exceeded a rate-limit threshold) or
+ * permanently (for example, because the device is a low-level device).
+ *
+ * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
+ * and it's ignored if the caller currently doesn't have an enabled autofill service for
+ * the user.
*/
- @TestApi
public boolean isFieldClassificationEnabled() {
try {
return mService.isFieldClassificationEnabled();
@@ -1987,7 +1976,11 @@
public abstract static class AutofillCallback {
/** @hide */
- @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN, EVENT_INPUT_UNAVAILABLE})
+ @IntDef(prefix = { "EVENT_INPUT_" }, value = {
+ EVENT_INPUT_SHOWN,
+ EVENT_INPUT_HIDDEN,
+ EVENT_INPUT_UNAVAILABLE
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface AutofillEventType {}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 3cd8d4a..80d7b6b7 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -281,10 +281,10 @@
boolean mActive = false;
/**
- * Set whenever this client becomes inactive, to know we need to reset
- * state with the IME the next time we receive focus.
+ * {@code true} if next {@link #onPostWindowFocus(View, View, int, boolean, int)} needs to
+ * restart input.
*/
- boolean mHasBeenInactive = true;
+ boolean mRestartOnNextWindowFocus = true;
/**
* As reported by IME through InputConnection.
@@ -489,7 +489,7 @@
// Some other client has starting using the IME, so note
// that this happened and make sure our own editor's
// state is reset.
- mHasBeenInactive = true;
+ mRestartOnNextWindowFocus = true;
try {
// Note that finishComposingText() is allowed to run
// even when we are not active.
@@ -500,7 +500,7 @@
// Check focus again in case that "onWindowFocus" is called before
// handling this message.
if (mServedView != null && mServedView.hasWindowFocus()) {
- if (checkFocusNoStartInput(mHasBeenInactive)) {
+ if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) {
final int reason = active ?
InputMethodClient.START_INPUT_REASON_ACTIVATED_BY_IMMS :
InputMethodClient.START_INPUT_REASON_DEACTIVATED_BY_IMMS;
@@ -1333,48 +1333,31 @@
+ Integer.toHexString(controlFlags));
final InputBindResult res = mService.startInputOrWindowGainedFocus(
startInputReason, mClient, windowGainingFocus, controlFlags, softInputMode,
- windowFlags, tba, servedContext, missingMethodFlags);
+ windowFlags, tba, servedContext, missingMethodFlags,
+ view.getContext().getApplicationInfo().targetSdkVersion);
if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
- if (res != null) {
- if (res.id != null) {
- setInputChannelLocked(res.channel);
- mBindSequence = res.sequence;
- mCurMethod = res.method;
- mCurId = res.id;
- mNextUserActionNotificationSequenceNumber =
- res.userActionNotificationSequenceNumber;
- } else {
- if (res.channel != null && res.channel != mCurChannel) {
- res.channel.dispose();
- }
- if (mCurMethod == null) {
- // This means there is no input method available.
- if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
- return true;
- }
- }
- } else {
- if (startInputReason
- == InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN) {
- // We are here probably because of an obsolete window-focus-in message sent
- // to windowGainingFocus. Since IMMS determines whether a Window can have
- // IME focus or not by using the latest window focus state maintained in the
- // WMS, this kind of race condition cannot be avoided. One obvious example
- // would be that we have already received a window-focus-out message but the
- // UI thread is still handling previous window-focus-in message here.
- // TODO: InputBindResult should have the error code.
- if (DEBUG) Log.w(TAG, "startInputOrWindowGainedFocus failed. "
- + "Window focus may have already been lost. "
- + "win=" + windowGainingFocus + " view=" + dumpViewInfo(view));
- if (!mActive) {
- // mHasBeenInactive is a latch switch to forcefully refresh IME focus
- // state when an inactive (mActive == false) client is gaining window
- // focus. In case we have unnecessary disable the latch due to this
- // spurious wakeup, we re-enable the latch here.
- // TODO: Come up with more robust solution.
- mHasBeenInactive = true;
- }
- }
+ if (res == null) {
+ Log.wtf(TAG, "startInputOrWindowGainedFocus must not return"
+ + " null. startInputReason="
+ + InputMethodClient.getStartInputReason(startInputReason)
+ + " editorInfo=" + tba
+ + " controlFlags=#" + Integer.toHexString(controlFlags));
+ return false;
+ }
+ if (res.id != null) {
+ setInputChannelLocked(res.channel);
+ mBindSequence = res.sequence;
+ mCurMethod = res.method;
+ mCurId = res.id;
+ mNextUserActionNotificationSequenceNumber =
+ res.userActionNotificationSequenceNumber;
+ } else if (res.channel != null && res.channel != mCurChannel) {
+ res.channel.dispose();
+ }
+ switch (res.result) {
+ case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
+ mRestartOnNextWindowFocus = true;
+ break;
}
if (mCurMethod != null && mCompletions != null) {
try {
@@ -1550,9 +1533,9 @@
+ " softInputMode=" + InputMethodClient.softInputModeToString(softInputMode)
+ " first=" + first + " flags=#"
+ Integer.toHexString(windowFlags));
- if (mHasBeenInactive) {
- if (DEBUG) Log.v(TAG, "Has been inactive! Starting fresh");
- mHasBeenInactive = false;
+ if (mRestartOnNextWindowFocus) {
+ if (DEBUG) Log.v(TAG, "Restarting due to mRestartOnNextWindowFocus");
+ mRestartOnNextWindowFocus = false;
forceNewFocus = true;
}
focusInLocked(focusedView != null ? focusedView : rootView);
@@ -1588,7 +1571,8 @@
mService.startInputOrWindowGainedFocus(
InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
rootView.getWindowToken(), controlFlags, softInputMode, windowFlags, null,
- null, 0 /* missingMethodFlags */);
+ null, 0 /* missingMethodFlags */,
+ rootView.getContext().getApplicationInfo().targetSdkVersion);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2483,7 +2467,7 @@
p.println(" mMainLooper=" + mMainLooper);
p.println(" mIInputContext=" + mIInputContext);
p.println(" mActive=" + mActive
- + " mHasBeenInactive=" + mHasBeenInactive
+ + " mRestartOnNextWindowFocus=" + mRestartOnNextWindowFocus
+ " mBindSequence=" + mBindSequence
+ " mCurId=" + mCurId);
p.println(" mFullscreenMode=" + mFullscreenMode);
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index fdc9f92..ed60430 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -16,17 +16,23 @@
package android.view.textclassifier;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
import android.annotation.WorkerThread;
import android.os.LocaleList;
+import android.util.ArraySet;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
/**
* Interface for providing text classification related features.
@@ -58,6 +64,20 @@
})
@interface EntityType {}
+ /** Designates that the TextClassifier should identify all entity types it can. **/
+ int ENTITY_PRESET_ALL = 0;
+ /** Designates that the TextClassifier should identify no entities. **/
+ int ENTITY_PRESET_NONE = 1;
+ /** Designates that the TextClassifier should identify a base set of entities determined by the
+ * TextClassifier. **/
+ int ENTITY_PRESET_BASE = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "ENTITY_CONFIG_" },
+ value = {ENTITY_PRESET_ALL, ENTITY_PRESET_NONE, ENTITY_PRESET_BASE})
+ @interface EntityPreset {}
+
/**
* No-op TextClassifier.
* This may be used to turn off TextClassifier features.
@@ -217,6 +237,8 @@
* Returns a {@link TextLinks} that may be applied to the text to annotate it with links
* information.
*
+ * If no options are supplied, default values will be used, determined by the TextClassifier.
+ *
* @param text the text to generate annotations for
* @param options configuration for link generation
*
@@ -251,6 +273,16 @@
}
/**
+ * Returns a {@link Collection} of the entity types in the specified preset.
+ *
+ * @see #ENTITIES_ALL
+ * @see #ENTITIES_NONE
+ */
+ default Collection<String> getEntitiesForPreset(@EntityPreset int entityPreset) {
+ return Collections.EMPTY_LIST;
+ }
+
+ /**
* Logs a TextClassifier event.
*
* @param source the text classifier used to generate this event
@@ -268,6 +300,62 @@
return TextClassifierConstants.DEFAULT;
}
+ /**
+ * Configuration object for specifying what entities to identify.
+ *
+ * Configs are initially based on a predefined preset, and can be modified from there.
+ */
+ final class EntityConfig {
+ private final @TextClassifier.EntityPreset int mEntityPreset;
+ private final Collection<String> mExcludedEntityTypes;
+ private final Collection<String> mIncludedEntityTypes;
+
+ public EntityConfig(@TextClassifier.EntityPreset int mEntityPreset) {
+ this.mEntityPreset = mEntityPreset;
+ mExcludedEntityTypes = new ArraySet<>();
+ mIncludedEntityTypes = new ArraySet<>();
+ }
+
+ /**
+ * Specifies an entity to include in addition to any specified by the enity preset.
+ *
+ * Note that if an entity has been excluded, the exclusion will take precedence.
+ */
+ public EntityConfig includeEntities(String... entities) {
+ for (String entity : entities) {
+ mIncludedEntityTypes.add(entity);
+ }
+ return this;
+ }
+
+ /**
+ * Specifies an entity to be excluded.
+ */
+ public EntityConfig excludeEntities(String... entities) {
+ for (String entity : entities) {
+ mExcludedEntityTypes.add(entity);
+ }
+ return this;
+ }
+
+ /**
+ * Returns an unmodifiable list of the final set of entities to find.
+ */
+ public List<String> getEntities(TextClassifier textClassifier) {
+ ArrayList<String> entities = new ArrayList<>();
+ for (String entity : textClassifier.getEntitiesForPreset(mEntityPreset)) {
+ if (!mExcludedEntityTypes.contains(entity)) {
+ entities.add(entity);
+ }
+ }
+ for (String entity : mIncludedEntityTypes) {
+ if (!mExcludedEntityTypes.contains(entity) && !entities.contains(entity)) {
+ entities.add(entity);
+ }
+ }
+ return Collections.unmodifiableList(entities);
+ }
+ }
/**
* Utility functions for TextClassifier methods.
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 6cf6b69..aea3cb0 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -42,6 +42,9 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@@ -66,6 +69,18 @@
private static final String MODEL_FILE_REGEX = "textclassifier\\.smartselection\\.(.*)\\.model";
private static final String UPDATED_MODEL_FILE_PATH =
"/data/misc/textclassifier/textclassifier.smartselection.model";
+ private static final List<String> ENTITY_TYPES_ALL =
+ Collections.unmodifiableList(Arrays.asList(
+ TextClassifier.TYPE_ADDRESS,
+ TextClassifier.TYPE_EMAIL,
+ TextClassifier.TYPE_PHONE,
+ TextClassifier.TYPE_URL));
+ private static final List<String> ENTITY_TYPES_BASE =
+ Collections.unmodifiableList(Arrays.asList(
+ TextClassifier.TYPE_ADDRESS,
+ TextClassifier.TYPE_EMAIL,
+ TextClassifier.TYPE_PHONE,
+ TextClassifier.TYPE_URL));
private final Context mContext;
@@ -168,17 +183,23 @@
@Override
public TextLinks generateLinks(
- @NonNull CharSequence text, @NonNull TextLinks.Options options) {
+ @NonNull CharSequence text, @Nullable TextLinks.Options options) {
Utils.validateInput(text);
final String textString = text.toString();
final TextLinks.Builder builder = new TextLinks.Builder(textString);
try {
- LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null;
+ final LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null;
+ final Collection<String> entitiesToIdentify =
+ options != null && options.getEntityConfig() != null
+ ? options.getEntityConfig().getEntities(this) : ENTITY_TYPES_ALL;
final SmartSelection smartSelection = getSmartSelection(defaultLocales);
final SmartSelection.AnnotatedSpan[] annotations = smartSelection.annotate(textString);
for (SmartSelection.AnnotatedSpan span : annotations) {
- final Map<String, Float> entityScores = new HashMap<>();
final SmartSelection.ClassificationResult[] results = span.getClassification();
+ if (results.length == 0 || !entitiesToIdentify.contains(results[0].mCollection)) {
+ continue;
+ }
+ final Map<String, Float> entityScores = new HashMap<>();
for (int i = 0; i < results.length; i++) {
entityScores.put(results[i].mCollection, results[i].mScore);
}
@@ -193,6 +214,20 @@
}
@Override
+ public Collection<String> getEntitiesForPreset(@TextClassifier.EntityPreset int entityPreset) {
+ switch (entityPreset) {
+ case TextClassifier.ENTITY_PRESET_NONE:
+ return Collections.emptyList();
+ case TextClassifier.ENTITY_PRESET_BASE:
+ return ENTITY_TYPES_BASE;
+ case TextClassifier.ENTITY_PRESET_ALL:
+ // fall through
+ default:
+ return ENTITY_TYPES_ALL;
+ }
+ }
+
+ @Override
public void logEvent(String source, String event) {
if (LOG_TAG.equals(source)) {
mMetricsLogger.count(event, 1);
@@ -382,7 +417,7 @@
}
final String type = getHighestScoringType(classifications);
- addActions(builder, IntentFactory.create(mContext, type, text));
+ addActions(builder, IntentFactory.create(mContext, type, classifiedText));
return builder.setSignature(getSignature(text, start, end)).build();
}
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 0e039e3..6c587cf 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -22,6 +22,8 @@
import android.os.LocaleList;
import android.text.SpannableString;
import android.text.style.ClickableSpan;
+import android.view.View;
+import android.widget.TextView;
import com.android.internal.util.Preconditions;
@@ -159,11 +161,12 @@
public static final class Options {
private LocaleList mDefaultLocales;
+ private TextClassifier.EntityConfig mEntityConfig;
/**
- * @param defaultLocales ordered list of locale preferences that may be used to disambiguate
- * the provided text. If no locale preferences exist, set this to null or an empty
- * locale list.
+ * @param defaultLocales ordered list of locale preferences that may be used to
+ * disambiguate the provided text. If no locale preferences exist,
+ * set this to null or an empty locale list.
*/
public Options setDefaultLocales(@Nullable LocaleList defaultLocales) {
mDefaultLocales = defaultLocales;
@@ -171,6 +174,17 @@
}
/**
+ * Sets the entity configuration to use. This determines what types of entities the
+ * TextClassifier will look for.
+ *
+ * @param entityConfig EntityConfig to use
+ */
+ public Options setEntityConfig(@Nullable TextClassifier.EntityConfig entityConfig) {
+ mEntityConfig = entityConfig;
+ return this;
+ }
+
+ /**
* @return ordered list of locale preferences that can be used to disambiguate
* the provided text.
*/
@@ -178,6 +192,15 @@
public LocaleList getDefaultLocales() {
return mDefaultLocales;
}
+
+ /**
+ * @return The config representing the set of entities to look for.
+ * @see #setEntityConfig(TextClassifier.EntityConfig)
+ */
+ @Nullable
+ public TextClassifier.EntityConfig getEntityConfig() {
+ return mEntityConfig;
+ }
}
/**
@@ -189,9 +212,14 @@
* @hide
*/
public static final Function<TextLink, ClickableSpan> DEFAULT_SPAN_FACTORY =
- textLink -> {
- // TODO: Implement.
- throw new UnsupportedOperationException("Not yet implemented");
+ textLink -> new ClickableSpan() {
+ @Override
+ public void onClick(View widget) {
+ if (widget instanceof TextView) {
+ final TextView textView = (TextView) widget;
+ textView.requestActionMode(textLink);
+ }
+ }
};
/**
diff --git a/core/java/android/webkit/TracingConfig.java b/core/java/android/webkit/TracingConfig.java
new file mode 100644
index 0000000..75e2bf7
--- /dev/null
+++ b/core/java/android/webkit/TracingConfig.java
@@ -0,0 +1,203 @@
+/*
+ * 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.webkit;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Holds tracing configuration information and predefined settings.
+ */
+public class TracingConfig {
+
+ private final String mCustomCategoryPattern;
+ private final @PresetCategories int mPresetCategories;
+ private @TracingMode int mTracingMode;
+
+ /** @hide */
+ @IntDef({CATEGORIES_NONE, CATEGORIES_WEB_DEVELOPER, CATEGORIES_INPUT_LATENCY,
+ CATEGORIES_RENDERING, CATEGORIES_JAVASCRIPT_AND_RENDERING, CATEGORIES_FRAME_VIEWER})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PresetCategories {}
+
+ /**
+ * Indicates that there are no preset categories.
+ */
+ public static final int CATEGORIES_NONE = -1;
+
+ /**
+ * Predefined categories typically useful for web developers.
+ * Typically includes blink, compositor, renderer.scheduler and v8 categories.
+ */
+ public static final int CATEGORIES_WEB_DEVELOPER = 0;
+
+ /**
+ * Predefined categories for analyzing input latency issues.
+ * Typically includes input, renderer.scheduler categories.
+ */
+ public static final int CATEGORIES_INPUT_LATENCY = 1;
+
+ /**
+ * Predefined categories for analyzing rendering issues.
+ * Typically includes blink, compositor and gpu categories.
+ */
+ public static final int CATEGORIES_RENDERING = 2;
+
+ /**
+ * Predefined categories for analyzing javascript and rendering issues.
+ * Typically includes blink, compositor, gpu, renderer.schduler and v8 categories.
+ */
+ public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 3;
+
+ /**
+ * Predefined categories for studying difficult rendering performance problems.
+ * Typically includes blink, compositor, gpu, renderer.scheduler, v8 and
+ * some other compositor categories which are disabled by default.
+ */
+ public static final int CATEGORIES_FRAME_VIEWER = 4;
+
+ /** @hide */
+ @IntDef({RECORD_UNTIL_FULL, RECORD_CONTINUOUSLY, RECORD_UNTIL_FULL_LARGE_BUFFER,
+ RECORD_TO_CONSOLE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TracingMode {}
+
+ /**
+ * Record trace events until the internal tracing buffer is full. Default tracing mode.
+ * Typically the buffer memory usage is between {@link #RECORD_CONTINUOUSLY} and the
+ * {@link #RECORD_UNTIL_FULL_LARGE_BUFFER}. Depending on the implementation typically allows
+ * up to 256k events to be stored.
+ */
+ public static final int RECORD_UNTIL_FULL = 0;
+
+ /**
+ * Record trace events continuously using an internal ring buffer. Overwrites
+ * old events if they exceed buffer capacity. Uses less memory than both
+ * {@link #RECORD_UNTIL_FULL} and {@link #RECORD_UNTIL_FULL_LARGE_BUFFER} modes.
+ * Depending on the implementation typically allows up to 64k events to be stored.
+ */
+ public static final int RECORD_CONTINUOUSLY = 1;
+
+ /**
+ * Record trace events using a larger internal tracing buffer until it is full.
+ * Uses more memory than the other modes and may not be suitable on devices
+ * with smaller RAM. Depending on the implementation typically allows up to
+ * 512 million events to be stored.
+ */
+ public static final int RECORD_UNTIL_FULL_LARGE_BUFFER = 2;
+
+ /**
+ * Record trace events to console (logcat). The events are discarded and nothing
+ * is sent back to the caller. Uses the least memory as compared to the other modes.
+ */
+ public static final int RECORD_TO_CONSOLE = 3;
+
+ /**
+ * Create config with the preset categories.
+ * <p>
+ * Example:
+ * TracingConfig(CATEGORIES_WEB_DEVELOPER) -- records trace events from the "web developer"
+ * preset categories.
+ *
+ * @param presetCategories preset categories to use, one of {@link #CATEGORIES_WEB_DEVELOPER},
+ * {@link #CATEGORIES_INPUT_LATENCY}, {@link #CATEGORIES_RENDERING},
+ * {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
+ * {@link #CATEGORIES_FRAME_VIEWER}.
+ *
+ * Note: for specifying custom categories without presets use
+ * {@link #TracingConfig(int, String, int)}.
+ *
+ */
+ public TracingConfig(@PresetCategories int presetCategories) {
+ this(presetCategories, "", RECORD_UNTIL_FULL);
+ }
+
+ /**
+ * Create a configuration with both preset categories and custom categories.
+ * Also allows to specify the tracing mode.
+ *
+ * Note that the categories are defined by the currently-in-use version of WebView. They live
+ * in chromium code and are not part of the Android API. See
+ * See <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">
+ * chromium documentation on tracing</a> for more details.
+ *
+ * <p>
+ * Examples:
+ *
+ * Preset category with a specified trace mode:
+ * TracingConfig(CATEGORIES_WEB_DEVELOPER, "", RECORD_UNTIL_FULL_LARGE_BUFFER);
+ * Custom categories:
+ * TracingConfig(CATEGORIES_NONE, "browser", RECORD_UNTIL_FULL)
+ * -- records only the trace events from the "browser" category.
+ * TraceConfig(CATEGORIES_NONE, "-input,-gpu", RECORD_UNTIL_FULL)
+ * -- records all trace events excluding the events from the "input" and 'gpu' categories.
+ * TracingConfig(CATEGORIES_NONE, "blink*,devtools*", RECORD_UNTIL_FULL)
+ * -- records only the trace events matching the "blink*" and "devtools*" patterns
+ * (e.g. "blink_gc" and "devtools.timeline" categories).
+ *
+ * Combination of preset and additional custom categories:
+ * TracingConfig(CATEGORIES_WEB_DEVELOPER, "memory-infra", RECORD_CONTINUOUSLY)
+ * -- records events from the "web developer" categories and events from the "memory-infra"
+ * category to understand where memory is being used.
+ *
+ * @param presetCategories preset categories to use, one of {@link #CATEGORIES_WEB_DEVELOPER},
+ * {@link #CATEGORIES_INPUT_LATENCY}, {@link #CATEGORIES_RENDERING},
+ * {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
+ * {@link #CATEGORIES_FRAME_VIEWER}.
+ * @param customCategories a comma-delimited list of category wildcards. A category can
+ * have an optional '-' prefix to make it an excluded category.
+ * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL},
+ * {@link #RECORD_CONTINUOUSLY}, {@link #RECORD_UNTIL_FULL_LARGE_BUFFER}
+ * or {@link #RECORD_TO_CONSOLE}.
+ */
+ public TracingConfig(@PresetCategories int presetCategories,
+ @NonNull String customCategories, @TracingMode int tracingMode) {
+ mPresetCategories = presetCategories;
+ mCustomCategoryPattern = customCategories;
+ mTracingMode = RECORD_UNTIL_FULL;
+ }
+
+ /**
+ * Returns the custom category pattern for this configuration.
+ *
+ * @return empty string if no custom category pattern is specified.
+ */
+ @NonNull
+ public String getCustomCategoryPattern() {
+ return mCustomCategoryPattern;
+ }
+
+ /**
+ * Returns the preset categories value of this configuration.
+ */
+ @PresetCategories
+ public int getPresetCategories() {
+ return mPresetCategories;
+ }
+
+ /**
+ * Returns the tracing mode of this configuration.
+ */
+ @TracingMode
+ public int getTracingMode() {
+ return mTracingMode;
+ }
+
+}
diff --git a/core/java/android/webkit/TracingController.java b/core/java/android/webkit/TracingController.java
new file mode 100644
index 0000000..cadb8a1
--- /dev/null
+++ b/core/java/android/webkit/TracingController.java
@@ -0,0 +1,126 @@
+/*
+ * 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.webkit;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+
+/**
+ * Manages tracing of WebViews. In particular provides functionality for the app
+ * to enable/disable tracing of parts of code and to collect tracing data.
+ * This is useful for profiling performance issues, debugging and memory usage
+ * analysis in production and real life scenarios.
+ * <p>
+ * The resulting trace data is sent back as a byte sequence in json format. This
+ * file can be loaded in "chrome://tracing" for further analysis.
+ * <p>
+ * Note: All methods in this class must be called on the UI thread. All callbacks
+ * are also called on the UI thread.
+ * <p>
+ * Example usage:
+ * <pre class="prettyprint">
+ * TracingController tracingController = TracingController.getInstance();
+ * tracingController.start(new TraceConfig(CATEGORIES_WEB_DEVELOPER));
+ * [..]
+ * tracingController.stopAndFlush(new TraceFileOutput("trace.json"), null);
+ * </pre></p>
+ */
+public abstract class TracingController {
+
+ /**
+ * Interface for capturing tracing data.
+ */
+ public interface TracingOutputStream {
+ /**
+ * Will be called to return tracing data in chunks.
+ * Tracing data is returned in json format an array of bytes.
+ */
+ void write(byte[] chunk);
+
+ /**
+ * Called when tracing is finished and the data collection is over.
+ * There will be no calls to #write after #complete is called.
+ */
+ void complete();
+ }
+
+ /**
+ * Returns the default TracingController instance. At present there is
+ * only one TracingController instance for all WebView instances,
+ * however this restriction may be relaxed in the future.
+ *
+ * @return the default TracingController instance
+ */
+ @NonNull
+ public static TracingController getInstance() {
+ return WebViewFactory.getProvider().getTracingController();
+ }
+
+ /**
+ * Starts tracing all webviews. Depeding on the trace mode in traceConfig
+ * specifies how the trace events are recorded.
+ *
+ * For tracing modes {@link TracingConfig#RECORD_UNTIL_FULL},
+ * {@link TracingConfig#RECORD_CONTINUOUSLY} and
+ * {@link TracingConfig#RECORD_UNTIL_FULL_LARGE_BUFFER} the events are recorded
+ * using an internal buffer and flushed to the outputStream when
+ * {@link #stopAndFlush(TracingOutputStream, Handler)} is called.
+ *
+ * @param tracingConfig configuration options to use for tracing
+ * @return false if the system is already tracing, true otherwise.
+ */
+ public abstract boolean start(TracingConfig tracingConfig);
+
+ /**
+ * Stops tracing and discards all tracing data.
+ *
+ * This method is particularly useful in conjunction with the
+ * {@link TracingConfig#RECORD_TO_CONSOLE} tracing mode because tracing data is logged to
+ * console and not sent to an outputStream as with
+ * {@link #stopAndFlush(TracingOutputStream, Handler)}.
+ *
+ * @return false if the system was not tracing at the time of the call, true
+ * otherwise.
+ */
+ public abstract boolean stop();
+
+ /**
+ * Stops tracing and flushes tracing data to the specifid outputStream.
+ *
+ * Note that if the {@link TracingConfig#RECORD_TO_CONSOLE} tracing mode is used
+ * nothing will be sent to the outputStream and no TracingOuputStream methods will be
+ * called. In that case it is more convenient to just use {@link #stop()} instead.
+ *
+ * @param outputStream the output steam the tracing data will be sent to.
+ * @param handler the {@link android.os.Handler} on which the outputStream callbacks
+ * will be invoked. If the handler is null the current thread's Looper
+ * will be used.
+ * @return false if the system was not tracing at the time of the call, true
+ * otherwise.
+ */
+ public abstract boolean stopAndFlush(TracingOutputStream outputStream,
+ @Nullable Handler handler);
+
+ /** True if the system is tracing */
+ public abstract boolean isTracing();
+
+ // TODO: consider adding getTraceBufferUsage, percentage and approx event count.
+ // TODO: consider adding String getCategories(), for obtaining the actual list
+ // of categories used (given that presets are ints).
+
+}
diff --git a/core/java/android/webkit/TracingFileOutputStream.java b/core/java/android/webkit/TracingFileOutputStream.java
new file mode 100644
index 0000000..8a5fa36
--- /dev/null
+++ b/core/java/android/webkit/TracingFileOutputStream.java
@@ -0,0 +1,63 @@
+/*
+ * 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.webkit;
+
+import android.annotation.NonNull;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Simple TracingOutputStream implementation which writes the trace data from
+ * {@link TracingController} to a new file.
+ *
+ */
+public class TracingFileOutputStream implements TracingController.TracingOutputStream {
+
+ private FileOutputStream mFileOutput;
+
+ public TracingFileOutputStream(@NonNull String filename) throws FileNotFoundException {
+ mFileOutput = new FileOutputStream(filename);
+ }
+
+ /**
+ * Writes bytes chunk to the file.
+ */
+ public void write(byte[] chunk) {
+ try {
+ mFileOutput.write(chunk);
+ } catch (IOException e) {
+ onIOException(e);
+ }
+ }
+
+ /**
+ * Closes the file.
+ */
+ public void complete() {
+ try {
+ mFileOutput.close();
+ } catch (IOException e) {
+ onIOException(e);
+ }
+ }
+
+ private void onIOException(IOException e) {
+ throw new RuntimeException(e);
+ }
+}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index e493739..90cc481 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -122,7 +122,13 @@
}
/** @hide */
- @IntDef({LOAD_DEFAULT, LOAD_NORMAL, LOAD_CACHE_ELSE_NETWORK, LOAD_NO_CACHE, LOAD_CACHE_ONLY})
+ @IntDef(prefix = { "LOAD_" }, value = {
+ LOAD_DEFAULT,
+ LOAD_NORMAL,
+ LOAD_CACHE_ELSE_NETWORK,
+ LOAD_NO_CACHE,
+ LOAD_CACHE_ONLY
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface CacheMode {}
@@ -1415,13 +1421,12 @@
/**
* @hide
*/
- @IntDef(flag = true,
- value = {
- MENU_ITEM_NONE,
- MENU_ITEM_SHARE,
- MENU_ITEM_WEB_SEARCH,
- MENU_ITEM_PROCESS_TEXT
- })
+ @IntDef(flag = true, prefix = { "MENU_ITEM_" }, value = {
+ MENU_ITEM_NONE,
+ MENU_ITEM_SHARE,
+ MENU_ITEM_WEB_SEARCH,
+ MENU_ITEM_PROCESS_TEXT
+ })
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.PARAMETER, ElementType.METHOD})
private @interface MenuItemFlags {}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 6f99254..de5a822 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -997,6 +997,10 @@
* hex encoding of URLs for octets outside that range. For example, '#',
* '%', '\', '?' should be replaced by %23, %25, %27, %3f respectively.
* <p>
+ * The {@code mimeType} parameter specifies the format of the data.
+ * If WebView can't handle the specified MIME type, it will download the data.
+ * If {@code null}, defaults to 'text/html'.
+ * <p>
* The 'data' scheme URL formed by this method uses the default US-ASCII
* charset. If you need need to set a different charset, you should form a
* 'data' scheme URL which explicitly specifies a charset parameter in the
@@ -1005,8 +1009,7 @@
* always overrides that specified in the HTML or XML document itself.
*
* @param data a String of data in the given encoding
- * @param mimeType the MIMEType of the data, e.g. 'text/html'. If {@code null},
- * defaults to 'text/html'.
+ * @param mimeType the MIME type of the data, e.g. 'text/html'.
* @param encoding the encoding of the data
*/
public void loadData(String data, @Nullable String mimeType, @Nullable String encoding) {
@@ -1020,6 +1023,10 @@
* applying JavaScript's same origin policy. The historyUrl is used for the
* history entry.
* <p>
+ * The {@code mimeType} parameter specifies the format of the data.
+ * If WebView can't handle the specified MIME type, it will download the data.
+ * If {@code null}, defaults to 'text/html'.
+ * <p>
* Note that content specified in this way can access local device files
* (via 'file' scheme URLs) only if baseUrl specifies a scheme other than
* 'http', 'https', 'ftp', 'ftps', 'about' or 'javascript'.
@@ -1037,8 +1044,7 @@
* @param baseUrl the URL to use as the page's base URL. If {@code null} defaults to
* 'about:blank'.
* @param data a String of data in the given encoding
- * @param mimeType the MIMEType of the data, e.g. 'text/html'. If {@code null},
- * defaults to 'text/html'.
+ * @param mimeType the MIME type of the data, e.g. 'text/html'.
* @param encoding the encoding of the data
* @param historyUrl the URL to use as the history entry. If {@code null} defaults
* to 'about:blank'. If non-null, this must be a valid URL.
@@ -2075,6 +2081,48 @@
}
/**
+ * Define the directory used to store WebView data for the current process.
+ * The provided suffix will be used when constructing data and cache
+ * directory paths. If this API is not called, no suffix will be used.
+ * Each directory can be used by only one process in the application. If more
+ * than one process in an app wishes to use WebView, only one process can use
+ * the default directory, and other processes must call this API to define
+ * a unique suffix.
+ * <p>
+ * This API must be called before any instances of WebView are created in
+ * this process and before any other methods in the android.webkit package
+ * are called by this process.
+ *
+ * @param suffix The directory name suffix to be used for the current
+ * process. Must not contain a path separator.
+ * @throws IllegalStateException if WebView has already been initialized
+ * in the current process.
+ * @throws IllegalArgumentException if the suffix contains a path separator.
+ */
+ public static void setDataDirectorySuffix(String suffix) {
+ WebViewFactory.setDataDirectorySuffix(suffix);
+ }
+
+ /**
+ * Indicate that the current process does not intend to use WebView, and
+ * that an exception should be thrown if a WebView is created or any other
+ * methods in the android.webkit package are used.
+ * <p>
+ * Applications with multiple processes may wish to call this in processes
+ * which are not intended to use WebView to prevent potential data directory
+ * conflicts (see {@link #setDataDirectorySuffix}) and to avoid accidentally
+ * incurring the memory usage of initializing WebView in long-lived
+ * processes which have no need for it.
+ *
+ * @throws IllegalStateException if WebView has already been initialized
+ * in the current process.
+ */
+ public static void disableWebView() {
+ WebViewFactory.disableWebView();
+ }
+
+
+ /**
* @deprecated This was used for Gears, which has been deprecated.
* @hide
*/
@@ -2249,10 +2297,10 @@
}
/** @hide */
- @IntDef({
- RENDERER_PRIORITY_WAIVED,
- RENDERER_PRIORITY_BOUND,
- RENDERER_PRIORITY_IMPORTANT
+ @IntDef(prefix = { "RENDERER_PRIORITY_" }, value = {
+ RENDERER_PRIORITY_WAIVED,
+ RENDERER_PRIORITY_BOUND,
+ RENDERER_PRIORITY_IMPORTANT
})
@Retention(RetentionPolicy.SOURCE)
public @interface RendererPriority {}
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 46c3983..d0f9eee 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -256,11 +256,11 @@
public static final int ERROR_UNSAFE_RESOURCE = -16;
/** @hide */
- @IntDef({
- SAFE_BROWSING_THREAT_UNKNOWN,
- SAFE_BROWSING_THREAT_MALWARE,
- SAFE_BROWSING_THREAT_PHISHING,
- SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE
+ @IntDef(prefix = { "SAFE_BROWSING_THREAT_" }, value = {
+ SAFE_BROWSING_THREAT_UNKNOWN,
+ SAFE_BROWSING_THREAT_MALWARE,
+ SAFE_BROWSING_THREAT_PHISHING,
+ SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE
})
@Retention(RetentionPolicy.SOURCE)
public @interface SafeBrowsingThreat {}
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 7339931..f067091 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -218,4 +218,11 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Returns the data directory suffix to use, or null for none.
+ */
+ public String getDataDirectorySuffix() {
+ return WebViewFactory.getDataDirectorySuffix();
+ }
}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 9db0e8d..e3efad0 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -33,6 +33,7 @@
import android.util.ArraySet;
import android.util.Log;
+import java.io.File;
import java.lang.reflect.Method;
/**
@@ -63,6 +64,8 @@
private static final Object sProviderLock = new Object();
private static PackageInfo sPackageInfo;
private static Boolean sWebViewSupported;
+ private static boolean sWebViewDisabled;
+ private static String sDataDirectorySuffix; // stored here so it can be set without loading WV
// Error codes for loadWebViewNativeLibraryFromPackage
public static final int LIBLOAD_SUCCESS = 0;
@@ -115,6 +118,45 @@
/**
* @hide
*/
+ static void disableWebView() {
+ synchronized (sProviderLock) {
+ if (sProviderInstance != null) {
+ throw new IllegalStateException(
+ "Can't disable WebView: WebView already initialized");
+ }
+ sWebViewDisabled = true;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static void setDataDirectorySuffix(String suffix) {
+ synchronized (sProviderLock) {
+ if (sProviderInstance != null) {
+ throw new IllegalStateException(
+ "Can't set data directory suffix: WebView already initialized");
+ }
+ if (suffix.indexOf(File.separatorChar) >= 0) {
+ throw new IllegalArgumentException("Suffix " + suffix
+ + " contains a path separator");
+ }
+ sDataDirectorySuffix = suffix;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static String getDataDirectorySuffix() {
+ synchronized (sProviderLock) {
+ return sDataDirectorySuffix;
+ }
+ }
+
+ /**
+ * @hide
+ */
public static String getWebViewLibrary(ApplicationInfo ai) {
if (ai.metaData != null)
return ai.metaData.getString("com.android.webview.WebViewLibrary");
@@ -204,6 +246,11 @@
throw new UnsupportedOperationException();
}
+ if (sWebViewDisabled) {
+ throw new IllegalStateException(
+ "WebView.disableWebView() was called: WebView is disabled");
+ }
+
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
try {
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 4c47abc..3ced6a5 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -134,6 +134,14 @@
TokenBindingService getTokenBindingService();
/**
+ * Gets the TracingController instance for this WebView implementation. The
+ * implementation must return the same instance on subsequent calls.
+ *
+ * @return the TracingController instance
+ */
+ TracingController getTracingController();
+
+ /**
* Gets the ServiceWorkerController instance for this WebView implementation. The
* implementation must return the same instance on subsequent calls.
*
diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java
index de0b97d..eb2b6bc 100644
--- a/core/java/android/webkit/WebViewLibraryLoader.java
+++ b/core/java/android/webkit/WebViewLibraryLoader.java
@@ -123,10 +123,11 @@
throw new IllegalArgumentException(
"Native library paths to the WebView RelRo process must not be null!");
}
- int pid = LocalServices.getService(ActivityManagerInternal.class).startIsolatedProcess(
- RelroFileCreator.class.getName(), new String[] { nativeLib.path },
- "WebViewLoader-" + abi, abi, Process.SHARED_RELRO_UID, crashHandler);
- if (pid <= 0) throw new Exception("Failed to start the relro file creator process");
+ boolean success = LocalServices.getService(ActivityManagerInternal.class)
+ .startIsolatedProcess(
+ RelroFileCreator.class.getName(), new String[] { nativeLib.path },
+ "WebViewLoader-" + abi, abi, Process.SHARED_RELRO_UID, crashHandler);
+ if (!success) throw new Exception("Failed to start the relro file creator process");
} catch (Throwable t) {
// Log and discard errors as we must not crash the system server.
Log.e(LOGTAG, "error starting relro file creator for abi " + abi, t);
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index dfb3642..b2b93fa 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -107,7 +107,10 @@
public static final int MODE_CALENDAR = 2;
/** @hide */
- @IntDef({MODE_SPINNER, MODE_CALENDAR})
+ @IntDef(prefix = { "MODE_" }, value = {
+ MODE_SPINNER,
+ MODE_CALENDAR
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface DatePickerMode {}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index df97112..05d18d1 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -107,6 +107,7 @@
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextLinks;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.TextView.Drawables;
import android.widget.TextView.OnEditorActionListener;
@@ -174,6 +175,13 @@
int SELECTION_END = 2;
}
+ @IntDef({TextActionMode.SELECTION, TextActionMode.INSERTION, TextActionMode.TEXT_LINK})
+ @interface TextActionMode {
+ int SELECTION = 0;
+ int INSERTION = 1;
+ int TEXT_LINK = 2;
+ }
+
// Each Editor manages its own undo stack.
private final UndoManager mUndoManager = new UndoManager();
private UndoOwner mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this);
@@ -544,7 +552,8 @@
chooseSize(mErrorPopup, mError, tv);
tv.setText(mError);
- mErrorPopup.showAsDropDown(mTextView, getErrorX(), getErrorY());
+ mErrorPopup.showAsDropDown(mTextView, getErrorX(), getErrorY(),
+ Gravity.TOP | Gravity.LEFT);
mErrorPopup.fixDirection(mErrorPopup.isAboveAnchor());
}
@@ -2052,7 +2061,7 @@
stopTextActionMode();
ActionMode.Callback actionModeCallback =
- new TextActionModeCallback(false /* hasSelection */);
+ new TextActionModeCallback(TextActionMode.INSERTION);
mTextActionMode = mTextView.startActionMode(
actionModeCallback, ActionMode.TYPE_FLOATING);
if (mTextActionMode != null && getInsertionController() != null) {
@@ -2078,7 +2087,23 @@
* Asynchronously starts a selection action mode using the TextClassifier.
*/
void startSelectionActionModeAsync(boolean adjustSelection) {
- getSelectionActionModeHelper().startActionModeAsync(adjustSelection);
+ getSelectionActionModeHelper().startSelectionActionModeAsync(adjustSelection);
+ }
+
+ void startLinkActionModeAsync(TextLinks.TextLink link) {
+ Preconditions.checkNotNull(link);
+ if (!(mTextView.getText() instanceof Spannable)) {
+ return;
+ }
+ Spannable text = (Spannable) mTextView.getText();
+ stopTextActionMode();
+ if (mTextView.isTextSelectable()) {
+ Selection.setSelection((Spannable) text, link.getStart(), link.getEnd());
+ } else {
+ //TODO: Nonselectable text
+ }
+
+ getSelectionActionModeHelper().startLinkActionModeAsync(link);
}
/**
@@ -2144,7 +2169,7 @@
return true;
}
- boolean startSelectionActionModeInternal() {
+ boolean startActionModeInternal(@TextActionMode int actionMode) {
if (extractedTextModeWillBeStarted()) {
return false;
}
@@ -2158,8 +2183,7 @@
return false;
}
- ActionMode.Callback actionModeCallback =
- new TextActionModeCallback(true /* hasSelection */);
+ ActionMode.Callback actionModeCallback = new TextActionModeCallback(actionMode);
mTextActionMode = mTextView.startActionMode(actionModeCallback, ActionMode.TYPE_FLOATING);
final boolean selectionStarted = mTextActionMode != null;
@@ -3827,8 +3851,9 @@
private final int mHandleHeight;
private final Map<MenuItem, OnClickListener> mAssistClickHandlers = new HashMap<>();
- public TextActionModeCallback(boolean hasSelection) {
- mHasSelection = hasSelection;
+ TextActionModeCallback(@TextActionMode int mode) {
+ mHasSelection = mode == TextActionMode.SELECTION
+ || (mTextIsSelectable && mode == TextActionMode.TEXT_LINK);
if (mHasSelection) {
SelectionModifierCursorController selectionController = getSelectionController();
if (selectionController.mStartHandle == null) {
@@ -4923,7 +4948,10 @@
}
@Retention(RetentionPolicy.SOURCE)
- @IntDef({HANDLE_TYPE_SELECTION_START, HANDLE_TYPE_SELECTION_END})
+ @IntDef(prefix = { "HANDLE_TYPE_" }, value = {
+ HANDLE_TYPE_SELECTION_START,
+ HANDLE_TYPE_SELECTION_END
+ })
public @interface HandleType {}
public static final int HANDLE_TYPE_SELECTION_START = 0;
public static final int HANDLE_TYPE_SELECTION_END = 1;
@@ -6128,7 +6156,11 @@
}
@Retention(RetentionPolicy.SOURCE)
- @IntDef({MERGE_EDIT_MODE_FORCE_MERGE, MERGE_EDIT_MODE_NEVER_MERGE, MERGE_EDIT_MODE_NORMAL})
+ @IntDef(prefix = { "MERGE_EDIT_MODE_" }, value = {
+ MERGE_EDIT_MODE_FORCE_MERGE,
+ MERGE_EDIT_MODE_NEVER_MERGE,
+ MERGE_EDIT_MODE_NORMAL
+ })
private @interface MergeMode {}
private static final int MERGE_EDIT_MODE_FORCE_MERGE = 0;
private static final int MERGE_EDIT_MODE_NEVER_MERGE = 1;
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index cbd1e0a..012b918 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -172,7 +172,10 @@
// Public constants
/** @hide */
- @IntDef({HORIZONTAL, VERTICAL})
+ @IntDef(prefix = { "HORIZONTAL", "VERTICAL" }, value = {
+ HORIZONTAL,
+ VERTICAL
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface Orientation {}
@@ -198,7 +201,10 @@
public static final int UNDEFINED = Integer.MIN_VALUE;
/** @hide */
- @IntDef({ALIGN_BOUNDS, ALIGN_MARGINS})
+ @IntDef(prefix = { "ALIGN_" }, value = {
+ ALIGN_BOUNDS,
+ ALIGN_MARGINS
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface AlignmentMode {}
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index fcb44af..1ec9b2f 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -65,7 +65,12 @@
@RemoteView
public class GridView extends AbsListView {
/** @hide */
- @IntDef({NO_STRETCH, STRETCH_SPACING, STRETCH_COLUMN_WIDTH, STRETCH_SPACING_UNIFORM})
+ @IntDef(prefix = { "NO_STRETCH", "STRETCH_" }, value = {
+ NO_STRETCH,
+ STRETCH_SPACING,
+ STRETCH_COLUMN_WIDTH,
+ STRETCH_SPACING_UNIFORM
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface StretchMode {}
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 380bf7a..7ea1f1e 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -95,13 +95,12 @@
public static final int VERTICAL = 1;
/** @hide */
- @IntDef(flag = true,
- value = {
- SHOW_DIVIDER_NONE,
- SHOW_DIVIDER_BEGINNING,
- SHOW_DIVIDER_MIDDLE,
- SHOW_DIVIDER_END
- })
+ @IntDef(flag = true, prefix = { "SHOW_DIVIDER_" }, value = {
+ SHOW_DIVIDER_NONE,
+ SHOW_DIVIDER_BEGINNING,
+ SHOW_DIVIDER_MIDDLE,
+ SHOW_DIVIDER_END
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface DividerMode {}
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 4d3189e..d98b865 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -510,7 +510,11 @@
*/
public interface OnScrollListener {
/** @hide */
- @IntDef({SCROLL_STATE_IDLE, SCROLL_STATE_TOUCH_SCROLL, SCROLL_STATE_FLING})
+ @IntDef(prefix = { "SCROLL_STATE_" }, value = {
+ SCROLL_STATE_IDLE,
+ SCROLL_STATE_TOUCH_SCROLL,
+ SCROLL_STATE_FLING
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface ScrollState {}
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
new file mode 100644
index 0000000..8f0d02f
--- /dev/null
+++ b/core/java/android/widget/OWNERS
@@ -0,0 +1,12 @@
+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 d0ad27a..2c6466c 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -35,6 +35,7 @@
import android.view.ActionMode;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextSelection;
import android.view.textclassifier.logging.SmartSelectionEventTracker;
import android.view.textclassifier.logging.SmartSelectionEventTracker.SelectionEvent;
@@ -97,7 +98,10 @@
}
}
- public void startActionModeAsync(boolean adjustSelection) {
+ /**
+ * Starts Selection ActionMode.
+ */
+ public void startSelectionActionModeAsync(boolean adjustSelection) {
// Check if the smart selection should run for editable text.
adjustSelection &= !mTextView.isTextEditable()
|| mTextView.getTextClassifier().getSettings()
@@ -109,7 +113,7 @@
mTextView.getSelectionEnd());
cancelAsyncTask();
if (skipTextClassification()) {
- startActionMode(null);
+ startSelectionActionMode(null);
} else {
resetTextClassificationHelper();
mTextClassificationAsyncTask = new TextClassificationAsyncTask(
@@ -119,8 +123,27 @@
? mTextClassificationHelper::suggestSelection
: mTextClassificationHelper::classifyText,
mSmartSelectSprite != null
- ? this::startActionModeWithSmartSelectAnimation
- : this::startActionMode)
+ ? this::startSelectionActionModeWithSmartSelectAnimation
+ : this::startSelectionActionMode)
+ .execute();
+ }
+ }
+
+ /**
+ * Starts Link ActionMode.
+ */
+ public void startLinkActionModeAsync(TextLinks.TextLink textLink) {
+ //TODO: tracking/logging
+ cancelAsyncTask();
+ if (skipTextClassification()) {
+ startLinkActionMode(null);
+ } else {
+ resetTextClassificationHelper(textLink.getStart(), textLink.getEnd());
+ mTextClassificationAsyncTask = new TextClassificationAsyncTask(
+ mTextView,
+ mTextClassificationHelper.getTimeoutDuration(),
+ mTextClassificationHelper::classifyText,
+ this::startLinkActionMode)
.execute();
}
}
@@ -200,9 +223,19 @@
return noOpTextClassifier || noSelection || password;
}
- private void startActionMode(@Nullable SelectionResult result) {
+ private void startLinkActionMode(@Nullable SelectionResult result) {
+ startActionMode(Editor.TextActionMode.TEXT_LINK, result);
+ }
+
+ private void startSelectionActionMode(@Nullable SelectionResult result) {
+ startActionMode(Editor.TextActionMode.SELECTION, result);
+ }
+
+ private void startActionMode(
+ @Editor.TextActionMode int actionMode, @Nullable SelectionResult result) {
final CharSequence text = getText(mTextView);
- if (result != null && text instanceof Spannable) {
+ if (result != null && text instanceof Spannable
+ && (mTextView.isTextSelectable() || mTextView.isTextEditable())) {
// Do not change the selection if TextClassifier should be dark launched.
if (!mTextView.getTextClassifier().getSettings().isDarkLaunch()) {
Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
@@ -211,12 +244,13 @@
} else {
mTextClassification = null;
}
- if (mEditor.startSelectionActionModeInternal()) {
+ if (mEditor.startActionModeInternal(actionMode)) {
final SelectionModifierCursorController controller = mEditor.getSelectionController();
- if (controller != null) {
+ if (controller != null
+ && (mTextView.isTextSelectable() || mTextView.isTextEditable())) {
controller.show();
}
- if (result != null) {
+ if (result != null && actionMode == Editor.TextActionMode.SELECTION) {
mSelectionTracker.onSmartSelection(result);
}
}
@@ -224,10 +258,11 @@
mTextClassificationAsyncTask = null;
}
- private void startActionModeWithSmartSelectAnimation(@Nullable SelectionResult result) {
+ private void startSelectionActionModeWithSmartSelectAnimation(
+ @Nullable SelectionResult result) {
final Layout layout = mTextView.getLayout();
- final Runnable onAnimationEndCallback = () -> startActionMode(result);
+ final Runnable onAnimationEndCallback = () -> startSelectionActionMode(result);
// TODO do not trigger the animation if the change included only non-printable characters
final boolean didSelectionChange =
result != null && (mTextView.getSelectionStart() != result.mStart
@@ -386,15 +421,24 @@
mTextClassificationAsyncTask = null;
}
- private void resetTextClassificationHelper() {
+ private void resetTextClassificationHelper(int selectionStart, int selectionEnd) {
+ if (selectionStart < 0 || selectionEnd < 0) {
+ // Use selection indices
+ selectionStart = mTextView.getSelectionStart();
+ selectionEnd = mTextView.getSelectionEnd();
+ }
mTextClassificationHelper.init(
mTextView.getContext(),
mTextView.getTextClassifier(),
getText(mTextView),
- mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
+ selectionStart, selectionEnd,
mTextView.getTextLocales());
}
+ private void resetTextClassificationHelper() {
+ resetTextClassificationHelper(-1, -1);
+ }
+
private void cancelSmartSelectAnimation() {
if (mSmartSelectSprite != null) {
mSmartSelectSprite.cancelAnimation();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d9bc51f..1e17f34 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -77,6 +77,7 @@
import android.text.InputType;
import android.text.Layout;
import android.text.ParcelableSpan;
+import android.text.PremeasuredText;
import android.text.Selection;
import android.text.SpanWatcher;
import android.text.Spannable;
@@ -159,6 +160,7 @@
import android.view.inputmethod.InputMethodManager;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
import android.view.textservice.SpellCheckerSubtype;
import android.view.textservice.TextServicesManager;
import android.widget.RemoteViews.RemoteView;
@@ -167,6 +169,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.FastMath;
+import com.android.internal.util.Preconditions;
import com.android.internal.widget.EditableInputConnection;
import libcore.util.EmptyArray;
@@ -750,7 +753,10 @@
public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1;
/** @hide */
- @IntDef({AUTO_SIZE_TEXT_TYPE_NONE, AUTO_SIZE_TEXT_TYPE_UNIFORM})
+ @IntDef(prefix = { "AUTO_SIZE_TEXT_TYPE_" }, value = {
+ AUTO_SIZE_TEXT_TYPE_NONE,
+ AUTO_SIZE_TEXT_TYPE_UNIFORM
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface AutoSizeTextType {}
// Default minimum size for auto-sizing text in scaled pixels.
@@ -4861,6 +4867,10 @@
* Sets line spacing for this TextView. Each line other than the last line will have its height
* multiplied by {@code mult} and have {@code add} added to it.
*
+ * @param add The value in pixels that should be added to each line other than the last line.
+ * This will be applied after the multiplier
+ * @param mult The value by which each line height other than the last line will be multiplied
+ * by
*
* @attr ref android.R.styleable#TextView_lineSpacingExtra
* @attr ref android.R.styleable#TextView_lineSpacingMultiplier
@@ -5326,7 +5336,7 @@
if (imm != null) imm.restartInput(this);
} else if (type == BufferType.SPANNABLE || mMovement != null) {
text = mSpannableFactory.newSpannable(text);
- } else if (!(text instanceof CharWrapper)) {
+ } else if (!(text instanceof PremeasuredText || text instanceof CharWrapper)) {
text = TextUtils.stringOrSpannedString(text);
}
@@ -5610,10 +5620,15 @@
spannable = (Spannable) text;
} else {
spannable = mSpannableFactory.newSpannable(text);
- text = spannable;
}
SuggestionSpan[] spans = spannable.getSpans(0, text.length(), SuggestionSpan.class);
+ if (spans.length == 0) {
+ return text;
+ } else {
+ text = spannable;
+ }
+
for (int i = 0; i < spans.length; i++) {
spannable.removeSpan(spans[i]);
}
@@ -11142,6 +11157,20 @@
}
/**
+ * Starts an ActionMode for the specified TextLink.
+ *
+ * @return Whether or not we're attempting to start the action mode.
+ * @hide
+ */
+ public boolean requestActionMode(@NonNull TextLinks.TextLink link) {
+ Preconditions.checkNotNull(link);
+ if (mEditor != null) {
+ mEditor.startLinkActionModeAsync(link);
+ return true;
+ }
+ return false;
+ }
+ /**
* @hide
*/
protected void stopTextActionMode() {
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index ae6881e..cfec3f2 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -77,7 +77,10 @@
public static final int MODE_CLOCK = 2;
/** @hide */
- @IntDef({MODE_SPINNER, MODE_CLOCK})
+ @IntDef(prefix = { "MODE_" }, value = {
+ MODE_SPINNER,
+ MODE_CLOCK
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface TimePickerMode {}
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index d807120..edcf209 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -71,7 +71,10 @@
static final boolean localLOGV = false;
/** @hide */
- @IntDef({LENGTH_SHORT, LENGTH_LONG})
+ @IntDef(prefix = { "LENGTH_" }, value = {
+ LENGTH_SHORT,
+ LENGTH_LONG
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface Duration {}
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index 6fb02b1..efc9c02 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -91,14 +91,14 @@
STATE_TOP, // ActivityManager.PROCESS_STATE_TOP
STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
- STATE_TOP, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND
STATE_BACKUP, // ActivityManager.PROCESS_STATE_BACKUP
- STATE_HEAVY_WEIGHT, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
STATE_SERVICE, // ActivityManager.PROCESS_STATE_SERVICE
STATE_RECEIVER, // ActivityManager.PROCESS_STATE_RECEIVER
+ STATE_TOP, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
+ STATE_HEAVY_WEIGHT, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
STATE_HOME, // ActivityManager.PROCESS_STATE_HOME
STATE_LAST_ACTIVITY, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
STATE_CACHED_ACTIVITY, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 2ce7936..96ba2b0 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -81,10 +81,10 @@
public static final int STATE_IMPORTANT_FOREGROUND = 2;
public static final int STATE_IMPORTANT_BACKGROUND = 3;
public static final int STATE_BACKUP = 4;
- public static final int STATE_HEAVY_WEIGHT = 5;
- public static final int STATE_SERVICE = 6;
- public static final int STATE_SERVICE_RESTARTING = 7;
- public static final int STATE_RECEIVER = 8;
+ public static final int STATE_SERVICE = 5;
+ public static final int STATE_SERVICE_RESTARTING = 6;
+ public static final int STATE_RECEIVER = 7;
+ public static final int STATE_HEAVY_WEIGHT = 8;
public static final int STATE_HOME = 9;
public static final int STATE_LAST_ACTIVITY = 10;
public static final int STATE_CACHED_ACTIVITY = 11;
@@ -141,8 +141,8 @@
public static final int[] NON_CACHED_PROC_STATES = new int[] {
STATE_PERSISTENT, STATE_TOP, STATE_IMPORTANT_FOREGROUND,
- STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT,
- STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER
+ STATE_IMPORTANT_BACKGROUND, STATE_BACKUP,
+ STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, STATE_HEAVY_WEIGHT
};
public static final int[] BACKGROUND_PROC_STATES = new int[] {
@@ -152,13 +152,13 @@
public static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT,
STATE_TOP, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP,
- STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER,
- STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY,
+ STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER,
+ STATE_HEAVY_WEIGHT, STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY,
STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY
};
// Current version of the parcel format.
- private static final int PARCEL_VERSION = 22;
+ private static final int PARCEL_VERSION = 23;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535454;
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index d49d572..a075705 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -236,6 +236,7 @@
moveInMediaStore(visibleFileBefore, getFileForDocId(afterDocId, true));
if (!TextUtils.equals(docId, afterDocId)) {
+ scanFile(after);
return afterDocId;
} else {
return null;
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 57efae6..d3b4dbf 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -16,6 +16,9 @@
package com.android.internal.inputmethod;
+import static android.view.inputmethod.InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR;
+import static android.view.inputmethod.InputMethodManager.CONTROL_WINDOW_VIEW_HAS_FOCUS;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -26,6 +29,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.os.Build;
import android.os.LocaleList;
import android.os.RemoteException;
import android.provider.Settings;
@@ -33,6 +37,7 @@
import android.text.TextUtils.SimpleStringSplitter;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import android.util.Pair;
import android.util.Printer;
import android.util.Slog;
@@ -1510,4 +1515,20 @@
}
return locales;
}
+
+ public static boolean isSoftInputModeStateVisibleAllowed(
+ int targetSdkVersion, int controlFlags) {
+ if (targetSdkVersion < Build.VERSION_CODES.P) {
+ // for compatibility.
+ return true;
+ }
+ if ((controlFlags & CONTROL_WINDOW_VIEW_HAS_FOCUS) == 0) {
+ return false;
+ }
+ if ((controlFlags & CONTROL_WINDOW_IS_TEXT_EDITOR) == 0) {
+ return false;
+ }
+ return true;
+ }
+
}
diff --git a/core/java/com/android/internal/net/OWNERS b/core/java/com/android/internal/net/OWNERS
index e2064a8..10d44bd 100644
--- a/core/java/com/android/internal/net/OWNERS
+++ b/core/java/com/android/internal/net/OWNERS
@@ -2,5 +2,7 @@
ek@google.com
hugobenichi@google.com
+jchalard@google.com
jsharkey@android.com
lorenzo@google.com
+satk@google.com
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index a050a3c..6510a70 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -120,7 +120,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 170 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 171 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS;
@@ -8678,13 +8678,15 @@
} else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
// Persistent and other foreground states go here.
uidRunningState = PROCESS_STATE_FOREGROUND_SERVICE;
- } else if (procState <= ActivityManager.PROCESS_STATE_TOP_SLEEPING) {
- uidRunningState = PROCESS_STATE_TOP_SLEEPING;
} else if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
// Persistent and other foreground states go here.
uidRunningState = PROCESS_STATE_FOREGROUND;
} else if (procState <= ActivityManager.PROCESS_STATE_RECEIVER) {
uidRunningState = PROCESS_STATE_BACKGROUND;
+ } else if (procState <= ActivityManager.PROCESS_STATE_TOP_SLEEPING) {
+ uidRunningState = PROCESS_STATE_TOP_SLEEPING;
+ } else if (procState <= ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
+ uidRunningState = PROCESS_STATE_HEAVY_WEIGHT;
} else {
uidRunningState = PROCESS_STATE_CACHED;
}
@@ -10938,6 +10940,7 @@
final int numWakelocks = partialTimers == null ? 0 : partialTimers.size();
final int numClusters = mPowerProfile.getNumCpuClusters();
mWakeLockAllocationsUs = null;
+ final long startTimeMs = mClocks.uptimeMillis();
mKernelUidCpuFreqTimeReader.readDelta((uid, cpuFreqTimeMs) -> {
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
@@ -11003,6 +11006,11 @@
}
});
+ final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
+ if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
+ Slog.d(TAG, "Reading cpu freq times took " + elapsedTimeMs + "ms");
+ }
+
if (mWakeLockAllocationsUs != null) {
for (int i = 0; i < numWakelocks; ++i) {
final Uid u = partialTimers.get(i).mUid;
@@ -11228,7 +11236,9 @@
reportChangesToStatsLog(mHaveBatteryLevel ? mHistoryCur : null,
status, plugType, level, temp);
- final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
+ final boolean onBattery =
+ plugType == BATTERY_PLUGGED_NONE &&
+ status != BatteryManager.BATTERY_STATUS_UNKNOWN;
final long uptime = mClocks.uptimeMillis();
final long elapsedRealtime = mClocks.elapsedRealtime();
if (!mHaveBatteryLevel) {
@@ -11262,7 +11272,8 @@
mRecordingHistory = true;
startRecordingHistory(elapsedRealtime, uptime, true);
}
- } else if (level < 96) {
+ } else if (level < 96 &&
+ status != BatteryManager.BATTERY_STATUS_UNKNOWN) {
if (!mRecordingHistory) {
mRecordingHistory = true;
startRecordingHistory(elapsedRealtime, uptime, true);
@@ -11400,9 +11411,12 @@
addHistoryRecordLocked(elapsedRealtime, uptime);
}
}
- if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
- // We don't record history while we are plugged in and fully charged.
- // The next time we are unplugged, history will be cleared.
+ if (!onBattery &&
+ (status == BatteryManager.BATTERY_STATUS_FULL ||
+ status == BatteryManager.BATTERY_STATUS_UNKNOWN)) {
+ // We don't record history while we are plugged in and fully charged
+ // (or when battery is not present). The next time we are
+ // unplugged, history will be cleared.
mRecordingHistory = DEBUG;
}
diff --git a/core/java/com/android/internal/os/ByteTransferPipe.java b/core/java/com/android/internal/os/ByteTransferPipe.java
new file mode 100644
index 0000000..6489894
--- /dev/null
+++ b/core/java/com/android/internal/os/ByteTransferPipe.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Helper class to get byte data through a pipe from a client app. Also {@see TransferPipe}.
+ */
+public class ByteTransferPipe extends TransferPipe {
+ static final String TAG = "ByteTransferPipe";
+
+ private ByteArrayOutputStream mOutputStream;
+
+ public ByteTransferPipe() throws IOException {
+ super();
+ }
+
+ public ByteTransferPipe(String bufferPrefix) throws IOException {
+ super(bufferPrefix, "ByteTransferPipe");
+ }
+
+ @Override
+ protected OutputStream getNewOutputStream() {
+ mOutputStream = new ByteArrayOutputStream();
+ return mOutputStream;
+ }
+
+ public byte[] get() throws IOException {
+ go(null);
+ return mOutputStream.toByteArray();
+ }
+}
diff --git a/core/java/com/android/internal/os/TransferPipe.java b/core/java/com/android/internal/os/TransferPipe.java
index 738ecc0b..1c09bd6 100644
--- a/core/java/com/android/internal/os/TransferPipe.java
+++ b/core/java/com/android/internal/os/TransferPipe.java
@@ -34,11 +34,12 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
/**
* Helper for transferring data through a pipe from a client app.
*/
-public final class TransferPipe implements Runnable, Closeable {
+public class TransferPipe implements Runnable, Closeable {
static final String TAG = "TransferPipe";
static final boolean DEBUG = false;
@@ -64,7 +65,11 @@
}
public TransferPipe(String bufferPrefix) throws IOException {
- mThread = new Thread(this, "TransferPipe");
+ this(bufferPrefix, "TransferPipe");
+ }
+
+ protected TransferPipe(String bufferPrefix, String threadName) throws IOException {
+ mThread = new Thread(this, threadName);
mFds = ParcelFileDescriptor.createPipe();
mBufferPrefix = bufferPrefix;
}
@@ -234,11 +239,15 @@
}
}
+ protected OutputStream getNewOutputStream() {
+ return new FileOutputStream(mOutFd);
+ }
+
@Override
public void run() {
final byte[] buffer = new byte[1024];
final FileInputStream fis;
- final FileOutputStream fos;
+ final OutputStream fos;
synchronized (this) {
ParcelFileDescriptor readFd = getReadFd();
@@ -247,7 +256,7 @@
return;
}
fis = new FileInputStream(readFd.getFileDescriptor());
- fos = new FileOutputStream(mOutFd);
+ fos = getNewOutputStream();
}
if (DEBUG) Slog.i(TAG, "Ready to read pipe...");
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index f983de1..7985e57 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -290,11 +290,11 @@
if (cur instanceof ArraySet) {
ArraySet<T> arraySet = (ArraySet<T>) cur;
for (int i = 0; i < size; i++) {
- action.accept(arraySet.valueAt(i));
+ action.acceptOrThrow(arraySet.valueAt(i));
}
} else {
for (T t : cur) {
- action.accept(t);
+ action.acceptOrThrow(t);
}
}
} catch (Exception e) {
diff --git a/core/java/com/android/internal/util/FunctionalUtils.java b/core/java/com/android/internal/util/FunctionalUtils.java
index eb92c1c..82ac241 100644
--- a/core/java/com/android/internal/util/FunctionalUtils.java
+++ b/core/java/com/android/internal/util/FunctionalUtils.java
@@ -16,6 +16,9 @@
package com.android.internal.util;
+import android.os.RemoteException;
+
+import java.util.function.Consumer;
import java.util.function.Supplier;
/**
@@ -25,6 +28,21 @@
private FunctionalUtils() {}
/**
+ * Converts a lambda expression that throws a checked exception(s) into a regular
+ * {@link Consumer} by propagating any checked exceptions as {@link RuntimeException}
+ */
+ public static <T> Consumer<T> uncheckExceptions(ThrowingConsumer<T> action) {
+ return action;
+ }
+
+ /**
+ *
+ */
+ public static <T> Consumer<T> ignoreRemoteException(RemoteExceptionIgnoringConsumer<T> action) {
+ return action;
+ }
+
+ /**
* An equivalent of {@link Runnable} that allows throwing checked exceptions
*
* This can be used to specify a lambda argument without forcing all the checked exceptions
@@ -47,13 +65,43 @@
}
/**
- * An equivalent of {@link java.util.function.Consumer} that allows throwing checked exceptions
+ * A {@link Consumer} that allows throwing checked exceptions from its single abstract method.
*
- * This can be used to specify a lambda argument without forcing all the checked exceptions
- * to be handled within it
+ * Can be used together with {@link #uncheckExceptions} to effectively turn a lambda expression
+ * that throws a checked exception into a regular {@link Consumer}
*/
@FunctionalInterface
- public interface ThrowingConsumer<T> {
- void accept(T t) throws Exception;
+ @SuppressWarnings("FunctionalInterfaceMethodChanged")
+ public interface ThrowingConsumer<T> extends Consumer<T> {
+ void acceptOrThrow(T t) throws Exception;
+
+ @Override
+ default void accept(T t) {
+ try {
+ acceptOrThrow(t);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ /**
+ * A {@link Consumer} that automatically ignores any {@link RemoteException}s.
+ *
+ * Used by {@link #ignoreRemoteException}
+ */
+ @FunctionalInterface
+ @SuppressWarnings("FunctionalInterfaceMethodChanged")
+ public interface RemoteExceptionIgnoringConsumer<T> extends Consumer<T> {
+ void acceptOrThrow(T t) throws RemoteException;
+
+ @Override
+ default void accept(T t) {
+ try {
+ acceptOrThrow(t);
+ } catch (RemoteException ex) {
+ // ignore
+ }
+ }
}
}
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index e5d5716..91c76af 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -494,4 +494,38 @@
return value;
}
+
+ /**
+ * Ensures that all elements in the argument integer array are within the inclusive range
+ *
+ * @param value an integer array of values
+ * @param lower the lower endpoint of the inclusive range
+ * @param upper the upper endpoint of the inclusive range
+ * @param valueName the name of the argument to use if the check fails
+ *
+ * @return the validated integer array
+ *
+ * @throws IllegalArgumentException if any of the elements in {@code value} were out of range
+ * @throws NullPointerException if the {@code value} was {@code null}
+ */
+ public static int[] checkArrayElementsInRange(int[] value, int lower, int upper,
+ String valueName) {
+ checkNotNull(value, valueName + " must not be null");
+
+ for (int i = 0; i < value.length; ++i) {
+ int v = value[i];
+
+ if (v < lower) {
+ throw new IllegalArgumentException(
+ String.format("%s[%d] is out of range of [%d, %d] (too low)",
+ valueName, i, lower, upper));
+ } else if (v > upper) {
+ throw new IllegalArgumentException(
+ String.format("%s[%d] is out of range of [%d, %d] (too high)",
+ valueName, i, lower, upper));
+ }
+ }
+
+ return value;
+ }
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index ca8624d..5e0a986b 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -56,12 +56,14 @@
in ResultReceiver resultReceiver);
// If windowToken is null, this just does startInput(). Otherwise this reports that a window
// has gained focus, and if 'attribute' is non-null then also does startInput.
+ // @NonNull
InputBindResult startInputOrWindowGainedFocus(
/* @InputMethodClient.StartInputReason */ int startInputReason,
in IInputMethodClient client, in IBinder windowToken, int controlFlags,
/* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
int windowFlags, in EditorInfo attribute, IInputContext inputContext,
- /* @InputConnectionInspector.MissingMethodFlags */ int missingMethodFlags);
+ /* @InputConnectionInspector.MissingMethodFlags */ int missingMethodFlags,
+ int unverifiedTargetSdkVersion);
void showInputMethodPickerFromClient(in IInputMethodClient client,
int auxiliarySubtypeMode);
@@ -80,7 +82,6 @@
boolean switchToLastInputMethod(in IBinder token);
boolean switchToNextInputMethod(in IBinder token, boolean onlyCurrentIme);
boolean shouldOfferSwitchingToNextInputMethod(in IBinder token);
- boolean setInputMethodEnabled(String id, boolean enabled);
void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
int getInputMethodWindowVisibleHeight();
void clearLastInputMethodWindowForTransition(in IBinder token);
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 3a3e56d..74dbaba 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -16,17 +16,134 @@
package com.android.internal.view;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.UserHandle;
import android.view.InputChannel;
+import java.lang.annotation.Retention;
+
/**
* Bundle of information returned by input method manager about a successful
* binding to an input method.
*/
public final class InputBindResult implements Parcelable {
- static final String TAG = "InputBindResult";
-
+
+ @Retention(SOURCE)
+ @IntDef({
+ ResultCode.SUCCESS_WITH_IME_SESSION,
+ ResultCode.SUCCESS_WAITING_IME_SESSION,
+ ResultCode.SUCCESS_WAITING_IME_BINDING,
+ ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
+ ResultCode.ERROR_NULL,
+ ResultCode.ERROR_NO_IME,
+ ResultCode.ERROR_INVALID_PACKAGE_NAME,
+ ResultCode.ERROR_SYSTEM_NOT_READY,
+ ResultCode.ERROR_IME_NOT_CONNECTED,
+ ResultCode.ERROR_INVALID_USER,
+ ResultCode.ERROR_NULL_EDITOR_INFO,
+ ResultCode.ERROR_NOT_IME_TARGET_WINDOW,
+ })
+ public @interface ResultCode {
+ /**
+ * Indicates that everything in this result object including {@link #method} is valid.
+ */
+ int SUCCESS_WITH_IME_SESSION = 0;
+ /**
+ * Indicates that this is a temporary binding until the
+ * {@link android.inputmethodservice.InputMethodService} (IMS) establishes a valid session
+ * to {@link com.android.server.InputMethodManagerService} (IMMS).
+ *
+ * <p>Note that in this state the IMS is already bound to IMMS but the logical session
+ * is not yet established on top of the IPC channel.</p>
+ *
+ * <p>Some of fields such as {@link #channel} is not yet available.</p>
+ *
+ * @see android.inputmethodservice.InputMethodService##onCreateInputMethodSessionInterface()
+ **/
+ int SUCCESS_WAITING_IME_SESSION = 1;
+ /**
+ * Indicates that this is a temporary binding until the
+ * {@link android.inputmethodservice.InputMethodService} (IMS) establishes a valid session
+ * to {@link com.android.server.InputMethodManagerService} (IMMS).
+ *
+ * <p>Note that in this state the IMMS has already initiated a connection to the IMS but
+ * the binding process is not completed yet.</p>
+ *
+ * <p>Some of fields such as {@link #channel} is not yet available.</p>
+ * @see android.content.ServiceConnection#onServiceConnected(ComponentName, IBinder)
+ */
+ int SUCCESS_WAITING_IME_BINDING = 2;
+ /**
+ * Indicates that this is not intended for starting input but just for reporting window
+ * focus change from the application process.
+ *
+ * <p>All other fields do not have meaningful value.</p>
+ */
+ int SUCCESS_REPORT_WINDOW_FOCUS_ONLY = 3;
+ /**
+ * Indicates somehow
+ * {@link com.android.server.InputMethodManagerService#startInputOrWindowGainedFocus} is
+ * trying to return null {@link InputBindResult}, which must never happen.
+ */
+ int ERROR_NULL = 4;
+ /**
+ * Indicates that {@link com.android.server.InputMethodManagerService} recognizes no IME.
+ */
+ int ERROR_NO_IME = 5;
+ /**
+ * Indicates that {@link android.view.inputmethod.EditorInfo#packageName} does not match
+ * the caller UID.
+ *
+ * @see android.view.inputmethod.EditorInfo#packageName
+ */
+ int ERROR_INVALID_PACKAGE_NAME = 6;
+ /**
+ * Indicates that the system is still in an early stage of the boot process and any 3rd
+ * party application is not allowed to run.
+ *
+ * @see com.android.server.SystemService#PHASE_THIRD_PARTY_APPS_CAN_START
+ */
+ int ERROR_SYSTEM_NOT_READY = 7;
+ /**
+ * Indicates that {@link com.android.server.InputMethodManagerService} tried to connect to
+ * an {@link android.inputmethodservice.InputMethodService} but failed.
+ *
+ * @see android.content.Context#bindServiceAsUser(Intent, ServiceConnection, int, UserHandle)
+ */
+ int ERROR_IME_NOT_CONNECTED = 8;
+ /**
+ * Indicates that the caller is not the foreground user (or does not have
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission).
+ */
+ int ERROR_INVALID_USER = 9;
+ /**
+ * Indicates that the caller should have specified non-null
+ * {@link android.view.inputmethod.EditorInfo}.
+ */
+ int ERROR_NULL_EDITOR_INFO = 10;
+ /**
+ * Indicates that the target window the client specified cannot be the IME target right now.
+ *
+ * <p>Due to the asynchronous nature of Android OS, we cannot completely avoid this error.
+ * The client should try to restart input when its {@link android.view.Window} is focused
+ * again.</p>
+ *
+ * @see com.android.server.wm.WindowManagerService#inputMethodClientHasFocus(IInputMethodClient)
+ */
+ int ERROR_NOT_IME_TARGET_WINDOW = 11;
+ }
+
+ @ResultCode
+ public final int result;
+
/**
* The input method service.
*/
@@ -53,16 +170,19 @@
*/
public final int userActionNotificationSequenceNumber;
- public InputBindResult(IInputMethodSession _method, InputChannel _channel,
+ public InputBindResult(@ResultCode int _result,
+ IInputMethodSession _method, InputChannel _channel,
String _id, int _sequence, int _userActionNotificationSequenceNumber) {
+ result = _result;
method = _method;
channel = _channel;
id = _id;
sequence = _sequence;
userActionNotificationSequenceNumber = _userActionNotificationSequenceNumber;
}
-
+
InputBindResult(Parcel source) {
+ result = source.readInt();
method = IInputMethodSession.Stub.asInterface(source.readStrongBinder());
if (source.readInt() != 0) {
channel = InputChannel.CREATOR.createFromParcel(source);
@@ -76,9 +196,9 @@
@Override
public String toString() {
- return "InputBindResult{" + method + " " + id
- + " sequence:" + sequence
- + " userActionNotificationSequenceNumber:" + userActionNotificationSequenceNumber
+ return "InputBindResult{result=" + getResultString() + " method="+ method + " id=" + id
+ + " sequence=" + sequence
+ + " userActionNotificationSequenceNumber=" + userActionNotificationSequenceNumber
+ "}";
}
@@ -90,6 +210,7 @@
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(result);
dest.writeStrongInterface(method);
if (channel != null) {
dest.writeInt(1);
@@ -122,4 +243,72 @@
public int describeContents() {
return channel != null ? channel.describeContents() : 0;
}
+
+ public String getResultString() {
+ switch (result) {
+ case ResultCode.SUCCESS_WITH_IME_SESSION:
+ return "SUCCESS_WITH_IME_SESSION";
+ case ResultCode.SUCCESS_WAITING_IME_SESSION:
+ return "SUCCESS_WAITING_IME_SESSION";
+ case ResultCode.SUCCESS_WAITING_IME_BINDING:
+ return "SUCCESS_WAITING_IME_BINDING";
+ case ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY:
+ return "SUCCESS_REPORT_WINDOW_FOCUS_ONLY";
+ case ResultCode.ERROR_NULL:
+ return "ERROR_NULL";
+ case ResultCode.ERROR_NO_IME:
+ return "ERROR_NO_IME";
+ case ResultCode.ERROR_INVALID_PACKAGE_NAME:
+ return "ERROR_INVALID_PACKAGE_NAME";
+ case ResultCode.ERROR_SYSTEM_NOT_READY:
+ return "ERROR_SYSTEM_NOT_READY";
+ case ResultCode.ERROR_IME_NOT_CONNECTED:
+ return "ERROR_IME_NOT_CONNECTED";
+ case ResultCode.ERROR_INVALID_USER:
+ return "ERROR_INVALID_USER";
+ case ResultCode.ERROR_NULL_EDITOR_INFO:
+ return "ERROR_NULL_EDITOR_INFO";
+ case ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
+ return "ERROR_NOT_IME_TARGET_WINDOW";
+ default:
+ return "Unknown(" + result + ")";
+ }
+ }
+
+ private static InputBindResult error(@ResultCode int result) {
+ return new InputBindResult(result, null, null, null, -1, -1);
+ }
+
+ /**
+ * Predefined error object for {@link ResultCode#ERROR_NULL}.
+ */
+ public static final InputBindResult NULL = error(ResultCode.ERROR_NULL);
+ /**
+ * Predefined error object for {@link ResultCode#NO_IME}.
+ */
+ public static final InputBindResult NO_IME = error(ResultCode.ERROR_NO_IME);
+ /**
+ * Predefined error object for {@link ResultCode#ERROR_INVALID_PACKAGE_NAME}.
+ */
+ public static final InputBindResult INVALID_PACKAGE_NAME =
+ error(ResultCode.ERROR_INVALID_PACKAGE_NAME);
+ /**
+ * Predefined error object for {@link ResultCode#ERROR_NULL_EDITOR_INFO}.
+ */
+ public static final InputBindResult NULL_EDITOR_INFO = error(ResultCode.ERROR_NULL_EDITOR_INFO);
+ /**
+ * Predefined error object for {@link ResultCode#ERROR_NOT_IME_TARGET_WINDOW}.
+ */
+ public static final InputBindResult NOT_IME_TARGET_WINDOW =
+ error(ResultCode.ERROR_NOT_IME_TARGET_WINDOW);
+ /**
+ * Predefined error object for {@link ResultCode#ERROR_IME_NOT_CONNECTED}.
+ */
+ public static final InputBindResult IME_NOT_CONNECTED =
+ error(ResultCode.ERROR_IME_NOT_CONNECTED);
+ /**
+ * Predefined error object for {@link ResultCode#ERROR_INVALID_USER}.
+ */
+ public static final InputBindResult INVALID_USER = error(ResultCode.ERROR_INVALID_USER);
+
}
diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java
index 8f80bfe..0d2b29b 100644
--- a/core/java/com/android/internal/view/menu/ListMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java
@@ -50,6 +50,7 @@
private TextView mShortcutView;
private ImageView mSubMenuArrowView;
private ImageView mGroupDivider;
+ private LinearLayout mContent;
private Drawable mBackground;
private int mTextAppearance;
@@ -114,6 +115,8 @@
mSubMenuArrowView.setImageDrawable(mSubMenuArrow);
}
mGroupDivider = findViewById(com.android.internal.R.id.group_divider);
+
+ mContent = findViewById(com.android.internal.R.id.content);
}
public void initialize(MenuItemImpl itemData, int menuType) {
@@ -131,6 +134,18 @@
setContentDescription(itemData.getContentDescription());
}
+ private void addContentView(View v) {
+ addContentView(v, -1);
+ }
+
+ private void addContentView(View v, int index) {
+ if (mContent != null) {
+ mContent.addView(v, index);
+ } else {
+ addView(v, index);
+ }
+ }
+
public void setForceShowIcon(boolean forceShow) {
mPreserveIconSpacing = mForceShowIcon = forceShow;
}
@@ -270,7 +285,7 @@
LayoutInflater inflater = getInflater();
mIconView = (ImageView) inflater.inflate(com.android.internal.R.layout.list_menu_item_icon,
this, false);
- addView(mIconView, 0);
+ addContentView(mIconView, 0);
}
private void insertRadioButton() {
@@ -278,7 +293,7 @@
mRadioButton =
(RadioButton) inflater.inflate(com.android.internal.R.layout.list_menu_item_radio,
this, false);
- addView(mRadioButton);
+ addContentView(mRadioButton);
}
private void insertCheckBox() {
@@ -286,7 +301,7 @@
mCheckBox =
(CheckBox) inflater.inflate(com.android.internal.R.layout.list_menu_item_checkbox,
this, false);
- addView(mCheckBox);
+ addContentView(mCheckBox);
}
public boolean prefersCondensedTitle() {
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index ee16ab6..164a745 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -17,6 +17,10 @@
package com.android.internal.widget;
import android.app.trust.IStrongAuthTracker;
+import android.os.Bundle;
+import android.security.recoverablekeystore.KeyEntryRecoveryData;
+import android.security.recoverablekeystore.KeyStoreRecoveryData;
+import android.security.recoverablekeystore.KeyStoreRecoveryMetadata;
import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.VerifyCredentialResponse;
@@ -52,4 +56,22 @@
boolean setLockCredentialWithToken(String credential, int type, long tokenHandle,
in byte[] token, int requestedQuality, int userId);
void unlockUserWithToken(long tokenHandle, in byte[] token, int userId);
+
+ // RecoverableKeyStoreLoader methods.
+ // {@code ServiceSpecificException} may be thrown to signal an error, which caller can
+ // convert to {@code RecoverableKeyStoreLoader}.
+ void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList,
+ int userId);
+ KeyStoreRecoveryData getRecoveryData(in byte[] account, int userId);
+ void setServerParameters(long serverParameters, int userId);
+ void setRecoveryStatus(in String packageName, in String[] aliases, int status, int userId);
+ void setRecoverySecretTypes(in int[] secretTypes, int userId);
+ int[] getRecoverySecretTypes(int userId);
+ int[] getPendingRecoverySecretTypes(int userId);
+ void recoverySecretAvailable(in KeyStoreRecoveryMetadata recoverySecret, int userId);
+ byte[] startRecoverySession(in String sessionId,
+ in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge,
+ in List<KeyStoreRecoveryMetadata> secrets, int userId);
+ void recoverKeys(in String sessionId, in byte[] recoveryKeyBlob,
+ in List<KeyEntryRecoveryData> applicationKeys, int userId);
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 4be6b28..e871003 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -27,7 +27,6 @@
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.AsyncTask;
-import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -35,7 +34,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.IStorageManager;
@@ -964,9 +962,12 @@
/**
* Retrieves whether the current profile and device locks can be unified.
+ * @param userHandle profile user handle.
*/
public boolean isSeparateProfileChallengeAllowedToUnify(int userHandle) {
- return getDevicePolicyManager().isProfileActivePasswordSufficientForParent(userHandle);
+ return getDevicePolicyManager().isProfileActivePasswordSufficientForParent(userHandle)
+ && !getUserManager().hasUserRestriction(
+ UserManager.DISALLOW_UNIFIED_PASSWORD, UserHandle.of(userHandle));
}
private boolean hasSeparateChallenge(int userHandle) {
diff --git a/core/java/com/android/server/net/OWNERS b/core/java/com/android/server/net/OWNERS
index 74f39a1..6f77e04 100644
--- a/core/java/com/android/server/net/OWNERS
+++ b/core/java/com/android/server/net/OWNERS
@@ -2,4 +2,6 @@
ek@google.com
hugobenichi@google.com
+jchalard@google.com
lorenzo@google.com
+satk@google.com
diff --git a/core/java/org/chromium/arc/EventLogTags.logtags b/core/java/org/chromium/arc/EventLogTags.logtags
new file mode 100644
index 0000000..1b7160e
--- /dev/null
+++ b/core/java/org/chromium/arc/EventLogTags.logtags
@@ -0,0 +1,11 @@
+# See system/core/logcat/event.logtags for a description of the format of this file.
+
+option java_package org.chromium.arc
+
+# We use ID range 300000-399999 for ARC.
+# In case of conflicts build will fail, so we do not need to worry too much
+# about it.
+
+# Emitted in ARC system events, such as start of ARC services.
+# These events will be watched in automations like autotests.
+300000 arc_system_event (event|3)
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 8df0fb8..297bae2 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -123,6 +123,7 @@
"android_graphics_Picture.cpp",
"android/graphics/Bitmap.cpp",
"android/graphics/BitmapFactory.cpp",
+ "android/graphics/ByteBufferStreamAdaptor.cpp",
"android/graphics/Camera.cpp",
"android/graphics/CanvasProperty.cpp",
"android/graphics/ColorFilter.cpp",
@@ -134,6 +135,7 @@
"android/graphics/GraphicBuffer.cpp",
"android/graphics/Graphics.cpp",
"android/graphics/HarfBuzzNGFaceSkia.cpp",
+ "android/graphics/ImageDecoder.cpp",
"android/graphics/Interpolator.cpp",
"android/graphics/MaskFilter.cpp",
"android/graphics/Matrix.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 9e907bd..b5d1868 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -57,10 +57,12 @@
extern int register_android_graphics_Bitmap(JNIEnv*);
extern int register_android_graphics_BitmapFactory(JNIEnv*);
extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*);
+extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env);
extern int register_android_graphics_Camera(JNIEnv* env);
extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env);
extern int register_android_graphics_GraphicBuffer(JNIEnv* env);
extern int register_android_graphics_Graphics(JNIEnv* env);
+extern int register_android_graphics_ImageDecoder(JNIEnv*);
extern int register_android_graphics_Interpolator(JNIEnv* env);
extern int register_android_graphics_MaskFilter(JNIEnv* env);
extern int register_android_graphics_Movie(JNIEnv* env);
@@ -644,6 +646,7 @@
char methodTraceFileBuf[sizeof("-Xmethod-trace-file:") + PROPERTY_VALUE_MAX];
char methodTraceFileSizeBuf[sizeof("-Xmethod-trace-file-size:") + PROPERTY_VALUE_MAX];
std::string fingerprintBuf;
+ char jdwpProviderBuf[sizeof("-XjdwpProvider:") - 1 + PROPERTY_VALUE_MAX];
bool checkJni = false;
property_get("dalvik.vm.checkjni", propBuf, "");
@@ -766,9 +769,15 @@
* Set suspend=y to pause during VM init and use android ADB transport.
*/
if (zygote) {
- addOption("-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y");
+ addOption("-XjdwpOptions:suspend=n,server=y");
}
+ // Set the JDWP provider. By default let the runtime choose.
+ parseRuntimeOption("dalvik.vm.jdwp-provider",
+ jdwpProviderBuf,
+ "-XjdwpProvider:",
+ "default");
+
parseRuntimeOption("dalvik.vm.lockprof.threshold",
lockProfThresholdBuf,
"-Xlockprofthreshold:");
@@ -1380,6 +1389,7 @@
REG_JNI(register_android_graphics_Bitmap),
REG_JNI(register_android_graphics_BitmapFactory),
REG_JNI(register_android_graphics_BitmapRegionDecoder),
+ REG_JNI(register_android_graphics_ByteBufferStreamAdaptor),
REG_JNI(register_android_graphics_Camera),
REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
REG_JNI(register_android_graphics_CanvasProperty),
@@ -1387,6 +1397,7 @@
REG_JNI(register_android_graphics_DrawFilter),
REG_JNI(register_android_graphics_FontFamily),
REG_JNI(register_android_graphics_GraphicBuffer),
+ REG_JNI(register_android_graphics_ImageDecoder),
REG_JNI(register_android_graphics_Interpolator),
REG_JNI(register_android_graphics_MaskFilter),
REG_JNI(register_android_graphics_Matrix),
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 2e8c27a..79aa5ac 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -47,9 +47,6 @@
jfieldID gBitmap_ninePatchInsetsFieldID;
-jclass gInsetStruct_class;
-jmethodID gInsetStruct_constructorMethodID;
-
jclass gBitmapConfig_class;
jmethodID gBitmapConfig_nativeToConfigMethodID;
@@ -99,43 +96,6 @@
return jstr;
}
-static void scaleDivRange(int32_t* divs, int count, float scale, int maxValue) {
- for (int i = 0; i < count; i++) {
- divs[i] = int32_t(divs[i] * scale + 0.5f);
- if (i > 0 && divs[i] == divs[i - 1]) {
- divs[i]++; // avoid collisions
- }
- }
-
- if (CC_UNLIKELY(divs[count - 1] > maxValue)) {
- // if the collision avoidance above put some divs outside the bounds of the bitmap,
- // slide outer stretchable divs inward to stay within bounds
- int highestAvailable = maxValue;
- for (int i = count - 1; i >= 0; i--) {
- divs[i] = highestAvailable;
- if (i > 0 && divs[i] <= divs[i-1]){
- // keep shifting
- highestAvailable = divs[i] - 1;
- } else {
- break;
- }
- }
- }
-}
-
-static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale,
- int scaledWidth, int scaledHeight) {
- chunk->paddingLeft = int(chunk->paddingLeft * scale + 0.5f);
- chunk->paddingTop = int(chunk->paddingTop * scale + 0.5f);
- chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f);
- chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f);
-
- // The max value for the divRange is one pixel less than the actual max to ensure that the size
- // of the last div is not zero. A div of size 0 is considered invalid input and will not render.
- scaleDivRange(chunk->getXDivs(), chunk->numXDivs, scale, scaledWidth - 1);
- scaleDivRange(chunk->getYDivs(), chunk->numYDivs, scale, scaledHeight - 1);
-}
-
class ScaleCheckingAllocator : public SkBitmap::HeapAllocator {
public:
ScaleCheckingAllocator(float scale, int size)
@@ -425,10 +385,18 @@
return nullObjectReturn("codec->getAndroidPixels() failed.");
}
+ // This is weird so let me explain: we could use the scale parameter
+ // directly, but for historical reasons this is how the corresponding
+ // Dalvik code has always behaved. We simply recreate the behavior here.
+ // The result is slightly different from simply using scale because of
+ // the 0.5f rounding bias applied when computing the target image size
+ const float scaleX = scaledWidth / float(decodingBitmap.width());
+ const float scaleY = scaledHeight / float(decodingBitmap.height());
+
jbyteArray ninePatchChunk = NULL;
if (peeker.mPatch != NULL) {
if (willScale) {
- scaleNinePatchChunk(peeker.mPatch, scale, scaledWidth, scaledHeight);
+ peeker.scale(scaleX, scaleY, scaledWidth, scaledHeight);
}
size_t ninePatchArraySize = peeker.mPatch->serializedSize();
@@ -448,12 +416,7 @@
jobject ninePatchInsets = NULL;
if (peeker.mHasInsets) {
- ninePatchInsets = env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID,
- peeker.mOpticalInsets[0], peeker.mOpticalInsets[1],
- peeker.mOpticalInsets[2], peeker.mOpticalInsets[3],
- peeker.mOutlineInsets[0], peeker.mOutlineInsets[1],
- peeker.mOutlineInsets[2], peeker.mOutlineInsets[3],
- peeker.mOutlineRadius, peeker.mOutlineAlpha, scale);
+ ninePatchInsets = peeker.createNinePatchInsets(env, scale);
if (ninePatchInsets == NULL) {
return nullObjectReturn("nine patch insets == null");
}
@@ -464,14 +427,6 @@
SkBitmap outputBitmap;
if (willScale) {
- // This is weird so let me explain: we could use the scale parameter
- // directly, but for historical reasons this is how the corresponding
- // Dalvik code has always behaved. We simply recreate the behavior here.
- // The result is slightly different from simply using scale because of
- // the 0.5f rounding bias applied when computing the target image size
- const float sx = scaledWidth / float(decodingBitmap.width());
- const float sy = scaledHeight / float(decodingBitmap.height());
-
// Set the allocator for the outputBitmap.
SkBitmap::Allocator* outputAllocator;
if (javaBitmap != nullptr) {
@@ -501,20 +456,14 @@
paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering
SkCanvas canvas(outputBitmap, SkCanvas::ColorBehavior::kLegacy);
- canvas.scale(sx, sy);
+ canvas.scale(scaleX, scaleY);
canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
} else {
outputBitmap.swap(decodingBitmap);
}
if (padding) {
- if (peeker.mPatch != NULL) {
- GraphicsJNI::set_jrect(env, padding,
- peeker.mPatch->paddingLeft, peeker.mPatch->paddingTop,
- peeker.mPatch->paddingRight, peeker.mPatch->paddingBottom);
- } else {
- GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
- }
+ peeker.getPadding(env, padding);
}
// If we get here, the outputBitmap should have an installed pixelref.
@@ -705,11 +654,6 @@
gBitmap_ninePatchInsetsFieldID = GetFieldIDOrDie(env, bitmap_class, "mNinePatchInsets",
"Landroid/graphics/NinePatch$InsetStruct;");
- gInsetStruct_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
- "android/graphics/NinePatch$InsetStruct"));
- gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "<init>",
- "(IIIIIIIIFIF)V");
-
gBitmapConfig_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
"android/graphics/Bitmap$Config"));
gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class,
diff --git a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp b/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
new file mode 100644
index 0000000..115edd4
--- /dev/null
+++ b/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
@@ -0,0 +1,323 @@
+#include "ByteBufferStreamAdaptor.h"
+#include "core_jni_helpers.h"
+
+#include <SkStream.h>
+
+using namespace android;
+
+static jmethodID gByteBuffer_getMethodID;
+static jmethodID gByteBuffer_setPositionMethodID;
+
+static JNIEnv* get_env_or_die(JavaVM* jvm) {
+ JNIEnv* env;
+ if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", jvm);
+ }
+ return env;
+}
+
+class ByteBufferStream : public SkStreamAsset {
+private:
+ ByteBufferStream(JavaVM* jvm, jobject jbyteBuffer, size_t initialPosition, size_t length,
+ jbyteArray storage)
+ : mJvm(jvm)
+ , mByteBuffer(jbyteBuffer)
+ , mPosition(0)
+ , mInitialPosition(initialPosition)
+ , mLength(length)
+ , mStorage(storage) {}
+
+public:
+ static ByteBufferStream* Create(JavaVM* jvm, JNIEnv* env, jobject jbyteBuffer,
+ size_t position, size_t length) {
+ // This object outlives its native method call.
+ jbyteBuffer = env->NewGlobalRef(jbyteBuffer);
+ if (!jbyteBuffer) {
+ return nullptr;
+ }
+
+ jbyteArray storage = env->NewByteArray(kStorageSize);
+ if (!storage) {
+ env->DeleteGlobalRef(jbyteBuffer);
+ return nullptr;
+ }
+
+ // This object outlives its native method call.
+ storage = static_cast<jbyteArray>(env->NewGlobalRef(storage));
+ if (!storage) {
+ env->DeleteGlobalRef(jbyteBuffer);
+ return nullptr;
+ }
+
+ return new ByteBufferStream(jvm, jbyteBuffer, position, length, storage);
+ }
+
+ ~ByteBufferStream() override {
+ auto* env = get_env_or_die(mJvm);
+ env->DeleteGlobalRef(mByteBuffer);
+ env->DeleteGlobalRef(mStorage);
+ }
+
+ size_t read(void* buffer, size_t size) override {
+ if (size > mLength - mPosition) {
+ size = mLength - mPosition;
+ }
+ if (!size) {
+ return 0;
+ }
+
+ if (!buffer) {
+ return this->setPosition(mPosition + size);
+ }
+
+ auto* env = get_env_or_die(mJvm);
+ size_t bytesRead = 0;
+ do {
+ const size_t requested = (size > kStorageSize) ? kStorageSize : size;
+ const jint jrequested = static_cast<jint>(requested);
+ env->CallObjectMethod(mByteBuffer, gByteBuffer_getMethodID, mStorage, 0, jrequested);
+ if (env->ExceptionCheck()) {
+ ALOGE("Error in ByteBufferStream::read - was the ByteBuffer modified externally?");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ mPosition = mLength;
+ return bytesRead;
+ }
+
+ env->GetByteArrayRegion(mStorage, 0, requested, reinterpret_cast<jbyte*>(buffer));
+ if (env->ExceptionCheck()) {
+ ALOGE("Internal error in ByteBufferStream::read");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ mPosition = mLength;
+ return bytesRead;
+ }
+
+ mPosition += requested;
+ buffer = reinterpret_cast<void*>(reinterpret_cast<char*>(buffer) + requested);
+ bytesRead += requested;
+ size -= requested;
+ } while (size);
+ return bytesRead;
+ }
+
+ bool isAtEnd() const override { return mLength == mPosition; }
+
+ // SkStreamRewindable overrides
+ bool rewind() override { return this->setPosition(0); }
+
+ SkStreamAsset* onDuplicate() const override {
+ // SkStreamRewindable requires overriding this, but it is not called by
+ // decoders, so does not need a true implementation. A proper
+ // implementation would require duplicating the ByteBuffer, which has
+ // its own internal position state.
+ return nullptr;
+ }
+
+ // SkStreamSeekable overrides
+ size_t getPosition() const override { return mPosition; }
+
+ bool seek(size_t position) override {
+ return this->setPosition(position > mLength ? mLength : position);
+ }
+
+ bool move(long offset) override {
+ long newPosition = mPosition + offset;
+ if (newPosition < 0) {
+ return this->setPosition(0);
+ }
+ return this->seek(static_cast<size_t>(newPosition));
+ }
+
+ SkStreamAsset* onFork() const override {
+ // SkStreamSeekable requires overriding this, but it is not called by
+ // decoders, so does not need a true implementation. A proper
+ // implementation would require duplicating the ByteBuffer, which has
+ // its own internal position state.
+ return nullptr;
+ }
+
+ // SkStreamAsset overrides
+ size_t getLength() const override { return mLength; }
+
+private:
+ JavaVM* mJvm;
+ jobject mByteBuffer;
+ // Logical position of the SkStream, between 0 and mLength.
+ size_t mPosition;
+ // Initial position of mByteBuffer, treated as mPosition 0.
+ const size_t mInitialPosition;
+ // Logical length of the SkStream, from mInitialPosition to
+ // mByteBuffer.limit().
+ const size_t mLength;
+
+ // Range has already been checked by the caller.
+ bool setPosition(size_t newPosition) {
+ auto* env = get_env_or_die(mJvm);
+ env->CallObjectMethod(mByteBuffer, gByteBuffer_setPositionMethodID,
+ newPosition + mInitialPosition);
+ if (env->ExceptionCheck()) {
+ ALOGE("Internal error in ByteBufferStream::setPosition");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ mPosition = mLength;
+ return false;
+ }
+ mPosition = newPosition;
+ return true;
+ }
+
+ // FIXME: This is an arbitrary storage size, which should be plenty for
+ // some formats (png, gif, many bmps). But for jpeg, the more we can supply
+ // in one call the better, and webp really wants all of the data. How to
+ // best choose the amount of storage used?
+ static constexpr size_t kStorageSize = 4096;
+ jbyteArray mStorage;
+};
+
+class ByteArrayStream : public SkStreamAsset {
+private:
+ ByteArrayStream(JavaVM* jvm, jbyteArray jarray, size_t offset, size_t length)
+ : mJvm(jvm), mByteArray(jarray), mOffset(offset), mPosition(0), mLength(length) {}
+
+public:
+ static ByteArrayStream* Create(JavaVM* jvm, JNIEnv* env, jbyteArray jarray, size_t offset,
+ size_t length) {
+ // This object outlives its native method call.
+ jarray = static_cast<jbyteArray>(env->NewGlobalRef(jarray));
+ if (!jarray) {
+ return nullptr;
+ }
+ return new ByteArrayStream(jvm, jarray, offset, length);
+ }
+
+ ~ByteArrayStream() override {
+ auto* env = get_env_or_die(mJvm);
+ env->DeleteGlobalRef(mByteArray);
+ }
+
+ size_t read(void* buffer, size_t size) override {
+ if (size > mLength - mPosition) {
+ size = mLength - mPosition;
+ }
+ if (!size) {
+ return 0;
+ }
+
+ auto* env = get_env_or_die(mJvm);
+ if (buffer) {
+ env->GetByteArrayRegion(mByteArray, mPosition + mOffset, size,
+ reinterpret_cast<jbyte*>(buffer));
+ if (env->ExceptionCheck()) {
+ ALOGE("Internal error in ByteArrayStream::read");
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ mPosition = mLength;
+ return 0;
+ }
+ }
+
+ mPosition += size;
+ return size;
+ }
+
+ bool isAtEnd() const override { return mLength == mPosition; }
+
+ // SkStreamRewindable overrides
+ bool rewind() override {
+ mPosition = 0;
+ return true;
+ }
+ SkStreamAsset* onDuplicate() const override {
+ // SkStreamRewindable requires overriding this, but it is not called by
+ // decoders, so does not need a true implementation. Note that a proper
+ // implementation is fairly straightforward
+ return nullptr;
+ }
+
+ // SkStreamSeekable overrides
+ size_t getPosition() const override { return mPosition; }
+
+ bool seek(size_t position) override {
+ mPosition = (position > mLength) ? mLength : position;
+ return true;
+ }
+
+ bool move(long offset) override {
+ long newPosition = mPosition + offset;
+ if (newPosition < 0) {
+ return this->seek(0);
+ }
+ return this->seek(static_cast<size_t>(newPosition));
+ }
+
+ SkStreamAsset* onFork() const override {
+ // SkStreamSeekable requires overriding this, but it is not called by
+ // decoders, so does not need a true implementation. Note that a proper
+ // implementation is fairly straightforward
+ return nullptr;
+ }
+
+ // SkStreamAsset overrides
+ size_t getLength() const override { return mLength; }
+
+private:
+ JavaVM* mJvm;
+ jbyteArray mByteArray;
+ // Offset in mByteArray. Only used when communicating with Java.
+ const size_t mOffset;
+ // Logical position of the SkStream, between 0 and mLength.
+ size_t mPosition;
+ const size_t mLength;
+};
+
+struct release_proc_context {
+ JavaVM* jvm;
+ jobject jbyteBuffer;
+};
+
+std::unique_ptr<SkStream> CreateByteBufferStreamAdaptor(JNIEnv* env, jobject jbyteBuffer,
+ size_t position, size_t limit) {
+ JavaVM* jvm;
+ LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK);
+
+ const size_t length = limit - position;
+ void* addr = env->GetDirectBufferAddress(jbyteBuffer);
+ if (addr) {
+ addr = reinterpret_cast<void*>(reinterpret_cast<char*>(addr) + position);
+ jbyteBuffer = env->NewGlobalRef(jbyteBuffer);
+ if (!jbyteBuffer) {
+ return nullptr;
+ }
+
+ auto* context = new release_proc_context{jvm, jbyteBuffer};
+ auto releaseProc = [](const void*, void* context) {
+ auto* c = reinterpret_cast<release_proc_context*>(context);
+ JNIEnv* env = get_env_or_die(c->jvm);
+ env->DeleteGlobalRef(c->jbyteBuffer);
+ delete c;
+ };
+ auto data = SkData::MakeWithProc(addr, length, releaseProc, context);
+ // The new SkMemoryStream will read directly from addr.
+ return std::unique_ptr<SkStream>(new SkMemoryStream(std::move(data)));
+ }
+
+ // Non-direct, or direct access is not supported.
+ return std::unique_ptr<SkStream>(ByteBufferStream::Create(jvm, env, jbyteBuffer, position,
+ length));
+}
+
+std::unique_ptr<SkStream> CreateByteArrayStreamAdaptor(JNIEnv* env, jbyteArray array, size_t offset,
+ size_t length) {
+ JavaVM* jvm;
+ LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK);
+
+ return std::unique_ptr<SkStream>(ByteArrayStream::Create(jvm, env, array, offset, length));
+}
+
+int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env) {
+ jclass byteBuffer_class = FindClassOrDie(env, "java/nio/ByteBuffer");
+ gByteBuffer_getMethodID = GetMethodIDOrDie(env, byteBuffer_class, "get", "([BII)Ljava/nio/ByteBuffer;");
+ gByteBuffer_setPositionMethodID = GetMethodIDOrDie(env, byteBuffer_class, "position", "(I)Ljava/nio/Buffer;");
+ return true;
+}
diff --git a/core/jni/android/graphics/ByteBufferStreamAdaptor.h b/core/jni/android/graphics/ByteBufferStreamAdaptor.h
new file mode 100644
index 0000000..367a48f
--- /dev/null
+++ b/core/jni/android/graphics/ByteBufferStreamAdaptor.h
@@ -0,0 +1,37 @@
+#ifndef _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_
+#define _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_
+
+#include <jni.h>
+#include <memory>
+
+class SkStream;
+
+/**
+ * Create an adaptor for treating a java.nio.ByteBuffer as an SkStream.
+ *
+ * This will special case direct ByteBuffers, but not the case where a byte[]
+ * can be used directly. For that, use CreateByteArrayStreamAdaptor.
+ *
+ * @param jbyteBuffer corresponding to the java ByteBuffer. This method will
+ * add a global ref.
+ * @param initialPosition returned by ByteBuffer.position(). Decoding starts
+ * from here.
+ * @param limit returned by ByteBuffer.limit().
+ *
+ * Returns null on failure.
+ */
+std::unique_ptr<SkStream> CreateByteBufferStreamAdaptor(JNIEnv*, jobject jbyteBuffer,
+ size_t initialPosition, size_t limit);
+
+/**
+ * Create an adaptor for treating a Java byte[] as an SkStream.
+ *
+ * @param offset into the byte[] of the beginning of the data to use.
+ * @param length of data to use, starting from offset.
+ *
+ * Returns null on failure.
+ */
+std::unique_ptr<SkStream> CreateByteArrayStreamAdaptor(JNIEnv*, jbyteArray array, size_t offset,
+ size_t length);
+
+#endif // _ANDROID_GRAPHICS_BYTE_BUFFER_STREAM_ADAPTOR_H_
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
new file mode 100644
index 0000000..bacab2a
--- /dev/null
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -0,0 +1,473 @@
+/*
+ * 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 "Bitmap.h"
+#include "ByteBufferStreamAdaptor.h"
+#include "GraphicsJNI.h"
+#include "NinePatchPeeker.h"
+#include "Utils.h"
+#include "core_jni_helpers.h"
+
+#include <hwui/Bitmap.h>
+#include <hwui/Canvas.h>
+
+#include <SkAndroidCodec.h>
+#include <SkEncodedImageFormat.h>
+#include <SkStream.h>
+
+#include <androidfw/Asset.h>
+#include <jni.h>
+
+using namespace android;
+
+static jclass gImageDecoder_class;
+static jclass gPoint_class;
+static jclass gIncomplete_class;
+static jclass gCorrupt_class;
+static jclass gCanvas_class;
+static jmethodID gImageDecoder_constructorMethodID;
+static jmethodID gPoint_constructorMethodID;
+static jmethodID gIncomplete_constructorMethodID;
+static jmethodID gCorrupt_constructorMethodID;
+static jmethodID gCallback_onExceptionMethodID;
+static jmethodID gPostProcess_postProcessMethodID;
+static jmethodID gCanvas_constructorMethodID;
+static jmethodID gCanvas_releaseMethodID;
+
+struct ImageDecoder {
+ // These need to stay in sync with ImageDecoder.java's Allocator constants.
+ enum Allocator {
+ kDefault_Allocator = 0,
+ kSoftware_Allocator = 1,
+ kSharedMemory_Allocator = 2,
+ kHardware_Allocator = 3,
+ };
+
+ // These need to stay in sync with PixelFormat.java's Format constants.
+ enum PixelFormat {
+ kUnknown = 0,
+ kTranslucent = -3,
+ kOpaque = -1,
+ };
+
+ std::unique_ptr<SkAndroidCodec> mCodec;
+ NinePatchPeeker mPeeker;
+};
+
+static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) {
+ if (!stream.get()) {
+ return nullObjectReturn("Failed to create a stream");
+ }
+ std::unique_ptr<ImageDecoder> decoder(new ImageDecoder);
+ decoder->mCodec = SkAndroidCodec::MakeFromStream(std::move(stream), &decoder->mPeeker);
+ if (!decoder->mCodec.get()) {
+ // FIXME: Add an error code to SkAndroidCodec::MakeFromStream, like
+ // SkCodec? Then this can print a more informative error message.
+ // (Or we can print one from within SkCodec.)
+ ALOGE("Failed to create an SkCodec");
+ return nullptr;
+ }
+
+ const auto& info = decoder->mCodec->getInfo();
+ const int width = info.width();
+ const int height = info.height();
+ return env->NewObject(gImageDecoder_class, gImageDecoder_constructorMethodID,
+ reinterpret_cast<jlong>(decoder.release()), width, height);
+}
+
+static jobject ImageDecoder_nCreate(JNIEnv* env, jobject /*clazz*/, jlong assetPtr) {
+ Asset* asset = reinterpret_cast<Asset*>(assetPtr);
+ std::unique_ptr<SkStream> stream(new AssetStreamAdaptor(asset));
+ return native_create(env, std::move(stream));
+}
+
+static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/, jobject jbyteBuffer,
+ jint initialPosition, jint limit) {
+ std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer,
+ initialPosition, limit);
+ if (!stream) {
+ return nullptr;
+ }
+ return native_create(env, std::move(stream));
+}
+
+static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/, jbyteArray byteArray,
+ jint offset, jint length) {
+ std::unique_ptr<SkStream> stream(CreateByteArrayStreamAdaptor(env, byteArray, offset, length));
+ return native_create(env, std::move(stream));
+}
+
+static bool supports_any_down_scale(const SkAndroidCodec* codec) {
+ return codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP;
+}
+
+static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
+ jobject jcallback, jobject jpostProcess,
+ jint desiredWidth, jint desiredHeight, jobject jsubset,
+ jboolean requireMutable, jint allocator,
+ jboolean requireUnpremul, jboolean preferRamOverQuality,
+ jboolean asAlphaMask) {
+ auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
+ SkAndroidCodec* codec = decoder->mCodec.get();
+ SkImageInfo decodeInfo = codec->getInfo();
+ bool scale = false;
+ int sampleSize = 1;
+ if (desiredWidth != decodeInfo.width() || desiredHeight != decodeInfo.height()) {
+ bool match = false;
+ if (desiredWidth < decodeInfo.width() && desiredHeight < decodeInfo.height()) {
+ if (supports_any_down_scale(codec)) {
+ match = true;
+ decodeInfo = decodeInfo.makeWH(desiredWidth, desiredHeight);
+ } else {
+ int sampleX = decodeInfo.width() / desiredWidth;
+ int sampleY = decodeInfo.height() / desiredHeight;
+ sampleSize = std::min(sampleX, sampleY);
+ SkISize sampledSize = codec->getSampledDimensions(sampleSize);
+ decodeInfo = decodeInfo.makeWH(sampledSize.width(), sampledSize.height());
+ if (decodeInfo.width() == desiredWidth && decodeInfo.height() == desiredHeight) {
+ match = true;
+ }
+ }
+ }
+ if (!match) {
+ scale = true;
+ if (requireUnpremul && kOpaque_SkAlphaType != decodeInfo.alphaType()) {
+ doThrowISE(env, "Cannot scale unpremultiplied pixels!");
+ return nullptr;
+ }
+ }
+ }
+
+ switch (decodeInfo.alphaType()) {
+ case kUnpremul_SkAlphaType:
+ if (!requireUnpremul) {
+ decodeInfo = decodeInfo.makeAlphaType(kPremul_SkAlphaType);
+ }
+ break;
+ case kPremul_SkAlphaType:
+ if (requireUnpremul) {
+ decodeInfo = decodeInfo.makeAlphaType(kUnpremul_SkAlphaType);
+ }
+ break;
+ case kOpaque_SkAlphaType:
+ break;
+ case kUnknown_SkAlphaType:
+ return nullObjectReturn("Unknown alpha type");
+ }
+
+ SkColorType colorType = kN32_SkColorType;
+ if (asAlphaMask && decodeInfo.colorType() == kGray_8_SkColorType) {
+ // We have to trick Skia to decode this to a single channel.
+ colorType = kGray_8_SkColorType;
+ } else if (preferRamOverQuality) {
+ // FIXME: The post-process might add alpha, which would make a 565
+ // result incorrect. If we call the postProcess before now and record
+ // to a picture, we can know whether alpha was added, and if not, we
+ // can still use 565.
+ if (decodeInfo.alphaType() == kOpaque_SkAlphaType && !jpostProcess) {
+ // If the final result will be hardware, decoding to 565 and then
+ // uploading to the gpu as 8888 will not save memory. This still
+ // may save us from using F16, but do not go down to 565.
+ if (allocator != ImageDecoder::kHardware_Allocator &&
+ (allocator != ImageDecoder::kDefault_Allocator || requireMutable)) {
+ colorType = kRGB_565_SkColorType;
+ }
+ }
+ // Otherwise, stick with N32
+ } else {
+ // This is currently the only way to know that we should decode to F16.
+ colorType = codec->computeOutputColorType(colorType);
+ }
+ sk_sp<SkColorSpace> colorSpace = codec->computeOutputColorSpace(colorType);
+ decodeInfo = decodeInfo.makeColorType(colorType).makeColorSpace(colorSpace);
+
+ SkBitmap bm;
+ auto bitmapInfo = decodeInfo;
+ if (asAlphaMask && colorType == kGray_8_SkColorType) {
+ bitmapInfo = bitmapInfo.makeColorType(kAlpha_8_SkColorType);
+ }
+ if (!bm.setInfo(bitmapInfo)) {
+ return nullObjectReturn("Failed to setInfo properly");
+ }
+
+ sk_sp<Bitmap> nativeBitmap;
+ // If we are going to scale or subset, we will create a new bitmap later on,
+ // so use the heap for the temporary.
+ // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380.
+ if (allocator == ImageDecoder::kSharedMemory_Allocator && !scale && !jsubset) {
+ nativeBitmap = Bitmap::allocateAshmemBitmap(&bm);
+ } else {
+ nativeBitmap = Bitmap::allocateHeapBitmap(&bm);
+ }
+ if (!nativeBitmap) {
+ ALOGE("OOM allocating Bitmap with dimensions %i x %i",
+ decodeInfo.width(), decodeInfo.height());
+ doThrowOOME(env);
+ return nullptr;
+ }
+
+ jobject jexception = nullptr;
+ SkAndroidCodec::AndroidOptions options;
+ options.fSampleSize = sampleSize;
+ auto result = codec->getAndroidPixels(decodeInfo, bm.getPixels(), bm.rowBytes(), &options);
+ switch (result) {
+ case SkCodec::kSuccess:
+ break;
+ case SkCodec::kIncompleteInput:
+ if (jcallback) {
+ jexception = env->NewObject(gIncomplete_class, gIncomplete_constructorMethodID);
+ }
+ break;
+ case SkCodec::kErrorInInput:
+ if (jcallback) {
+ jexception = env->NewObject(gCorrupt_class, gCorrupt_constructorMethodID);
+ }
+ break;
+ default:
+ ALOGE("getPixels failed with error %i", result);
+ return nullptr;
+ }
+
+ if (jexception) {
+ if (!env->CallBooleanMethod(jcallback, gCallback_onExceptionMethodID, jexception) ||
+ env->ExceptionCheck()) {
+ return nullptr;
+ }
+ }
+
+ float scaleX = 1.0f;
+ float scaleY = 1.0f;
+ if (scale) {
+ scaleX = (float) desiredWidth / decodeInfo.width();
+ scaleY = (float) desiredHeight / decodeInfo.height();
+ }
+
+ jbyteArray ninePatchChunk = nullptr;
+ jobject ninePatchInsets = nullptr;
+
+ // Ignore ninepatch when post-processing.
+ if (!jpostProcess) {
+ // FIXME: Share more code with BitmapFactory.cpp.
+ if (decoder->mPeeker.mPatch != nullptr) {
+ if (scale) {
+ decoder->mPeeker.scale(scaleX, scaleY, desiredWidth, desiredHeight);
+ }
+ size_t ninePatchArraySize = decoder->mPeeker.mPatch->serializedSize();
+ ninePatchChunk = env->NewByteArray(ninePatchArraySize);
+ if (ninePatchChunk == nullptr) {
+ return nullObjectReturn("ninePatchChunk == null");
+ }
+
+ env->SetByteArrayRegion(ninePatchChunk, 0, decoder->mPeeker.mPatchSize,
+ reinterpret_cast<jbyte*>(decoder->mPeeker.mPatch));
+ }
+
+ if (decoder->mPeeker.mHasInsets) {
+ ninePatchInsets = decoder->mPeeker.createNinePatchInsets(env, 1.0f);
+ if (ninePatchInsets == nullptr) {
+ return nullObjectReturn("nine patch insets == null");
+ }
+ }
+ }
+
+ if (scale || jsubset) {
+ int translateX = 0;
+ int translateY = 0;
+ if (jsubset) {
+ SkIRect subset;
+ GraphicsJNI::jrect_to_irect(env, jsubset, &subset);
+
+ // FIXME: If there is no scale, should this instead call
+ // SkBitmap::extractSubset? If we could upload a subset
+ // (b/70626068), this would save memory and time. Even for a
+ // software Bitmap, the extra speed might be worth the memory
+ // tradeoff if the subset is large?
+ translateX = -subset.fLeft;
+ translateY = -subset.fTop;
+ desiredWidth = subset.width();
+ desiredHeight = subset.height();
+ }
+ SkImageInfo scaledInfo = bitmapInfo.makeWH(desiredWidth, desiredHeight);
+ SkBitmap scaledBm;
+ if (!scaledBm.setInfo(scaledInfo)) {
+ nullObjectReturn("Failed scaled setInfo");
+ }
+
+ sk_sp<Bitmap> scaledPixelRef;
+ if (allocator == ImageDecoder::kSharedMemory_Allocator) {
+ scaledPixelRef = Bitmap::allocateAshmemBitmap(&scaledBm);
+ } else {
+ scaledPixelRef = Bitmap::allocateHeapBitmap(&scaledBm);
+ }
+ if (!scaledPixelRef) {
+ ALOGE("OOM allocating scaled Bitmap with dimensions %i x %i",
+ desiredWidth, desiredHeight);
+ doThrowOOME(env);
+ return nullptr;
+ }
+
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrc);
+ paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering
+
+ SkCanvas canvas(scaledBm, SkCanvas::ColorBehavior::kLegacy);
+ canvas.translate(translateX, translateY);
+ canvas.scale(scaleX, scaleY);
+ canvas.drawBitmap(bm, 0.0f, 0.0f, &paint);
+
+ bm.swap(scaledBm);
+ nativeBitmap = scaledPixelRef;
+ }
+
+ if (jpostProcess) {
+ std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm));
+ if (!canvas) {
+ return nullObjectReturn("Failed to create Canvas for PostProcess!");
+ }
+ jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID,
+ reinterpret_cast<jlong>(canvas.get()));
+ if (!jcanvas) {
+ return nullObjectReturn("Failed to create Java Canvas for PostProcess!");
+ }
+ // jcanvas will now own canvas.
+ canvas.release();
+
+ jint pixelFormat = env->CallIntMethod(jpostProcess, gPostProcess_postProcessMethodID,
+ jcanvas, bm.width(), bm.height());
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+
+ // The Canvas objects are no longer needed, and will not remain valid.
+ env->CallVoidMethod(jcanvas, gCanvas_releaseMethodID);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+
+ SkAlphaType newAlphaType = bm.alphaType();
+ switch (pixelFormat) {
+ case ImageDecoder::kUnknown:
+ break;
+ case ImageDecoder::kTranslucent:
+ newAlphaType = kPremul_SkAlphaType;
+ break;
+ case ImageDecoder::kOpaque:
+ newAlphaType = kOpaque_SkAlphaType;
+ break;
+ default:
+ ALOGE("invalid return from postProcess: %i", pixelFormat);
+ doThrowIAE(env);
+ return nullptr;
+ }
+
+ if (newAlphaType != bm.alphaType()) {
+ if (!bm.setAlphaType(newAlphaType)) {
+ ALOGE("incompatible return from postProcess: %i", pixelFormat);
+ doThrowIAE(env);
+ return nullptr;
+ }
+ nativeBitmap->setAlphaType(newAlphaType);
+ }
+ }
+
+ int bitmapCreateFlags = 0x0;
+ if (!requireUnpremul) {
+ // Even if the image is opaque, setting this flag means that
+ // if alpha is added (e.g. by PostProcess), it will be marked as
+ // premultiplied.
+ bitmapCreateFlags |= bitmap::kBitmapCreateFlag_Premultiplied;
+ }
+
+ if (requireMutable) {
+ bitmapCreateFlags |= bitmap::kBitmapCreateFlag_Mutable;
+ } else {
+ if ((allocator == ImageDecoder::kDefault_Allocator ||
+ allocator == ImageDecoder::kHardware_Allocator)
+ && bm.colorType() != kAlpha_8_SkColorType)
+ {
+ sk_sp<Bitmap> hwBitmap = Bitmap::allocateHardwareBitmap(bm);
+ if (hwBitmap) {
+ hwBitmap->setImmutable();
+ return bitmap::createBitmap(env, hwBitmap.release(), bitmapCreateFlags,
+ ninePatchChunk, ninePatchInsets);
+ }
+ if (allocator == ImageDecoder::kHardware_Allocator) {
+ return nullObjectReturn("failed to allocate hardware Bitmap!");
+ }
+ // If we failed to create a hardware bitmap, go ahead and create a
+ // software one.
+ }
+
+ nativeBitmap->setImmutable();
+ }
+ return bitmap::createBitmap(env, nativeBitmap.release(), bitmapCreateFlags, ninePatchChunk,
+ ninePatchInsets);
+}
+
+static jobject ImageDecoder_nGetSampledSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
+ jint sampleSize) {
+ auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
+ SkISize size = decoder->mCodec->getSampledDimensions(sampleSize);
+ return env->NewObject(gPoint_class, gPoint_constructorMethodID, size.width(), size.height());
+}
+
+static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
+ jobject outPadding) {
+ auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
+ decoder->mPeeker.getPadding(env, outPadding);
+}
+
+static void ImageDecoder_nRecycle(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) {
+ delete reinterpret_cast<ImageDecoder*>(nativePtr);
+}
+
+static const JNINativeMethod gImageDecoderMethods[] = {
+ { "nCreate", "(J)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreate },
+ { "nCreate", "(Ljava/nio/ByteBuffer;II)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
+ { "nCreate", "([BII)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
+ { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder$OnExceptionListener;Landroid/graphics/PostProcess;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
+ (void*) ImageDecoder_nDecodeBitmap },
+ { "nGetSampledSize","(JI)Landroid/graphics/Point;", (void*) ImageDecoder_nGetSampledSize },
+ { "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding },
+ { "nRecycle", "(J)V", (void*) ImageDecoder_nRecycle},
+};
+
+int register_android_graphics_ImageDecoder(JNIEnv* env) {
+ gImageDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder"));
+ gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JII)V");
+
+ gPoint_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Point"));
+ gPoint_constructorMethodID = GetMethodIDOrDie(env, gPoint_class, "<init>", "(II)V");
+
+ gIncomplete_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$IncompleteException"));
+ gIncomplete_constructorMethodID = GetMethodIDOrDie(env, gIncomplete_class, "<init>", "()V");
+
+ gCorrupt_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$CorruptException"));
+ gCorrupt_constructorMethodID = GetMethodIDOrDie(env, gCorrupt_class, "<init>", "()V");
+
+ jclass callback_class = FindClassOrDie(env, "android/graphics/ImageDecoder$OnExceptionListener");
+ gCallback_onExceptionMethodID = GetMethodIDOrDie(env, callback_class, "onException", "(Ljava/lang/Exception;)Z");
+
+ jclass postProcess_class = FindClassOrDie(env, "android/graphics/PostProcess");
+ gPostProcess_postProcessMethodID = GetMethodIDOrDie(env, postProcess_class, "postProcess", "(Landroid/graphics/Canvas;II)I");
+
+ gCanvas_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas"));
+ gCanvas_constructorMethodID = GetMethodIDOrDie(env, gCanvas_class, "<init>", "(J)V");
+ gCanvas_releaseMethodID = GetMethodIDOrDie(env, gCanvas_class, "release", "()V");
+
+ return android::RegisterMethodsOrDie(env, "android/graphics/ImageDecoder", gImageDecoderMethods,
+ NELEM(gImageDecoderMethods));
+}
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index 564afeb..2619107 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -29,11 +29,15 @@
#include "SkLatticeIter.h"
#include "SkRegion.h"
#include "GraphicsJNI.h"
+#include "NinePatchPeeker.h"
#include "NinePatchUtils.h"
#include <nativehelper/JNIHelp.h>
#include "core_jni_helpers.h"
+jclass gInsetStruct_class;
+jmethodID gInsetStruct_constructorMethodID;
+
using namespace android;
/**
@@ -128,6 +132,30 @@
};
+jobject NinePatchPeeker::createNinePatchInsets(JNIEnv* env, float scale) const {
+ if (!mHasInsets) {
+ return nullptr;
+ }
+
+ return env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID,
+ mOpticalInsets[0], mOpticalInsets[1],
+ mOpticalInsets[2], mOpticalInsets[3],
+ mOutlineInsets[0], mOutlineInsets[1],
+ mOutlineInsets[2], mOutlineInsets[3],
+ mOutlineRadius, mOutlineAlpha, scale);
+}
+
+void NinePatchPeeker::getPadding(JNIEnv* env, jobject outPadding) const {
+ if (mPatch) {
+ GraphicsJNI::set_jrect(env, outPadding,
+ mPatch->paddingLeft, mPatch->paddingTop,
+ mPatch->paddingRight, mPatch->paddingBottom);
+
+ } else {
+ GraphicsJNI::set_jrect(env, outPadding, -1, -1, -1, -1);
+ }
+}
+
/////////////////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gNinePatchMethods[] = {
@@ -140,6 +168,10 @@
};
int register_android_graphics_NinePatch(JNIEnv* env) {
+ gInsetStruct_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
+ "android/graphics/NinePatch$InsetStruct"));
+ gInsetStruct_constructorMethodID = GetMethodIDOrDie(env, gInsetStruct_class, "<init>",
+ "(IIIIIIIIFIF)V");
return android::RegisterMethodsOrDie(env,
"android/graphics/NinePatch", gNinePatchMethods, NELEM(gNinePatchMethods));
}
diff --git a/core/jni/android/graphics/NinePatchPeeker.cpp b/core/jni/android/graphics/NinePatchPeeker.cpp
index 1ea5650..9171fc6 100644
--- a/core/jni/android/graphics/NinePatchPeeker.cpp
+++ b/core/jni/android/graphics/NinePatchPeeker.cpp
@@ -16,7 +16,8 @@
#include "NinePatchPeeker.h"
-#include "SkBitmap.h"
+#include <SkBitmap.h>
+#include <cutils/compiler.h>
using namespace android;
@@ -46,3 +47,47 @@
}
return true; // keep on decoding
}
+
+static void scaleDivRange(int32_t* divs, int count, float scale, int maxValue) {
+ for (int i = 0; i < count; i++) {
+ divs[i] = int32_t(divs[i] * scale + 0.5f);
+ if (i > 0 && divs[i] == divs[i - 1]) {
+ divs[i]++; // avoid collisions
+ }
+ }
+
+ if (CC_UNLIKELY(divs[count - 1] > maxValue)) {
+ // if the collision avoidance above put some divs outside the bounds of the bitmap,
+ // slide outer stretchable divs inward to stay within bounds
+ int highestAvailable = maxValue;
+ for (int i = count - 1; i >= 0; i--) {
+ divs[i] = highestAvailable;
+ if (i > 0 && divs[i] <= divs[i-1]) {
+ // keep shifting
+ highestAvailable = divs[i] - 1;
+ } else {
+ break;
+ }
+ }
+ }
+}
+
+void NinePatchPeeker::scale(float scaleX, float scaleY, int scaledWidth, int scaledHeight) {
+ if (!mPatch) {
+ return;
+ }
+
+ // The max value for the divRange is one pixel less than the actual max to ensure that the size
+ // of the last div is not zero. A div of size 0 is considered invalid input and will not render.
+ if (!SkScalarNearlyEqual(scaleX, 1.0f)) {
+ mPatch->paddingLeft = int(mPatch->paddingLeft * scaleX + 0.5f);
+ mPatch->paddingRight = int(mPatch->paddingRight * scaleX + 0.5f);
+ scaleDivRange(mPatch->getXDivs(), mPatch->numXDivs, scaleX, scaledWidth - 1);
+ }
+
+ if (!SkScalarNearlyEqual(scaleY, 1.0f)) {
+ mPatch->paddingTop = int(mPatch->paddingTop * scaleY + 0.5f);
+ mPatch->paddingBottom = int(mPatch->paddingBottom * scaleY + 0.5f);
+ scaleDivRange(mPatch->getYDivs(), mPatch->numYDivs, scaleY, scaledHeight - 1);
+ }
+}
diff --git a/core/jni/android/graphics/NinePatchPeeker.h b/core/jni/android/graphics/NinePatchPeeker.h
index 126eab2..e4e58dd 100644
--- a/core/jni/android/graphics/NinePatchPeeker.h
+++ b/core/jni/android/graphics/NinePatchPeeker.h
@@ -20,7 +20,7 @@
#include "SkPngChunkReader.h"
#include <androidfw/ResourceTypes.h>
-class SkImageDecoder;
+#include <jni.h>
using namespace android;
@@ -42,9 +42,14 @@
bool readChunk(const char tag[], const void* data, size_t length) override;
+ jobject createNinePatchInsets(JNIEnv*, float scale) const;
+ void getPadding(JNIEnv*, jobject outPadding) const;
+ void scale(float scaleX, float scaleY, int scaledWidth, int scaledHeight);
+
Res_png_9patch* mPatch;
size_t mPatchSize;
bool mHasInsets;
+private:
int32_t mOpticalInsets[4];
int32_t mOutlineInsets[4];
float mOutlineRadius;
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index 1522c20..1676d4b 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -25,7 +25,8 @@
#include <assert.h>
#include <dlfcn.h>
-#include <GLES/gl.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
#include <ETC1/etc1.h>
#include <SkBitmap.h>
@@ -639,6 +640,10 @@
return 0;
}
break;
+ case kRGBA_F16_SkColorType:
+ if (type == GL_HALF_FLOAT_OES && format == PIXEL_FORMAT_RGBA_FP16)
+ return 0;
+ break;
default:
break;
}
@@ -656,6 +661,8 @@
return GL_RGBA;
case kRGB_565_SkColorType:
return GL_RGB;
+ case kRGBA_F16_SkColorType:
+ return PIXEL_FORMAT_RGBA_FP16;
default:
return -1;
}
@@ -672,6 +679,8 @@
return GL_UNSIGNED_BYTE;
case kRGB_565_SkColorType:
return GL_UNSIGNED_SHORT_5_6_5;
+ case kRGBA_F16_SkColorType:
+ return GL_HALF_FLOAT_OES;
default:
return -1;
}
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 17b98da..cc2646c 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -236,17 +236,15 @@
return INSTALL_SUCCEEDED;
}
- char localTmpFileName[nativeLibPath.size() + TMP_FILE_PATTERN_LEN + 2];
+ char localTmpFileName[nativeLibPath.size() + TMP_FILE_PATTERN_LEN + 1];
if (strlcpy(localTmpFileName, nativeLibPath.c_str(), sizeof(localTmpFileName))
!= nativeLibPath.size()) {
ALOGD("Couldn't allocate local file name for library");
return INSTALL_FAILED_INTERNAL_ERROR;
}
- *(localTmpFileName + nativeLibPath.size()) = '/';
-
if (strlcpy(localTmpFileName + nativeLibPath.size(), TMP_FILE_PATTERN,
- TMP_FILE_PATTERN_LEN - nativeLibPath.size()) != TMP_FILE_PATTERN_LEN) {
+ TMP_FILE_PATTERN_LEN + 1) != TMP_FILE_PATTERN_LEN) {
ALOGI("Couldn't allocate temporary file name for library");
return INSTALL_FAILED_INTERNAL_ERROR;
}
diff --git a/core/proto/android/app/activitymanager.proto b/core/proto/android/app/activitymanager.proto
index e87499e..7385050 100644
--- a/core/proto/android/app/activitymanager.proto
+++ b/core/proto/android/app/activitymanager.proto
@@ -20,61 +20,59 @@
option java_multiple_files = true;
-message ActivityManagerProto {
+// ActivityManager.java PROCESS_STATEs
+enum ProcessState {
+ // Order matters for process states, so values have been spaced to provide
+ // room for future additions.
- // ActivityManager.java PROCESS_STATEs
- enum ProcessState {
- // Order matters for process states, so values have been spaced to provide
- // room for future additions.
-
- // Not a real process state.
- PROCESS_STATE_UNKNOWN = -100;
- // Process is a persistent system process.
- PROCESS_STATE_PERSISTENT = 0;
- // Process is a persistent system process and is doing UI.
- PROCESS_STATE_PERSISTENT_UI = 100;
- // Process is hosting the current top activities. Note that this covers
- // all activities that are visible to the user.
- PROCESS_STATE_TOP = 200;
- // Process is hosting a foreground service due to a system binding.
- PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 300;
- // Process is hosting a foreground service.
- PROCESS_STATE_FOREGROUND_SERVICE = 400;
- // Same as PROCESS_STATE_TOP but while device is sleeping.
- PROCESS_STATE_TOP_SLEEPING = 500;
- // Process is important to the user, and something they are aware of.
- PROCESS_STATE_IMPORTANT_FOREGROUND = 600;
- // Process is important to the user, but not something they are aware of.
- PROCESS_STATE_IMPORTANT_BACKGROUND = 700;
- // Process is in the background transient so we will try to keep running.
- PROCESS_STATE_TRANSIENT_BACKGROUND = 800;
- // Process is in the background running a backup/restore operation.
- PROCESS_STATE_BACKUP = 900;
- // Process is in the background, but it can't restore its state so we want
- // to try to avoid killing it.
- PROCESS_STATE_HEAVY_WEIGHT = 1000;
- // Process is in the background running a service. Unlike oom_adj, this
- // level is used for both the normal running in background state and the
- // executing operations state.
- PROCESS_STATE_SERVICE = 1100;
- // Process is in the background running a receiver. Note that from the
- // perspective of oom_adj, receivers run at a higher foreground level, but
- // for our prioritization here that is not necessary and putting them
- // below services means many fewer changes in some process states as they
- // receive broadcasts.
- PROCESS_STATE_RECEIVER = 1200;
- // Process is in the background but hosts the home activity.
- PROCESS_STATE_HOME = 1300;
- // Process is in the background but hosts the last shown activity.
- PROCESS_STATE_LAST_ACTIVITY = 1400;
- // Process is being cached for later use and contains activities.
- PROCESS_STATE_CACHED_ACTIVITY = 1500;
- // Process is being cached for later use and is a client of another cached
- // process that contains activities.
- PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 1600;
- // Process is being cached for later use and is empty.
- PROCESS_STATE_CACHED_EMPTY = 1700;
- // Process does not exist.
- PROCESS_STATE_NONEXISTENT = 1800;
- }
+ // Not a real process state.
+ PROCESS_STATE_UNKNOWN = -100;
+ // Process is a persistent system process.
+ PROCESS_STATE_PERSISTENT = 0;
+ // Process is a persistent system process and is doing UI.
+ PROCESS_STATE_PERSISTENT_UI = 100;
+ // Process is hosting the current top activities. Note that this covers
+ // all activities that are visible to the user.
+ PROCESS_STATE_TOP = 200;
+ // Process is hosting a foreground service due to a system binding.
+ PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 300;
+ // Process is hosting a foreground service.
+ PROCESS_STATE_FOREGROUND_SERVICE = 400;
+ // Same as PROCESS_STATE_TOP but while device is sleeping.
+ PROCESS_STATE_TOP_SLEEPING = 500;
+ // Process is important to the user, and something they are aware of.
+ PROCESS_STATE_IMPORTANT_FOREGROUND = 600;
+ // Process is important to the user, but not something they are aware of.
+ PROCESS_STATE_IMPORTANT_BACKGROUND = 700;
+ // Process is in the background transient so we will try to keep running.
+ PROCESS_STATE_TRANSIENT_BACKGROUND = 800;
+ // Process is in the background running a backup/restore operation.
+ PROCESS_STATE_BACKUP = 900;
+ // Process is in the background, but it can't restore its state so we want
+ // to try to avoid killing it.
+ PROCESS_STATE_HEAVY_WEIGHT = 1000;
+ // Process is in the background running a service. Unlike oom_adj, this
+ // level is used for both the normal running in background state and the
+ // executing operations state.
+ PROCESS_STATE_SERVICE = 1100;
+ // Process is in the background running a receiver. Note that from the
+ // perspective of oom_adj, receivers run at a higher foreground level, but
+ // for our prioritization here that is not necessary and putting them
+ // below services means many fewer changes in some process states as they
+ // receive broadcasts.
+ PROCESS_STATE_RECEIVER = 1200;
+ // Process is in the background but hosts the home activity.
+ PROCESS_STATE_HOME = 1300;
+ // Process is in the background but hosts the last shown activity.
+ PROCESS_STATE_LAST_ACTIVITY = 1400;
+ // Process is being cached for later use and contains activities.
+ PROCESS_STATE_CACHED_ACTIVITY = 1500;
+ // Process is being cached for later use and is a client of another cached
+ // process that contains activities.
+ PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 1600;
+ // Process is being cached for later use and is empty.
+ PROCESS_STATE_CACHED_EMPTY = 1700;
+ // Process does not exist.
+ PROCESS_STATE_NONEXISTENT = 1800;
}
+
diff --git a/core/proto/android/graphics/point.proto b/core/proto/android/graphics/point.proto
new file mode 100644
index 0000000..5ae17cb
--- /dev/null
+++ b/core/proto/android/graphics/point.proto
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.graphics;
+
+option java_multiple_files = true;
+
+message PointProto {
+ optional int32 x = 1;
+ optional int32 y = 2;
+}
+
diff --git a/core/proto/android/internal/processstats.proto b/core/proto/android/internal/processstats.proto
new file mode 100644
index 0000000..5629c2d
--- /dev/null
+++ b/core/proto/android/internal/processstats.proto
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package com.android.internal.app.procstats;
+
+message ProcessStatsProto {
+ enum MemoryFactor {
+ MEM_FACTOR_NORMAL = 0;
+ MEM_FACTOR_MODERATE = 1;
+ MEM_FACTOR_LOW = 2;
+ MEM_FACTOR_CRITICAL = 3;
+ }
+}
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index cff1879..0fa428e 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -28,12 +28,8 @@
optional int64 parcel_version = 2;
optional string start_platform_version = 3;
optional string end_platform_version = 4;
- optional BatteryHistoryProto history = 5;
- repeated UidProto uids = 6;
- optional SystemProto system = 7;
-}
-
-message BatteryHistoryProto {
+ repeated UidProto uids = 5;
+ optional SystemProto system = 6;
}
message ControllerActivityProto {
diff --git a/core/proto/android/os/cpuinfo.proto b/core/proto/android/os/cpuinfo.proto
index a95fa57..522ff24 100644
--- a/core/proto/android/os/cpuinfo.proto
+++ b/core/proto/android/os/cpuinfo.proto
@@ -18,8 +18,6 @@
option java_multiple_files = true;
option java_outer_classname = "CpuInfoProto";
-import "frameworks/base/tools/streaming_proto/stream.proto";
-
package android.os;
/**
@@ -31,8 +29,6 @@
message CpuInfo {
message TaskStats {
- option (stream_proto.stream_msg).enable_fields_mapping = true;
-
optional int32 total = 1; // total number of cpu tasks
optional int32 running = 2; // number of running tasks
optional int32 sleeping = 3; // number of sleeping tasks
@@ -42,8 +38,6 @@
optional TaskStats task_stats = 1;
message MemStats { // unit in kB
- option (stream_proto.stream_msg).enable_fields_mapping = true;
-
optional int32 total = 1;
optional int32 used = 2;
optional int32 free = 3;
@@ -54,8 +48,6 @@
optional MemStats swap = 3;
message CpuUsage { // unit is percentage %
- option (stream_proto.stream_msg).enable_fields_mapping = true;
-
optional int32 cpu = 1; // 400% cpu indicates 4 cores
optional int32 user = 2;
optional int32 nice = 3;
@@ -70,8 +62,6 @@
// Next Tag: 13
message Task {
- option (stream_proto.stream_msg).enable_fields_mapping = true;
-
optional int32 pid = 1;
optional int32 tid = 2;
optional string user = 3;
@@ -80,8 +70,6 @@
optional float cpu = 6; // precentage of cpu usage of the task
enum Status {
- option (stream_proto.stream_enum).enable_enums_mapping = true;
-
STATUS_UNKNOWN = 0;
STATUS_D = 1; // uninterruptible sleep
STATUS_R = 2; // running
@@ -95,8 +83,6 @@
// How Android memory manager will treat the task
enum Policy {
- option (stream_proto.stream_enum).enable_enums_mapping = true;
-
POLICY_UNKNOWN = 0;
POLICY_fg = 1; // foreground, the name is lower case for parsing the value
POLICY_bg = 2; // background, the name is lower case for parsing the value
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 4015a7e..ac8f26d 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -28,13 +28,14 @@
import "frameworks/base/core/proto/android/providers/settings.proto";
import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
import "frameworks/base/core/proto/android/server/alarmmanagerservice.proto";
-import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
import "frameworks/base/core/proto/android/server/fingerprint.proto";
import "frameworks/base/core/proto/android/server/powermanagerservice.proto";
+import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
import "frameworks/base/core/proto/android/service/appwidget.proto";
import "frameworks/base/core/proto/android/service/battery.proto";
import "frameworks/base/core/proto/android/service/batterystats.proto";
import "frameworks/base/core/proto/android/service/diskstats.proto";
+import "frameworks/base/core/proto/android/service/graphicsstats.proto";
import "frameworks/base/core/proto/android/service/netstats.proto";
import "frameworks/base/core/proto/android/service/notification.proto";
import "frameworks/base/core/proto/android/service/package.proto";
@@ -176,6 +177,11 @@
optional com.android.server.am.proto.MemInfoProto meminfo = 3018 [
(section).type = SECTION_DUMPSYS,
- (section).args = "meminfo --proto"
+ (section).args = "meminfo -a --proto"
+ ];
+
+ optional android.service.GraphicsStatsServiceDumpProto graphicsstats = 3019 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "graphicsstats --proto"
];
}
diff --git a/core/proto/android/os/kernelwake.proto b/core/proto/android/os/kernelwake.proto
index eaad37a..7e5be9d 100644
--- a/core/proto/android/os/kernelwake.proto
+++ b/core/proto/android/os/kernelwake.proto
@@ -18,8 +18,6 @@
option java_multiple_files = true;
option java_outer_classname = "WakeupSourcesProto";
-import "frameworks/base/tools/streaming_proto/stream.proto";
-
package android.os;
message KernelWakeSources {
@@ -29,8 +27,6 @@
// Next Tag: 11
message WakeupSourceProto {
- option (stream_proto.stream_msg).enable_fields_mapping = true;
-
// Name of the event which triggers application processor
optional string name = 1;
diff --git a/core/proto/android/os/pagetypeinfo.proto b/core/proto/android/os/pagetypeinfo.proto
index b86ee01..f82ea76 100644
--- a/core/proto/android/os/pagetypeinfo.proto
+++ b/core/proto/android/os/pagetypeinfo.proto
@@ -18,8 +18,6 @@
option java_multiple_files = true;
option java_outer_classname = "PageTypeInfoProto";
-import "frameworks/base/tools/streaming_proto/stream.proto";
-
package android.os;
/*
@@ -63,7 +61,6 @@
// Next tag: 9
message BlockProto {
- option (stream_proto.stream_msg).enable_fields_mapping = true;
optional int32 node = 1;
diff --git a/core/proto/android/os/procrank.proto b/core/proto/android/os/procrank.proto
index f684c84..204a5af 100644
--- a/core/proto/android/os/procrank.proto
+++ b/core/proto/android/os/procrank.proto
@@ -19,7 +19,6 @@
option java_outer_classname = "ProcrankProto";
import "frameworks/base/libs/incident/proto/android/privacy.proto";
-import "frameworks/base/tools/streaming_proto/stream.proto";
package android.os;
@@ -36,7 +35,6 @@
// Next Tag: 11
message ProcessProto {
- option (stream_proto.stream_msg).enable_fields_mapping = true;
option (android.msg_privacy).dest = DEST_AUTOMATIC;
// ID of the process
diff --git a/core/proto/android/os/system_properties.proto b/core/proto/android/os/system_properties.proto
index 921bb5a..a51253f 100644
--- a/core/proto/android/os/system_properties.proto
+++ b/core/proto/android/os/system_properties.proto
@@ -17,641 +17,454 @@
syntax = "proto2";
option java_multiple_files = true;
-option java_outer_classname = "SystemPropertiesProtoMetadata";
-import "frameworks/base/tools/streaming_proto/stream.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
package android.os;
-// System Properties from getprop
+// Android Platform Exported System Properties
+// TODO: This is not the completed list, new properties need to be whitelisted.
message SystemPropertiesProto {
- option (stream_proto.stream_msg).enable_fields_mapping = true;
- // Properties that are not specified below are appended here.
+ // Properties that are not specified below would be appended here.
+ // These values stay on device only.
message Property {
+ option (android.msg_privacy).dest = DEST_LOCAL;
+
optional string name = 1;
optional string value = 2;
}
repeated Property extra_properties = 1;
- optional int32 aaudio_hw_burst_min_usec = 2;
- optional int32 aaudio_mmap_exclusive_policy = 3;
- optional int32 aaudio_mmap_policy = 4;
-
- optional int32 af_fast_track_multiplier = 5;
-
- optional int32 audio_adm_buffering_ms = 6;
- optional int32 audio_hal_period_size = 7;
-
- optional string dalvik_vm_appimageformat = 8;
- optional string dalvik_vm_dex2oat_Xms = 9;
- optional string dalvik_vm_dex2oat_Xmx = 10;
- optional bool dalvik_vm_dexopt_secondary = 11;
- optional string dalvik_vm_heapgrowthlimit = 12;
- optional string dalvik_vm_heapmaxfree = 13;
- optional string dalvik_vm_heapminfree = 14;
- optional string dalvik_vm_heapsize = 15;
- optional string dalvik_vm_heapstartsize = 16;
- optional float dalvik_vm_heaptargetutilization = 17;
- optional string dalvik_vm_image_dex2oat_Xms = 18;
- optional string dalvik_vm_image_dex2oat_Xmx = 19;
- optional string dalvik_vm_image_dex2oat_filter = 20;
- optional string dalvik_vm_isa_arm_features = 21;
- optional string dalvik_vm_isa_arm_variant = 22;
- optional string dalvik_vm_isa_arm64_features = 23;
- optional string dalvik_vm_isa_arm64_variant = 24;
- optional int32 dalvik_vm_lockprof_threshold = 25;
- optional string dalvik_vm_stack_trace_dir = 26;
- optional bool dalvik_vm_usejit = 27;
- optional bool dalvik_vm_usejitprofiles = 28;
-
- optional int32 debug_atrace_tags_enableflags = 29;
- optional int32 debug_force_rtl = 30;
- optional string debug_htc_hrdump = 31;
-
- optional int32 dev_bootcomplete = 32;
-
- optional bool drm_service_enabled = 33;
-
- optional int32 fmas_hdph_sgain = 34;
-
- optional int32 gsm_current_phone_type = 35;
- optional string gsm_network_type = 36;
- optional string gsm_operator_alpha = 37;
- optional string gsm_operator_iso_country = 38;
- optional bool gsm_operator_isroaming = 39;
- optional string gsm_operator_numeric = 40;
- optional string gsm_sim_operator_alpha = 41;
- optional string gsm_sim_operator_iso_country = 42;
- optional int32 gsm_sim_operator_numeric = 43;
- optional string gsm_sim_state = 44;
- optional string gsm_version_baseband = 45;
- optional string gsm_version_ril_impl = 46;
-
- optional sint32 htc_camera_sensor_inf = 47;
-
- optional bool hwservicemanager_ready = 48;
-
- // Enum values for a lot of system properties
- enum Status {
- STATUS_UNKNOWN = 0;
- STATUS_RUNNING = 1;
- STATUS_STOPPED = 2;
+ message AacDrc {
+ optional int32 boost = 1;
+ optional int32 cut = 2;
+ optional int32 enc_target_level = 3;
+ optional int32 heavy = 4;
+ optional int32 reference_level = 5;
}
- optional Status init_svc_adbd = 49;
- optional Status init_svc_audioserver = 50;
- optional Status init_svc_bootanim = 51;
- optional Status init_svc_bufferhubd = 52;
- optional Status init_svc_cameraserver = 53;
- optional Status init_svc_clear_bcb = 54;
- optional Status init_svc_drm = 55;
- optional Status init_svc_gatekeeperd = 56;
- optional Status init_svc_healthd = 57;
- optional Status init_svc_hidl_memory = 58;
- optional Status init_svc_hostapd = 59;
- optional Status init_svc_hwservicemanager = 60;
- optional Status init_svc_installd = 61;
- optional Status init_svc_keystore = 62;
- optional Status init_svc_lmkd = 63;
- optional Status init_svc_logd = 64;
- optional Status init_svc_logd_reinit = 65;
- optional Status init_svc_media = 66;
- optional Status init_svc_mediadrm = 67;
- optional Status init_svc_mediaextractor = 68;
- optional Status init_svc_mediametrics = 69;
- optional Status init_svc_netd = 70;
- optional Status init_svc_performanced = 71;
- optional Status init_svc_ril_daemon = 72;
- optional Status init_svc_servicemanager = 73;
- optional Status init_svc_storaged = 74;
- optional Status init_svc_surfaceflinger = 75;
- optional Status init_svc_thermalservice = 76;
- optional Status init_svc_tombstoned = 77;
- optional Status init_svc_ueventd = 78;
- optional Status init_svc_update_engine = 79;
- optional Status init_svc_update_verifier_nonencrypted = 80;
- optional Status init_svc_vendor_adsprpcd = 81;
- optional Status init_svc_vendor_atfwd = 82;
- optional Status init_svc_vendor_audio_hal_2_0 = 83;
- optional Status init_svc_vendor_bluetooth_1_0 = 84;
- optional Status init_svc_vendor_boot_hal_1_0 = 85;
- optional Status init_svc_vendor_camera_provider_2_4 = 86;
- optional Status init_svc_vendor_cas_hal_1_0 = 87;
- optional Status init_svc_vendor_cnd = 88;
- optional Status init_svc_vendor_cnss_daemon = 89;
- optional Status init_svc_vendor_cnss_diag = 90;
- optional Status init_svc_vendor_configstore_hal = 91;
- optional Status init_svc_vendor_contexthub_hal_1_0 = 92;
- optional Status init_svc_vendor_devstart_sh = 93;
- optional Status init_svc_vendor_drm_hal_1_0 = 94;
- optional Status init_svc_vendor_drm_widevine_hal_1_0 = 95;
- optional Status init_svc_vendor_dumpstate_1_0 = 96;
- optional Status init_svc_vendor_flash_nanohub_fw = 97;
- optional Status init_svc_vendor_foreground_sh = 98;
- optional Status init_svc_vendor_fps_hal = 99;
- optional Status init_svc_vendor_gatekeeper_1_0 = 100;
- optional Status init_svc_vendor_gnss_service = 101;
- optional Status init_svc_vendor_gralloc_2_0 = 102;
- optional Status init_svc_vendor_hci_filter_root = 103;
- optional Status init_svc_vendor_hwcomposer_2_1 = 104;
- optional Status init_svc_vendor_imsdatadaemon = 105;
- optional Status init_svc_vendor_imsqmidaemon = 106;
- optional Status init_svc_vendor_init_radio_sh = 107;
- optional Status init_svc_vendor_irsc_util = 108;
- optional Status init_svc_vendor_keymaster_3_0 = 109;
- optional Status init_svc_vendor_light_hal_2_0 = 110;
- optional Status init_svc_vendor_loc_launcher = 111;
- optional Status init_svc_vendor_media_omx = 112;
- optional Status init_svc_vendor_memtrack_hal_1_0 = 113;
- optional Status init_svc_vendor_mid_sh = 114;
- optional Status init_svc_vendor_msm_irqbalance = 115;
- optional Status init_svc_vendor_nanohub_slpi = 116;
- optional Status init_svc_vendor_netmgrd = 117;
- optional Status init_svc_vendor_nfc_hal_service = 118;
- optional Status init_svc_vendor_per_mgr = 119;
- optional Status init_svc_vendor_per_proxy = 120;
- optional Status init_svc_vendor_perfd = 121;
- optional Status init_svc_vendor_port_bridge = 122;
- optional Status init_svc_vendor_power_hal_1_1 = 123;
- optional Status init_svc_vendor_power_sh = 124;
- optional Status init_svc_vendor_qseecomd = 125;
- optional Status init_svc_vendor_ramdump_auto = 126;
- optional Status init_svc_vendor_rmt_storage = 127;
- optional Status init_svc_vendor_sensors_hal_1_0 = 128;
- optional Status init_svc_vendor_ss_ramdump = 129;
- optional Status init_svc_vendor_ssr_setup = 130;
- optional Status init_svc_vendor_thermal_engine = 131;
- optional Status init_svc_vendor_time_daemon = 132;
- optional Status init_svc_vendor_usb_hal_1_1 = 133;
- optional Status init_svc_vendor_vibrator_1_0 = 134;
- optional Status init_svc_vendor_vr_1_0 = 135;
- optional Status init_svc_vendor_wifi_hal_legacy = 136;
- optional Status init_svc_virtual_touchpad = 137;
- optional Status init_svc_vndservicemanager = 138;
- optional Status init_svc_vold = 139;
- optional Status init_svc_vr_hwc = 140;
- optional Status init_svc_webview_zygote32 = 141;
- optional Status init_svc_wificond = 142;
- optional Status init_svc_wpa_supplicant = 143;
- optional Status init_svc_zygote = 144;
- optional Status init_svc_zygote_secondary = 145;
+ optional AacDrc aac_drc = 2;
- optional bool keyguard_no_require_sim = 146;
+ message Aaudio {
+ optional int32 hw_burst_min_usec = 1;
+ optional int32 minimum_sleep_usec = 2;
+ optional int32 mixer_bursts = 3;
+ optional int32 mmap_exclusive_policy = 4;
+ optional int32 mmap_policy = 5;
+ optional int32 wakeup_delay_usec = 6;
- optional string log_tag_WifiHAL = 147;
+ // Next Tag: 7
+ }
+ optional Aaudio aaudio = 3;
- optional bool logd_logpersistd_enable = 148;
+ optional int32 af_fast_track_multiplier = 4;
- optional bool media_mediadrmservice_enable = 149;
- optional bool media_recorder_show_manufacturer_and_model = 150;
+ message Camera {
+ optional bool disable_zsl_mode = 1;
+ optional int32 fifo_disable = 2;
- optional string net_bt_name = 151;
- optional string net_dns1 = 152;
- optional string net_dns2 = 153;
- optional string net_dns3 = 154;
- optional string net_dns4 = 155;
- optional bool net_lte_ims_data_enabled = 156;
- optional int32 net_qtaguid_enabled = 157;
- optional int32 net_tcp_2g_init_rwnd = 158;
- repeated int32 net_tcp_buffersize_default = 159;
- repeated int32 net_tcp_buffersize_edge = 160;
- repeated int32 net_tcp_buffersize_evdo = 161;
- repeated int32 net_tcp_buffersize_gprs = 162;
- repeated int32 net_tcp_buffersize_hsdpa = 163;
- repeated int32 net_tcp_buffersize_hspa = 164;
- repeated int32 net_tcp_buffersize_hspap = 165;
- repeated int32 net_tcp_buffersize_hsupa = 166;
- repeated int32 net_tcp_buffersize_lte = 167;
- repeated int32 net_tcp_buffersize_umts = 168;
- repeated int32 net_tcp_buffersize_wifi = 169;
- optional int32 net_tcp_default_init_rwnd = 170;
+ // Next Tag: 3
+ }
+ optional Camera camera = 5;
- optional bool nfc_initialized = 171;
+ message DalvikVm {
+ optional string appimageformat = 1;
+ optional string backgroundgctype = 2;
+ optional bool checkjni = 3;
+ optional string dex2oat_filter = 4;
+ optional string dex2oat_flags = 5;
+ optional int32 dex2oat_threads = 6;
+ optional string dex2oat_Xms = 7;
+ optional string dex2oat_Xmx = 8;
+ optional bool dexopt_secondary = 9;
+ optional string execution_mode = 10;
+ optional string extra_opts = 11;
+ optional string gctype = 12;
+ optional string heapgrowthlimit = 13;
+ optional string heapmaxfree = 14;
+ optional string heapminfree = 15;
+ optional string heapsize = 16;
+ optional string heapstartsize = 17;
+ optional double heaptargetutilization = 18;
+ optional int32 hot_startup_method_samples = 19;
+ optional string image_dex2oat_filter = 20;
+ optional string image_dex2oat_flags = 21;
+ optional int32 image_dex2oat_threads = 22;
+ optional string image_dex2oat_Xms = 23;
+ optional string image_dex2oat_Xmx = 24;
+ optional string isa_arm_features = 25;
+ optional string isa_arm_variant = 26;
+ optional string isa_arm64_features = 27;
+ optional string isa_arm64_variant = 28;
+ optional string isa_mips_features = 29;
+ optional string isa_mips_variant = 30;
+ optional string isa_mips64_features = 31;
+ optional string isa_mips64_variant = 32;
+ optional string isa_unknown_features = 33;
+ optional string isa_unknown_variant = 34;
+ optional string isa_x86_64_features = 35;
+ optional string isa_x86_64_variant = 36;
+ optional string isa_x86_features = 37;
+ optional string isa_x86_variant = 38;
+ optional string jitinitialsize = 39;
+ optional string jitmaxsize = 40;
+ optional int32 jitprithreadweight = 41;
+ optional int32 jitthreshold = 42;
+ optional int32 jittransitionweight = 43;
+ optional string jniopts = 44;
+ optional int32 lockprof_threshold = 45;
+ optional bool method_trace = 46;
+ optional string method_trace_file = 47;
+ optional int32 method_trace_file_siz = 48;
+ optional bool method_trace_stream = 49;
+ optional bool profilesystemserver = 50;
+ optional string stack_trace_dir = 51;
+ optional bool usejit = 52;
+ optional bool usejitprofiles = 53;
+ optional int32 zygote_max_boot_retry = 54;
- optional bool persist_audio_fluence_speaker = 172;
- optional bool persist_audio_fluence_voicecall = 173;
- optional bool persist_audio_fluence_voicecomm = 174;
- optional bool persist_audio_fluence_voicerec = 175;
+ // Next Tag: 55
+ }
+ optional DalvikVm dalvik_vm = 6;
- optional int32 persist_camera_debug_logfile = 176;
- optional int32 persist_camera_eis_enable = 177;
- optional int32 persist_camera_gyro_android = 178;
- optional int32 persist_camera_is_type = 179;
- optional int32 persist_camera_tnr_preview = 180;
- optional int32 persist_camera_tnr_video = 181;
- optional int32 persist_camera_tof_direct = 182;
+ optional bool drm_64bit_enabled = 7;
+ optional bool drm_service_enabled = 8;
+ optional bool dumpstate_dry_run = 9;
+ optional string gsm_sim_operator_numeric = 10;
+ optional bool hal_instrumentation_enable = 11;
- optional int32 persist_cne_feature = 183;
+ message InitSvc {
+ enum Status {
+ STATUS_UNKNOWN = 0;
+ STATUS_RUNNING = 1;
+ STATUS_STOPPED = 2;
+ }
+ optional Status adbd = 1;
+ optional Status audioserver = 2;
+ optional Status bootanim = 3;
+ optional Status bufferhubd = 4;
+ optional Status cameraserver = 5;
+ optional Status clear_bcb = 6;
+ optional Status drm = 7;
+ optional Status gatekeeperd = 8;
+ optional Status healthd = 9;
+ optional Status hidl_memory = 10;
+ optional Status hostapd = 11;
+ optional Status hwservicemanager = 12;
+ optional Status installd = 13;
+ optional Status keystore = 14;
+ optional Status lmkd = 15;
+ optional Status logd = 16;
+ optional Status logd_reinit = 17;
+ optional Status media = 18;
+ optional Status mediadrm = 19;
+ optional Status mediaextractor = 20;
+ optional Status mediametrics = 21;
+ optional Status netd = 22;
+ optional Status performanced = 23;
+ optional Status ril_daemon = 24;
+ optional Status servicemanager = 25;
+ optional Status storaged = 26;
+ optional Status surfaceflinger = 27;
+ optional Status thermalservice = 28;
+ optional Status tombstoned = 29;
+ optional Status ueventd = 30;
+ optional Status update_engine = 31;
+ optional Status update_verifier_nonencrypted = 32;
+ optional Status virtual_touchpad = 33;
+ optional Status vndservicemanager = 34;
+ optional Status vold = 35;
+ optional Status vr_hwc = 36;
+ optional Status webview_zygote32 = 37;
+ optional Status wificond = 38;
+ optional Status wpa_supplicant = 39;
+ optional Status zygote = 40;
+ optional Status zygote_secondary = 41;
- optional bool persist_data_iwlan_enable = 184;
- optional string persist_data_mode = 185;
+ // Next Tag: 42
+ }
+ optional InitSvc init_svc = 12;
- optional int32 persist_radio_RATE_ADAPT_ENABLE = 186;
- optional int32 persist_radio_ROTATION_ENABLE = 187;
- optional int32 persist_radio_VT_ENABLE = 188;
- optional int32 persist_radio_VT_HYBRID_ENABLE = 189;
- optional int32 persist_radio_adb_log_on = 190;
- optional int32 persist_radio_airplane_mode_on = 191;
- optional int32 persist_radio_apm_sim_not_pwdn = 192;
- optional int32 persist_radio_custom_ecc = 193;
- optional bool persist_radio_data_con_rprt = 194;
- optional int32 persist_radio_data_ltd_sys_ind = 195;
- optional string persist_radio_enable_tel_mon = 196;
- optional bool persist_radio_eons_enabled = 197;
- optional bool persist_radio_is_wps_enabled = 198;
- optional int32 persist_radio_pwropt_modepref_0 = 199;
- optional int32 persist_radio_ril_payload_on = 200;
- optional int32 persist_radio_sglte_target = 201;
- optional int32 persist_radio_sib16_support = 202;
- optional int32 persist_radio_smlog_switch = 203;
- optional int32 persist_radio_snapshot_enabled = 204;
- optional int32 persist_radio_snapshot_timer = 205;
- optional int32 persist_radio_sw_mbn_loaded = 206;
- optional int32 persist_radio_sw_mbn_update = 207;
- optional string persist_radio_ver_info = 208;
- optional int32 persist_radio_videopause_mode = 209;
+ optional bool keyguard_no_require_sim = 13;
+ optional string libc_debug_malloc_options = 14;
+ optional string libc_debug_malloc_program = 15;
- optional int32 persist_rcs_supported = 210;
+ message Log {
+ optional string tag_WifiHAL = 1;
+ optional string tag_stats_log = 2;
- optional string persist_sys_boot_reason = 211;
- optional int32 persist_sys_cnd_iwlan = 212;
- optional string persist_sys_dalvik_vm_lib_2 = 213;
- optional string persist_sys_gps_lpp = 214;
- optional string persist_sys_locale = 215;
- optional int32 persist_sys_ssr_enable_ramdumps = 216;
- optional string persist_sys_ssr_restart_level = 217;
- optional string persist_sys_timezone = 218;
- optional string persist_sys_usb_config = 219;
- optional int32 persist_sys_webview_vmsize = 220;
+ // Next Tag: 3
+ }
+ optional Log log = 16;
- optional string pm_dexopt_ab_ota = 221;
- optional string pm_dexopt_bg_dexopt = 222;
- optional string pm_dexopt_boot = 223;
- optional string pm_dexopt_first_boot = 224;
- optional string pm_dexopt_inactive = 225;
- optional string pm_dexopt_install = 226;
- optional string pm_dexopt_shared = 227;
+ optional bool media_mediadrmservice_enable = 17;
+ optional bool media_recorder_show_manufacturer_and_model = 18;
- optional string qcom_bluetooth_soc = 228;
+ message Persist {
+ optional string config_calibration_fac = 1;
+ optional int32 dbg_volte_avail_ovr = 2;
+ optional int32 dbg_vt_avail_ovr = 3;
+ optional int32 dbg_wfc_avail_ovr = 4;
+ optional int32 radio_airplane_mode_on = 5;
+ optional string radio_multisim_config = 6;
+ optional int32 rcs_supported = 7;
+ optional bool sys_crash_rcu = 8;
+ optional string sys_dalvik_vm_lib_2 = 9;
+ optional float sys_sf_color_saturation = 10;
+ optional string sys_timezone = 11;
- optional int32 qdcm_diagonal_matrix_mode = 229;
- optional int32 qdcm_only_pcc_for_trans = 230;
+ // Next Tag: 12
+ }
+ optional Persist persist = 19;
- repeated string ril_ecclist = 231;
- optional int32 ril_power_backoff_suppressed = 232;
- optional int32 ril_qcril_pre_init_lock_held = 233;
- optional string ril_voice_network_type = 234;
- optional string rild_libpath = 235;
+ message PmDexopt {
+ optional string ab_ota = 1;
+ optional string bg_dexopt = 2;
+ optional string boot = 3;
+ optional string first_boot = 4;
+ optional string install = 5;
- optional int32 ro_adb_secure = 236;
- optional int32 ro_allow_mock_location = 237;
- repeated string ro_atrace_core_services = 238;
- optional string ro_baseband = 239;
- optional int32 ro_bionic_ld_warning = 240;
- optional bool ro_bluetooth_dun = 241;
- optional string ro_bluetooth_hfp_ver = 242;
- optional bool ro_bluetooth_sap = 243;
- optional string ro_board_platform = 244;
+ // Next Tag: 6
+ }
+ optional PmDexopt pm_dexopt = 20;
- optional string ro_boot_baseband = 245;
- optional string ro_boot_bootdevice = 246;
- optional string ro_boot_bootloader = 247;
- optional string ro_boot_bootreason = 248;
- repeated string ro_boot_boottime = 249;
- optional int32 ro_boot_cid = 250;
- optional string ro_boot_console = 251;
- optional string ro_boot_ddrinfo = 252;
- optional string ro_boot_ddrsize = 253;
- optional int32 ro_boot_flash_locked = 254;
- optional int32 ro_boot_fp_src = 255;
- optional string ro_boot_hardware = 256;
- optional string ro_boot_hardware_color = 257;
- optional string ro_boot_hardware_ddr = 258;
- optional string ro_boot_hardware_revision = 259;
- optional string ro_boot_hardware_sku = 260;
- optional string ro_boot_hardware_ufs = 261;
- optional string ro_boot_htc_hrdump = 262;
- optional int32 ro_boot_keymaster = 263;
- optional string ro_boot_mid = 264;
- optional int32 ro_boot_msm_hw_ver_id = 265;
- optional int32 ro_boot_oem_unlock_support = 266;
- optional int32 ro_boot_qf_st = 267;
- optional int32 ro_boot_ramdump_enable = 268;
- optional string ro_boot_serialno = 269;
- optional string ro_boot_slot_suffix = 270;
- optional int32 ro_boot_temp_protect_ignore = 271;
- optional string ro_boot_vendor_overlay_theme = 272;
- optional string ro_boot_verifiedbootstate = 273;
- optional string ro_boot_veritymode = 274;
- optional string ro_boot_wificountrycode = 275;
+ message Ro {
+ optional int32 adb_secure = 1;
+ optional string arch = 2;
+ optional bool audio_ignore_effects = 3;
+ optional bool audio_monitorRotation = 4;
+ optional string baseband = 5;
+ optional string board_platform = 6;
- optional string ro_bootimage_build_date = 276;
- optional int64 ro_bootimage_build_date_utc = 277;
- optional string ro_bootimage_build_fingerprint = 278;
+ message Boot {
+ optional string avb_version = 1;
+ optional string baseband = 2;
+ optional string bootdevice = 3;
+ optional string bootloader = 4;
+ repeated string boottime = 5;
+ optional string console = 6;
+ optional int32 fake_battery = 7;
+ optional string hardware = 8;
+ optional string hardware_color = 9;
+ optional string hardware_revision = 10;
+ optional string hardware_sku = 11;
+ optional string keymaster = 12;
+ optional string mode = 13;
+ optional string revision = 14;
+ optional string slot_suffix = 15;
+ optional string vbmeta_avb_version = 16;
+ optional string vendor_overlay_theme = 17;
+ optional string verifiedbootstate = 18;
+ optional string veritymode = 19;
+ optional string wificountrycode = 20;
- optional string ro_bootloader = 279;
- optional string ro_bootmode = 280;
+ // Next Tag: 21
+ }
+ optional Boot boot = 7;
- optional int64 ro_boottime_adbd = 281;
- optional int64 ro_boottime_audioserver = 282;
- optional int64 ro_boottime_bootanim = 283;
- optional int64 ro_boottime_bufferhubd = 284;
- optional int64 ro_boottime_cameraserver = 285;
- optional int64 ro_boottime_clear_bcb = 286;
- optional int64 ro_boottime_drm = 287;
- optional int64 ro_boottime_gatekeeperd = 288;
- optional int64 ro_boottime_healthd = 289;
- optional int64 ro_boottime_hidl_memory = 290;
- optional int64 ro_boottime_hwservicemanager = 291;
- optional int64 ro_boottime_init = 292;
- optional int64 ro_boottime_init_cold_boot_wait = 293;
- optional int32 ro_boottime_init_mount_all_early = 294;
- optional int32 ro_boottime_init_mount_all_late = 295;
- optional int32 ro_boottime_init_selinux = 296;
- optional int64 ro_boottime_installd = 297;
- optional int64 ro_boottime_keystore = 298;
- optional int64 ro_boottime_lmkd = 299;
- optional int64 ro_boottime_logd = 300;
- optional int64 ro_boottime_logd_reinit = 301;
- optional int64 ro_boottime_media = 302;
- optional int64 ro_boottime_mediadrm = 303;
- optional int64 ro_boottime_mediaextractor = 304;
- optional int64 ro_boottime_mediametrics = 305;
- optional int64 ro_boottime_netd = 306;
- optional int64 ro_boottime_performanced = 307;
- optional int64 ro_boottime_ril_daemon = 308;
- optional int64 ro_boottime_servicemanager = 309;
- optional int64 ro_boottime_storaged = 310;
- optional int64 ro_boottime_surfaceflinger = 311;
- optional int64 ro_boottime_thermalservice = 312;
- optional int64 ro_boottime_tombstoned = 313;
- optional int64 ro_boottime_ueventd = 314;
- optional int64 ro_boottime_update_engine = 315;
- optional int64 ro_boottime_update_verifier_nonencrypted = 316;
- optional int64 ro_boottime_vendor_adsprpcd = 317;
- optional int64 ro_boottime_vendor_atfwd = 318;
- optional int64 ro_boottime_vendor_audio_hal_2_0 = 319;
- optional int64 ro_boottime_vendor_bluetooth_1_0 = 320;
- optional int64 ro_boottime_vendor_boot_hal_1_0 = 321;
- optional int64 ro_boottime_vendor_camera_provider_2_4 = 322;
- optional int64 ro_boottime_vendor_cas_hal_1_0 = 323;
- optional int64 ro_boottime_vendor_cnd = 324;
- optional int64 ro_boottime_vendor_cnss_daemon = 325;
- optional int64 ro_boottime_vendor_cnss_diag = 326;
- optional int64 ro_boottime_vendor_configstore_hal = 327;
- optional int64 ro_boottime_vendor_contexthub_hal_1_0 = 328;
- optional int64 ro_boottime_vendor_devstart_sh = 329;
- optional int64 ro_boottime_vendor_drm_hal_1_0 = 330;
- optional int64 ro_boottime_vendor_drm_widevine_hal_1_0 = 331;
- optional int64 ro_boottime_vendor_dumpstate_1_0 = 332;
- optional int64 ro_boottime_vendor_flash_nanohub_fw = 333;
- optional int64 ro_boottime_vendor_foreground_sh = 334;
- optional int64 ro_boottime_vendor_fps_hal = 335;
- optional int64 ro_boottime_vendor_gatekeeper_1_0 = 336;
- optional int64 ro_boottime_vendor_gnss_service = 337;
- optional int64 ro_boottime_vendor_gralloc_2_0 = 338;
- optional int64 ro_boottime_vendor_hci_filter_root = 339;
- optional int64 ro_boottime_vendor_hwcomposer_2_1 = 340;
- optional int64 ro_boottime_vendor_imsdatadaemon = 341;
- optional int64 ro_boottime_vendor_imsqmidaemon = 342;
- optional int64 ro_boottime_vendor_init_radio_sh = 343;
- optional int64 ro_boottime_vendor_irsc_util = 344;
- optional int64 ro_boottime_vendor_keymaster_3_0 = 345;
- optional int64 ro_boottime_vendor_light_hal_2_0 = 346;
- optional int64 ro_boottime_vendor_loc_launcher = 347;
- optional int64 ro_boottime_vendor_media_omx = 348;
- optional int64 ro_boottime_vendor_memtrack_hal_1_0 = 349;
- optional int64 ro_boottime_vendor_mid_sh = 350;
- optional int64 ro_boottime_vendor_msm_irqbalance = 351;
- optional int64 ro_boottime_vendor_nanohub_slpi = 352;
- optional int64 ro_boottime_vendor_netmgrd = 353;
- optional int64 ro_boottime_vendor_nfc_hal_service = 354;
- optional int64 ro_boottime_vendor_per_mgr = 355;
- optional int64 ro_boottime_vendor_per_proxy = 356;
- optional int64 ro_boottime_vendor_perfd = 357;
- optional int64 ro_boottime_vendor_port_bridge = 358;
- optional int64 ro_boottime_vendor_power_hal_1_1 = 359;
- optional int64 ro_boottime_vendor_power_sh = 360;
- optional int64 ro_boottime_vendor_qseecomd = 361;
- optional int64 ro_boottime_vendor_ramdump_auto = 362;
- optional int64 ro_boottime_vendor_rmt_storage = 363;
- optional int64 ro_boottime_vendor_sensors_hal_1_0 = 364;
- optional int64 ro_boottime_vendor_ss_ramdump = 365;
- optional int64 ro_boottime_vendor_ssr_setup = 366;
- optional int64 ro_boottime_vendor_thermal_engine = 367;
- optional int64 ro_boottime_vendor_time_daemon = 368;
- optional int64 ro_boottime_vendor_usb_hal_1_1 = 369;
- optional int64 ro_boottime_vendor_vibrator_1_0 = 370;
- optional int64 ro_boottime_vendor_vr_1_0 = 371;
- optional int64 ro_boottime_vendor_wifi_hal_legacy = 372;
- optional int64 ro_boottime_virtual_touchpad = 373;
- optional int64 ro_boottime_vndservicemanager = 374;
- optional int64 ro_boottime_vold = 375;
- optional int64 ro_boottime_vr_hwc = 376;
- optional int64 ro_boottime_webview_zygote32 = 377;
- optional int64 ro_boottime_wificond = 378;
- optional int64 ro_boottime_wpa_supplicant = 379;
- optional int64 ro_boottime_zygote = 380;
- optional int64 ro_boottime_zygote_secondary = 381;
+ message BootImage {
+ optional string build_date = 1;
+ optional int32 build_date_utc = 2;
+ optional string build_fingerprint = 3;
- optional string ro_bt_bdaddr_path = 382;
+ // Next Tag: 4
+ }
+ optional BootImage bootimage = 8;
- optional bool ro_build_ab_update = 383;
- optional string ro_build_characteristics = 384;
- optional string ro_build_date = 385;
- optional int64 ro_build_date_utc = 386;
- optional string ro_build_description = 387;
- optional string ro_build_display_id = 388;
- optional string ro_build_expect_baseband = 389;
- optional string ro_build_expect_bootloader = 390;
- optional string ro_build_fingerprint = 391;
- optional string ro_build_flavor = 392;
- optional string ro_build_host = 393;
- optional string ro_build_id = 394;
- optional string ro_build_product = 395;
- optional bool ro_build_system_root_image = 396;
- optional string ro_build_tags = 397;
- optional string ro_build_type = 398;
- optional string ro_build_user = 399;
- optional string ro_build_version_all_codenames = 400;
- optional string ro_build_version_base_os = 401;
- optional string ro_build_version_codename = 402;
- optional string ro_build_version_incremental = 403;
- optional int32 ro_build_version_preview_sdk = 404;
- optional string ro_build_version_release = 405;
- optional int32 ro_build_version_sdk = 406;
- optional string ro_build_version_security_patch = 407;
+ optional string bootloader = 9;
+ optional string bootmode = 10;
- optional int32 ro_camera_notify_nfc = 408;
- optional string ro_carrier = 409;
- optional bool ro_com_android_dataroaming = 410;
- optional bool ro_com_android_prov_mobiledata = 411;
- optional string ro_com_google_clientidbase = 412;
- optional int32 ro_com_google_ime_theme_id = 413;
+ message Build {
+ optional string date = 1;
+ optional int32 date_utc = 2;
+ optional string description = 3;
+ optional string display_id = 4;
+ optional string host = 5;
+ optional string id = 6;
+ optional string product = 7;
+ optional bool system_root_image = 8;
+ optional string tags = 9;
+ optional string type = 10;
+ optional string user = 11;
- optional string ro_config_alarm_alert = 414;
- optional string ro_config_notification_sound = 415;
- optional string ro_config_ringtone = 416;
- optional int32 ro_config_vc_call_vol_steps = 417;
- optional string ro_control_privapp_permissions = 418;
- optional int32 ro_cp_system_other_odex = 419;
+ message Version {
+ optional string base_os = 1;
+ optional string codename = 2;
+ optional string incremental = 3;
+ optional int32 preview_sdk = 4;
+ optional string release = 5;
+ optional int32 sdk = 6;
+ optional string security_patch = 7;
- optional string ro_crypto_scrypt_params = 420;
- optional string ro_crypto_state = 421;
- optional string ro_crypto_type = 422;
- optional string ro_crypto_volume_filenames_mode = 423;
- optional int32 ro_dalvik_vm_native_bridge = 424;
- optional int32 ro_debuggable = 425;
- optional bool ro_device_owner = 426;
- optional string ro_error_receiver_system_apps = 427;
+ // Next Tag: 8
+ }
+ optional Version version = 12;
- optional int32 ro_facelock_black_timeout = 428;
- optional int32 ro_facelock_det_timeout = 429;
- optional int32 ro_facelock_est_max_time = 430;
- optional int32 ro_facelock_rec_timeout = 431;
+ // Next Tag: 13
+ }
+ optional Build build = 11;
- optional string ro_frp_pst = 432;
+ optional int32 camera_notify_nfc = 12;
+ optional string carrier = 13;
+ optional bool com_android_dataroaming = 14;
+ optional bool com_android_prov_mobiledata = 15;
+ optional string com_google_clientidbase = 16;
- optional string ro_hardware = 433;
- optional string ro_hardware_power = 434;
+ message Config {
+ optional string alarm_alert = 1;
+ optional int32 media_vol_steps = 2;
+ optional string notification_sound = 3;
+ optional string ringtone = 4;
+ optional int32 vc_call_vol_steps = 5;
- optional int32 ro_hwui_drop_shadow_cache_size = 435;
- optional int32 ro_hwui_gradient_cache_size = 436;
- optional int32 ro_hwui_layer_cache_size = 437;
- optional int32 ro_hwui_path_cache_size = 438;
- optional int32 ro_hwui_r_buffer_cache_size = 439;
- optional int32 ro_hwui_text_large_cache_height = 440;
- optional int32 ro_hwui_text_large_cache_width = 441;
- optional int32 ro_hwui_text_small_cache_height = 442;
- optional int32 ro_hwui_text_small_cache_width = 443;
- optional float ro_hwui_texture_cache_flushrate = 444;
- optional int32 ro_hwui_texture_cache_size = 445;
+ // Next Tag: 6
+ }
+ optional Config config = 17;
- optional bool ro_init_subcontexts_enabled = 446;
- optional int32 ro_kernel_android_checkjni = 447;
- optional string ro_logd_size = 448;
- optional int32 ro_min_freq_0 = 449;
- optional int32 ro_oem_unlock_supported = 450;
- optional bool ro_opa_eligible_device = 451;
- optional int32 ro_opengles_version = 452;
- optional bool ro_persistent_properties_ready = 453;
+ optional string control_privapp_permissions = 18;
+ optional int32 cp_system_other_odex = 19;
+ optional string crypto_scrypt_params = 20;
+ optional string crypto_state = 21;
+ optional string crypto_type = 22;
+ optional string dalvik_vm_native_bridge = 23;
+ optional int32 debuggable = 24;
+ optional string frp_pst = 25;
+ optional string gfx_driver_0 = 26;
- optional string ro_product_board = 454;
- optional string ro_product_brand = 455;
- optional string ro_product_cpu_abi = 456;
- repeated string ro_product_cpu_abilist = 457;
- repeated string ro_product_cpu_abilist32 = 458;
- optional string ro_product_cpu_abilist64 = 459;
- optional string ro_product_device = 460;
- optional int32 ro_product_first_api_level = 461;
- optional string ro_product_locale = 462;
- optional string ro_product_manufacturer = 463;
- optional string ro_product_model = 464;
- optional string ro_product_name = 465;
- optional string ro_product_vendor_brand = 466;
- optional string ro_product_vendor_device = 467;
- optional string ro_product_vendor_manufacturer = 468;
- optional string ro_product_vendor_model = 469;
- optional string ro_product_vendor_name = 470;
+ message Hardware {
+ optional string value = 1; // value of ro.hardware itself
- optional int32 ro_property_service_version = 471;
- optional string ro_qc_sdk_audio_fluencetype = 472;
- optional int32 ro_qcom_adreno_qgl_ShaderStorageImageExtendedFormats = 473;
- optional bool ro_qualcomm_bluetooth_ftp = 474;
- optional bool ro_qualcomm_bluetooth_hfp = 475;
- optional bool ro_qualcomm_bluetooth_hsp = 476;
- optional bool ro_qualcomm_bluetooth_map = 477;
- optional bool ro_qualcomm_bluetooth_nap = 478;
- optional bool ro_qualcomm_bluetooth_opp = 479;
- optional bool ro_qualcomm_bluetooth_pbap = 480;
+ optional string activity_recognition = 2;
+ optional string audio = 3;
+ optional string audio_policy = 4;
+ optional string audio_a2dp = 5;
+ optional string audio_primary = 6;
+ optional string audio_usb = 7;
+ optional string bootctrl = 8;
+ optional string camera = 9;
+ optional string consumerir = 10;
+ optional string context_hub = 11;
+ optional string egl = 12;
+ optional string fingerprint = 13;
+ optional string flp = 14;
+ optional string gatekeeper = 15;
+ optional string gps = 16;
+ optional string gralloc = 17;
+ optional string hdmi_cec = 18;
+ optional string hwcomposer = 19;
+ optional string input = 20;
+ optional string keystore = 21;
+ optional string lights = 22;
+ optional string local_time = 23;
+ optional string memtrack = 24;
+ optional string nfc = 25;
+ optional string nfc_nci = 26;
+ optional string nfc_tag = 27;
+ optional string nvram = 28;
+ optional string power = 29;
+ optional string radio = 30;
+ optional string sensors = 31;
+ optional string sound_trigger = 32;
+ optional string thermal = 33;
+ optional string tv_input = 34;
+ optional string type = 35;
+ optional string vehicle = 36;
+ optional string vibrator = 37;
+ optional string virtual_device = 38;
+ optional string vulkan = 39;
- optional string ro_radio_log_loc = 481;
- optional string ro_radio_log_prefix = 482;
- optional int32 ro_revision = 483;
- optional bool ro_ril_svdo = 484;
- optional bool ro_ril_svlte1x = 485;
- optional int64 ro_runtime_firstboot = 486;
- optional int32 ro_secure = 487;
- optional string ro_serialno = 488;
- optional int32 ro_setupwizard_enterprise_mode = 489;
- optional bool ro_setupwizard_rotation_locked = 490;
- optional int32 ro_sf_lcd_density = 491;
- optional bool ro_storage_manager_enabled = 492;
- optional bool ro_telephony_call_ring_multiple = 493;
- optional int32 ro_telephony_default_cdma_sub = 494;
- optional int32 ro_telephony_default_network = 495;
- optional bool ro_treble_enabled = 496;
- optional string ro_url_legal = 497;
- optional string ro_url_legal_android_privacy = 498;
- optional string ro_vendor_build_date = 499;
- optional int64 ro_vendor_build_date_utc = 500;
- optional string ro_vendor_build_fingerprint = 501;
- optional string ro_vendor_extension_library = 502;
- optional bool ro_wallpapers_loc_request_suw = 503;
- optional string ro_wifi_channels = 504;
- optional string ro_zygote = 505;
+ // Next Tag: 40
+ }
+ optional Hardware hardware = 27;
- optional int32 sdm_debug_disable_rotator_split = 506;
+ optional int32 kernel_qemu = 28;
+ optional int32 kernel_qemu_gles = 29;
+ optional int32 oem_unlock_supported = 30;
+ optional int32 opengles_version = 31;
- optional string selinux_restorecon_recursive = 507;
+ message Product {
+ optional string board = 1;
+ optional string brand = 2;
+ optional string cpu_abi = 3;
+ repeated string cpu_abilist = 4;
+ repeated string cpu_abilist32 = 5;
+ repeated string cpu_abilist64 = 6;
+ optional string device = 7;
+ optional int32 first_api_level = 8;
+ optional string manufacturer = 9;
+ optional string model = 10;
+ optional string name = 11;
- optional string sendbug_preferred_domain = 508;
+ message Vendor {
+ optional string brand = 1;
+ optional string device = 2;
+ optional string manufacturer = 3;
+ optional string model = 4;
+ optional string name = 5;
+ }
+ optional Vendor vendor = 12;
- optional string sensors_contexthub_lid_state = 509;
+ // Next Tag: 13
+ }
+ optional Product product = 32;
- optional int32 service_bootanim_exit = 510;
- optional int32 service_sf_present_timestamp = 511;
+ optional int32 property_service_version = 33;
+ optional string retaildemo_video_path = 34;
+ optional string revision = 35;
+ optional int32 sf_lcd_density = 36;
+ optional bool storage_manager_enabled = 37;
+ optional bool telephony_call_ring_multiple = 38;
+ optional int32 telephony_default_cdma_sub = 39;
+ optional int32 telephony_default_network = 40;
+ optional string url_legal = 41;
+ optional string url_legal_android_privacy = 42;
+ optional string vendor_build_date = 43;
+ optional int32 vendor_build_date_utc = 44;
+ optional string vendor_build_fingerprint = 45;
+ optional string vndk_version = 46;
+ optional int32 vts_coverage = 47;
+ optional string zygote = 48;
- optional string setupwizard_theme = 512;
+ // Next Tag: 49
+ }
+ optional Ro ro = 21;
- optional string sys_boot_reason = 513;
- optional int32 sys_boot_completed = 514;
- optional int32 sys_ims_QMI_DAEMON_STATUS = 515;
- optional bool sys_keymaster_loaded = 516;
- optional bool sys_listeners_registered = 517;
- optional int32 sys_logbootcomplete = 518;
- optional int32 sys_oem_unlock_allowed = 519;
- optional int32 sys_post_boot_parsed = 520;
- optional int32 sys_qcom_devup = 521;
- optional int32 sys_retaildemo_enabled = 522;
- optional string sys_slpi_firmware_version = 523;
- optional int32 sys_sysctl_extra_free_kbytes = 524;
- optional int32 sys_sysctl_tcp_def_init_rwnd = 525;
- optional bool sys_time_set = 526;
- optional string sys_usb_config = 527;
- optional int32 sys_usb_configfs = 528;
- optional string sys_usb_controller = 529;
- optional int32 sys_usb_ffs_max_read = 530;
- optional int32 sys_usb_ffs_max_write = 531;
- optional int32 sys_usb_ffs_mtp_ready = 532;
- optional int32 sys_usb_ffs_ready = 533;
- optional int32 sys_usb_mtp_device_type = 534;
- optional int32 sys_usb_rps_mask = 535;
- optional string sys_usb_state = 536;
- optional bool sys_user_0_ce_available = 537;
- optional int32 sys_wifitracing_started = 538;
+ optional string sendbug_preferred_domain = 22;
+ optional int32 service_bootanim_exit = 23;
- optional int32 telephony_lteOnCdmaDevice = 539;
+ message Sys {
+ optional int32 boot_completed = 1;
+ optional int32 boot_from_charger_mode = 2;
+ optional int32 retaildemo_enabled = 3;
+ optional string shutdown_requested = 4;
- optional int32 tombstoned_max_tombstone_count = 540;
+ message Usb {
+ optional string config = 1;
+ optional int32 configfs = 2;
+ optional string controller = 3;
+ optional int32 ffs_max_read = 4;
+ optional int32 ffs_max_write = 5;
+ optional int32 ffs_mtp_ready = 6;
+ optional int32 ffs_ready = 7;
+ optional int32 mtp_device_type = 8;
+ optional string state = 9;
- optional int32 vidc_debug_perf_mode = 541;
+ // Next Tag: 10
+ }
+ optional Usb usb = 5;
- optional int32 vold_has_adoptable = 542;
- optional int32 vold_has_quota = 543;
- optional int32 vold_post_fs_data_done = 544;
+ // Next Tag: 6
+ }
+ optional Sys sys = 24;
- optional int32 wc_transport_clean_up = 545;
- optional int32 wc_transport_hci_filter_status = 546;
- optional string wc_transport_patch_dnld_inprog = 547;
- optional int32 wc_transport_ref_count = 548;
- optional int32 wc_transport_soc_initialized = 549;
- optional bool wc_transport_start_root = 550;
- optional int32 wc_transport_vnd_power = 551;
+ optional int32 telephony_lteOnCdmaDevice = 25;
+ optional int32 tombstoned_max_tombstone_count = 26;
+ optional string vold_decrypt = 27;
+ optional int32 vold_post_fs_data_done = 28;
+ optional int32 vts_native_server_on = 29;
+ optional string wifi_direct_interface = 30;
+ optional string wifi_interface = 31;
- optional string wifi_interface = 552;
- optional string wlan_driver_status = 553;
-
- // Next Tag: 554
+ // Next Tag: 32
}
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 8d091ab..fb0ebed 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -387,8 +387,9 @@
optional SettingProto enable_cache_quota_calculation = 339;
optional SettingProto enable_deletion_helper_no_threshold_toggle = 340;
optional SettingProto notification_snooze_options = 341;
+ optional SettingProto enable_gnss_raw_meas_full_tracking = 346;
- // Next tag = 346;
+ // Next tag = 347;
}
message SecureSettingsProto {
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 3af6f51..d3ca496 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -21,6 +21,7 @@
import "frameworks/base/core/proto/android/app/notification.proto";
import "frameworks/base/core/proto/android/content/intent.proto";
import "frameworks/base/core/proto/android/graphics/rect.proto";
+import "frameworks/base/core/proto/android/internal/processstats.proto";
import "frameworks/base/core/proto/android/os/looper.proto";
import "frameworks/base/core/proto/android/server/intentresolver.proto";
import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
@@ -163,7 +164,7 @@
optional int64 uptime_duration_ms = 1;
optional int64 elapsed_realtime_ms = 2;
- message NativeProcess {
+ message ProcessMemory {
optional int32 pid = 1;
optional string process_name = 2;
@@ -197,7 +198,7 @@
optional HeapInfo native_heap = 3;
optional HeapInfo dalvik_heap = 4;
repeated MemoryInfo other_heaps = 5;
- optional HeapInfo unknown_heap = 6;
+ optional MemoryInfo unknown_heap = 6;
// Summation of native_heap, dalvik_heap, and other_heaps.
optional HeapInfo total_heap = 7;
@@ -219,7 +220,113 @@
}
optional AppSummary app_summary = 9;
}
- repeated NativeProcess native_processes = 3;
+ repeated ProcessMemory native_processes = 3;
+
+ message AppData {
+ optional ProcessMemory process_memory = 1;
+
+ message ObjectStats {
+ optional int32 view_instance_count = 1;
+ optional int32 view_root_instance_count = 2;
+ optional int32 app_context_instance_count = 3;
+ optional int32 activity_instance_count = 4;
+ optional int32 global_asset_count = 5;
+ optional int32 global_asset_manager_count = 6;
+ optional int32 local_binder_object_count = 7;
+ optional int32 proxy_binder_object_count = 8;
+ optional int64 parcel_memory_kb = 9;
+ optional int32 parcel_count = 10;
+ optional int32 binder_object_death_count = 11;
+ optional int32 open_ssl_socket_count = 12;
+ optional int32 webview_instance_count = 13;
+ }
+ optional ObjectStats objects = 2;
+
+ message SqlStats {
+ optional int32 memory_used_kb = 1;
+ optional int32 pagecache_overflow_kb = 2;
+ optional int32 malloc_size_kb = 3;
+
+ message Database {
+ optional string name = 1;
+ optional int32 page_size = 2;
+ optional int32 db_size = 3;
+ // Number of lookaside slots:
+ // http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html
+ optional int32 lookaside_b = 4;
+ // Statement cache stats: hits/misses/cachesize
+ optional string cache = 5;
+ }
+ repeated Database databases = 4;
+ }
+ optional SqlStats sql = 3;
+
+ optional string asset_allocations = 4;
+ optional string unreachable_memory = 5;
+ }
+ repeated AppData app_processes = 4;
+
+ message MemItem {
+ optional string tag = 1;
+ optional string label = 2;
+ optional int32 id = 3;
+ optional bool is_proc = 4;
+ optional bool has_activities = 5;
+ optional int64 pss_kb = 6;
+ optional int64 swap_pss_kb = 7;
+ repeated MemItem sub_items = 8;
+ }
+ repeated MemItem total_pss_by_process = 5;
+ repeated MemItem total_pss_by_oom_adjustment = 6;
+ repeated MemItem total_pss_by_category = 7;
+
+ optional int64 total_ram_kb = 8;
+ optional .com.android.internal.app.procstats.ProcessStatsProto.MemoryFactor status = 9;
+ // Total free RAM = cached_pss_kb + cached_kernel_kb + free_kb.
+ optional int64 cached_pss_kb = 10;
+ optional int64 cached_kernel_kb = 11;
+ optional int64 free_kb = 12;
+ // Total used RAM = used_pss_kb + used_kernel_kb.
+ optional int64 used_pss_kb = 13;
+ optional int64 used_kernel_kb = 14;
+
+ optional int64 lost_ram_kb = 15;
+
+ optional int64 total_zram_kb = 16;
+ optional int64 zram_physical_used_in_swap_kb = 17;
+ optional int64 total_zram_swap_kb = 18;
+
+ optional int64 ksm_sharing_kb = 19;
+ optional int64 ksm_shared_kb = 20;
+ optional int64 ksm_unshared_kb = 21;
+ optional int64 ksm_volatile_kb = 22;
+
+ // The approximate per-application memory class of the current device. This
+ // gives developers an idea of how hard a memory limit you should impose on
+ // their application to let the overall system work best. The value is in
+ // megabytes; the baseline Android memory class is 16 (which happens to be the
+ // Java heap limit of those devices); some devices with more memory may have
+ // 24 or even higher numbers.
+ optional int32 tuning_mb = 23;
+ // The approximate per-application memory class of the current device when an
+ // application is running with a large heap. This is the space available for
+ // memory-intensive applications; most applications should not need this
+ // amount of memory, and should instead stay with the tuning_mb limit. The
+ // value is in megabytes. This may be the same size as tuning_mb on memory
+ // constrained devices, or it may be significantly larger on devices with a
+ // large amount of available RAM.
+ // This is the size of the application's Dalvik heap if it has specified
+ // 'android:largeHeap="true"' in its manifest.
+ optional int32 tuning_large_mb = 24;
+
+ optional int64 oom_kb = 25;
+
+ // The maximum pss size in kb that we consider a process acceptable to restore
+ // from its cached state for running in the background when RAM is low.
+ optional int64 restore_limit_kb = 26;
+
+ optional bool is_low_ram_device = 27;
+ optional bool is_high_end_gfx = 28;
}
message StickyBroadcastProto {
diff --git a/core/proto/android/server/alarmmanagerservice.proto b/core/proto/android/server/alarmmanagerservice.proto
index d724437..87d302e 100644
--- a/core/proto/android/server/alarmmanagerservice.proto
+++ b/core/proto/android/server/alarmmanagerservice.proto
@@ -37,71 +37,71 @@
// Dump from ForceAppStandbyTracker.
optional ForceAppStandbyTrackerProto force_app_standby_tracker = 6;
- optional bool is_interactive = 8;
+ optional bool is_interactive = 7;
// Only valid if is_interactive is false.
- optional int64 time_since_non_interactive_ms = 9;
+ optional int64 time_since_non_interactive_ms = 8;
// Only valid if is_interactive is false.
- optional int64 max_wakeup_delay_ms = 10;
+ optional int64 max_wakeup_delay_ms = 9;
// Only valid if is_interactive is false.
- optional int64 time_since_last_dispatch_ms = 11;
+ optional int64 time_since_last_dispatch_ms = 10;
// Only valid if is_interactive is false.
- optional int64 time_until_next_non_wakeup_delivery_ms = 12;
+ optional int64 time_until_next_non_wakeup_delivery_ms = 11;
- optional int64 time_until_next_non_wakeup_alarm_ms = 13;
- optional int64 time_until_next_wakeup_ms = 14;
- optional int64 time_since_last_wakeup_ms = 15;
+ optional int64 time_until_next_non_wakeup_alarm_ms = 12;
+ optional int64 time_until_next_wakeup_ms = 13;
+ optional int64 time_since_last_wakeup_ms = 14;
// Time since the last wakeup was set.
- optional int64 time_since_last_wakeup_set_ms = 16;
- optional int64 time_change_event_count = 17;
+ optional int64 time_since_last_wakeup_set_ms = 15;
+ optional int64 time_change_event_count = 16;
// The current set of user whitelisted apps for device idle mode, meaning
// these are allowed to freely schedule alarms. These are app IDs, not UIDs.
- repeated int32 device_idle_user_whitelist_app_ids = 18;
+ repeated int32 device_idle_user_whitelist_app_ids = 17;
- repeated AlarmClockMetadataProto next_alarm_clock_metadata = 19;
+ repeated AlarmClockMetadataProto next_alarm_clock_metadata = 18;
- repeated BatchProto pending_alarm_batches = 20;
+ repeated BatchProto pending_alarm_batches = 19;
// List of alarms per uid deferred due to user applied background restrictions
// on the source app.
- repeated AlarmProto pending_user_blocked_background_alarms = 21;
+ repeated AlarmProto pending_user_blocked_background_alarms = 20;
// When idling mode will end. Will be empty if the device is not currently
// idling.
- optional AlarmProto pending_idle_until = 22;
+ optional AlarmProto pending_idle_until = 21;
// Any alarms that we don't want to run during idle mode. Will be empty if the
// device is not currently idling.
- repeated AlarmProto pending_while_idle_alarms = 23;
+ repeated AlarmProto pending_while_idle_alarms = 22;
// This is a special alarm that will put the system into idle until it goes
// off. The caller has given the time they want this to happen at.
- optional AlarmProto next_wake_from_idle = 24;
+ optional AlarmProto next_wake_from_idle = 23;
- repeated AlarmProto past_due_non_wakeup_alarms = 25;
+ repeated AlarmProto past_due_non_wakeup_alarms = 24;
// Number of delayed alarms.
- optional int32 delayed_alarm_count = 26;
+ optional int32 delayed_alarm_count = 25;
// The total amount of time alarms had been delayed. Overlapping alarms are
// only counted once (ie. If two alarms were meant to trigger at the same time
// but were delayed by 5 seconds, the total time would be 5 seconds).
- optional int64 total_delay_time_ms = 27;
- optional int64 max_delay_duration_ms = 28;
- optional int64 max_non_interactive_duration_ms = 29;
+ optional int64 total_delay_time_ms = 26;
+ optional int64 max_delay_duration_ms = 27;
+ optional int64 max_non_interactive_duration_ms = 28;
- optional int32 broadcast_ref_count = 30;
+ optional int32 broadcast_ref_count = 29;
// Canonical count of (operation.send() - onSendFinished()) and listener
// send/complete/timeout invocations.
- optional int32 pending_intent_send_count = 31;
- optional int32 pending_intent_finish_count = 32;
- optional int32 listener_send_count = 33;
- optional int32 listener_finish_count = 34;
+ optional int32 pending_intent_send_count = 30;
+ optional int32 pending_intent_finish_count = 31;
+ optional int32 listener_send_count = 32;
+ optional int32 listener_finish_count = 33;
- repeated InFlightProto outstanding_deliveries = 35;
+ 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 = 36;
+ optional int64 allow_while_idle_min_duration_ms = 35;
message LastAllowWhileIdleDispatch {
optional int32 uid = 1;
@@ -110,25 +110,25 @@
}
// 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 = 37;
+ repeated LastAllowWhileIdleDispatch last_allow_while_idle_dispatch_times = 36;
- optional com.android.internal.util.LocalLogProto recent_problems = 38;
+ optional com.android.internal.util.LocalLogProto recent_problems = 37;
message TopAlarm {
optional int32 uid = 1;
optional string package_name = 2;
optional FilterStatsProto filter = 3;
}
- repeated TopAlarm top_alarms = 39;
+ repeated TopAlarm top_alarms = 38;
message AlarmStat {
optional BroadcastStatsProto broadcast = 1;
repeated FilterStatsProto filters = 2;
}
- repeated AlarmStat alarm_stats = 40;
+ repeated AlarmStat alarm_stats = 39;
- repeated IdleDispatchEntryProto allow_while_idle_dispatches = 41;
- repeated WakeupEventProto recent_wakeup_history = 42;
+ repeated IdleDispatchEntryProto allow_while_idle_dispatches = 40;
+ repeated WakeupEventProto recent_wakeup_history = 41;
}
// This is a soft wrapper for alarm clock information. It is not representative
@@ -145,8 +145,9 @@
message AlarmProto {
optional string tag = 1;
optional .android.app.AlarmManagerProto.AlarmType type = 2;
- // How long until the alarm goes off, in the 'elapsed' timebase.
- optional int64 when_elapsed_ms = 3;
+ // How long until the alarm goes off, in the 'elapsed' timebase. Can be
+ // negative if 'when' is in the past.
+ optional int64 time_until_when_elapsed_ms = 3;
optional int64 window_length_ms = 4;
optional int64 repeat_interval_ms = 5;
optional int32 count = 6;
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index d3b2cde..c1bd692 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -57,7 +57,7 @@
optional bool is_active = 3;
optional int32 num_wake_locks = 4;
optional bool is_process_state_unknown = 5;
- optional .android.app.ActivityManagerProto.ProcessState process_state = 6;
+ optional .android.app.ProcessState process_state = 6;
}
optional ConstantsProto constants = 1;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 915fe9d..ef7b6b0 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -170,6 +170,8 @@
optional WindowStateAnimatorProto animator = 13;
optional bool animating_exit = 14;
repeated WindowStateProto child_windows = 15;
+ optional .android.graphics.RectProto surface_position = 16;
+ optional .android.graphics.RectProto shown_position = 17;
}
message IdentifierProto {
diff --git a/core/proto/android/service/graphicsstats.proto b/core/proto/android/service/graphicsstats.proto
index ee9d6fc..f422065 100644
--- a/core/proto/android/service/graphicsstats.proto
+++ b/core/proto/android/service/graphicsstats.proto
@@ -19,7 +19,6 @@
option java_multiple_files = true;
option java_outer_classname = "GraphicsStatsServiceProto";
-option optimize_for = LITE_RUNTIME;
message GraphicsStatsServiceDumpProto {
repeated GraphicsStatsProto stats = 1;
@@ -31,7 +30,7 @@
optional string package_name = 1;
// The version code of the app
- optional int32 version_code = 2;
+ optional int64 version_code = 2;
// The start & end timestamps in UTC as
// milliseconds since January 1, 1970
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0920426..13fedfe 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -407,6 +407,7 @@
<protected-broadcast android:name="android.internal.policy.action.BURN_IN_PROTECTION" />
<protected-broadcast android:name="android.app.action.SYSTEM_UPDATE_POLICY_CHANGED" />
<protected-broadcast android:name="android.app.action.DEVICE_OWNER_CHANGED" />
+ <protected-broadcast android:name="android.app.action.MANAGED_USER_CREATED" />
<!-- Added in N -->
<protected-broadcast android:name="android.intent.action.ANR" />
@@ -1619,6 +1620,11 @@
<permission android:name="android.permission.ACCESS_PDB_STATE"
android:protectionLevel="signature" />
+ <!-- Allows testing if a passwords is forbidden by the admins.
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.TEST_BLACKLISTED_PASSWORD"
+ android:protectionLevel="signature" />
+
<!-- @hide Allows system update service to notify device owner about pending updates.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.NOTIFY_PENDING_SYSTEM_UPDATE"
@@ -2923,6 +2929,11 @@
<permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to modify the display brightness configuration
+ @hide -->
+ <permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"
+ android:protectionLevel="signature|privileged|development" />
+
<!-- @SystemApi Allows an application to control VPN.
<p>Not for use by third-party applications.</p>
@hide -->
diff --git a/core/res/res/drawable/ic_wifi_settings.xml b/core/res/res/drawable/ic_wifi_settings.xml
new file mode 100644
index 0000000..c678ad4
--- /dev/null
+++ b/core/res/res/drawable/ic_wifi_settings.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+ <path
+ android:fillColor="#000000"
+ android:pathData="M12.584,15.93c0.026-0.194,0.044-0.397,0.044-0.608c0-0.211-0.018-0.405-0.044-0.608l1.304-1.022
+c0.115-0.088,0.15-0.256,0.071-0.397l-1.234-2.133c-0.071-0.132-0.238-0.185-0.379-0.132l-1.533,0.617
+c-0.317-0.247-0.67-0.449-1.04-0.608L9.535,9.4c-0.018-0.132-0.141-0.247-0.3-0.247H6.768c-0.15,0-0.282,0.115-0.3,0.256
+L6.23,11.048c-0.379,0.159-0.723,0.361-1.04,0.608l-1.533-0.617c-0.141-0.053-0.3,0-0.379,0.132l-1.234,2.133
+c-0.079,0.132-0.044,0.3,0.07,0.397l1.304,1.022c-0.026,0.194-0.044,0.405-0.044,0.608s0.018,0.405,0.044,0.608l-1.304,1.022
+c-0.115,0.088-0.15,0.256-0.07,0.397l1.234,2.133c0.07,0.132,0.238,0.185,0.379,0.132l1.533-0.617
+c0.317,0.247,0.67,0.449,1.04,0.608l0.238,1.639c0.018,0.15,0.15,0.256,0.3,0.256h2.467c0.159,0,0.282-0.115,0.3-0.256
+l0.238-1.639c0.379-0.15,0.723-0.361,1.04-0.608l1.533,0.617c0.141,0.053,0.3,0,0.379-0.132l1.234-2.133
+c0.071-0.132,0.044-0.3-0.07-0.397L12.584,15.93z
+M8.002,17.481c-1.19,0-2.159-0.969-2.159-2.159s0.969-2.159,2.159-2.159
+s2.159,0.969,2.159,2.159C10.161,16.512,9.191,17.481,8.002,17.481z" />
+ <path
+ android:fillColor="#000000"
+ android:pathData="M16.003,12.026l5.995-7.474c-0.229-0.172-2.537-2.06-6-2.06s-5.771,1.889-6,2.06l5.995,7.469l0.005,0.01L16.003,12.026z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/interpolator/aggressive_ease.xml b/core/res/res/interpolator/aggressive_ease.xml
new file mode 100644
index 0000000..620424f
--- /dev/null
+++ b/core/res/res/interpolator/aggressive_ease.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0.2"
+ android:controlY1="0"
+ android:controlX2="0"
+ android:controlY2="1"/>
\ No newline at end of file
diff --git a/core/res/res/interpolator/emphasized_deceleration.xml b/core/res/res/interpolator/emphasized_deceleration.xml
new file mode 100644
index 0000000..60c315c
--- /dev/null
+++ b/core/res/res/interpolator/emphasized_deceleration.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0.1"
+ android:controlY1="0.8"
+ android:controlX2="0.2"
+ android:controlY2="1"/>
\ No newline at end of file
diff --git a/core/res/res/interpolator/exaggerated_ease.xml b/core/res/res/interpolator/exaggerated_ease.xml
new file mode 100644
index 0000000..4961c1c
--- /dev/null
+++ b/core/res/res/interpolator/exaggerated_ease.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.08, 0.166666, 0.4 C 0.225, 0.94, 0.25, 1, 1, 1"/>
diff --git a/core/res/res/layout/activity_list_item_2.xml b/core/res/res/layout/activity_list_item_2.xml
index 608e986..af1963c 100644
--- a/core/res/res/layout/activity_list_item_2.xml
+++ b/core/res/res/layout/activity_list_item_2.xml
@@ -21,5 +21,6 @@
android:textAppearance="?attr/textAppearanceListItemSmall"
android:gravity="center_vertical"
android:drawablePadding="14dip"
+ android:textAlignment="viewStart"
android:paddingStart="?attr/listPreferredItemPaddingStart"
android:paddingEnd="?attr/listPreferredItemPaddingEnd" />
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
index 865685f..435289d 100644
--- a/core/res/res/layout/notification_template_material_ambient.xml
+++ b/core/res/res/layout/notification_template_material_ambient.xml
@@ -72,7 +72,7 @@
android:textColor="#eeffffff"
android:layout_marginTop="4dp"
android:ellipsize="end"
- android:maxLines="7"
+ android:maxLines="3"
/>
</LinearLayout>
</LinearLayout>
diff --git a/core/res/res/layout/popup_menu_item_layout.xml b/core/res/res/layout/popup_menu_item_layout.xml
index 3eecb36..3b89f0d 100644
--- a/core/res/res/layout/popup_menu_item_layout.xml
+++ b/core/res/res/layout/popup_menu_item_layout.xml
@@ -33,6 +33,7 @@
<!-- The title and summary have some gap between them,
and this 'group' should be centered vertically. -->
<LinearLayout
+ android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="?attr/dropdownListPreferredItemHeight"
android:paddingEnd="16dip"
diff --git a/core/res/res/values-af/required_apps_managed_device.xml b/core/res/res/values-af/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-af/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-af/required_apps_managed_profile.xml b/core/res/res/values-af/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-af/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-af/required_apps_managed_user.xml b/core/res/res/values-af/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-af/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 66e4c09..1646073 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Sit af"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Noodgeval"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Foutverslag"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Beëindig sessie"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Neem foutverslag"</string>
<string name="bugreport_message" msgid="398447048750350456">"Dit sal inligting oor die huidige toestand van jou toestel insamel om as \'n e-posboodskap te stuur. Dit sal \'n tydjie neem vandat die foutverslag begin is totdat dit reg is om gestuur te word; wees asseblief geduldig."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktiewe verslag"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Wanneer die kortpad aan is, sal \'n toeganklikheidkenmerk begin word as albei volumeknoppies 3 sekondes lank gedruk word.\n\n Bestaande toeganklikheidkenmerk:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Jy kan die kenmerk in Instellings > Toeganklikheid verander."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Skakel kortpad af"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Gebruik kortpad"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Kleuromkering"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Kleurkorreksie"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Toeganklikheidskortpad het <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aangeskakel"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Toeganklikheidskortpad het <xliff:g id="SERVICE_NAME">%1$s</xliff:g> afgeskakel"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Kies \'n kenmerk om te gebruik wanneer jy op die Toeganklikheid-knoppie tik:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Weeksaand"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Naweek"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Geleentheid"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Slaap"</string>
<string name="muted_by" msgid="6147073845094180001">"Gedemp deur <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Daar is \'n interne probleem met jou toestel en dit sal dalk onstabiel wees totdat jy \'n fabriekterugstelling doen."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Daar is \'n interne probleem met jou toestel. Kontak jou vervaardiger vir besonderhede."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD-versoek is gewysig tot DIAL-versoek."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD-versoek is gewysig tot SS-versoek."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD-versoek is gewysig tot nuwe USSD-versoek."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD-versoek is gewysig tot Video DIAL-versoek."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-versoek is gewysig tot DIAL-versoek."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS-versoek is gewysig tot Video DIAL-versoek."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-versoek is gewysig tot USSD-versoek."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-versoek is gewysig tot nuwe SS-versoek."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Werkprofiel"</string>
diff --git a/core/res/res/values-am/required_apps_managed_device.xml b/core/res/res/values-am/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-am/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-am/required_apps_managed_profile.xml b/core/res/res/values-am/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-am/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-am/required_apps_managed_user.xml b/core/res/res/values-am/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-am/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 318fe81..868b065 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"ኃይል አጥፋ"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"ድንገተኛ አደጋ"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"የሳንካ ሪፖርት"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"ክፍለ-ጊዜን አብቃ"</string>
<string name="bugreport_title" msgid="2667494803742548533">"የሳንካ ሪፖርት ውሰድ"</string>
<string name="bugreport_message" msgid="398447048750350456">"ይሄ እንደ የኢሜይል መልዕክት አድርጎ የሚልከውን ስለመሣሪያዎ የአሁኑ ሁኔታ መረጃ ይሰበስባል። የሳንካ ሪፖርቱን ከመጀመር ጀምሮ እስኪላክ ድረስ ትንሽ ጊዜ ይወስዳል፤ እባክዎ ይታገሱ።"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"መስተጋብራዊ ሪፖርት"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"አቋራጩ ሲበራ ሁለቱንም የድምፅ አዝራሮች ለ3 ሰከንዶች ተጭኖ መቆየት የተደራሽነት ባህሪን ያስጀምረዋል።\n\n አሁን ያለ የተደራሽነት ባህሪ፦\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ባህሪውን በቅንብሮች > ተደራሽነት ውስጥ ሊለውጡት ይችላሉ።"</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"አቋራጩን አጥፋ"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"አቋራጭ ይጠቀሙ"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"ተቃራኒ ቀለም"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"የቀለም ማስተካከያ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"የተደራሽነት አቋራጭ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ን አብርቶታል"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"የተደራሽነት አቋራጭ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ን አጥፍቶታል"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"የተደራሽነት አዝራርን መታ በሚያደርጉበት ጊዜ ጥቅም ላይ የሚውለውን ባህሪ ይምረጡ፦"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"የሳምንት ለሊት"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"የሳምንት እረፍት ቀናት"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"ክስተት"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"መተኛት"</string>
<string name="muted_by" msgid="6147073845094180001">"ድምጽ በ<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ተዘግቷል"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"መሣሪያዎ ላይ የውስጣዊ ችግር አለ፣ የፋብሪካ ውሂብ ዳግም እስኪያስጀምሩት ድረስ ላይረጋጋ ይችላል።"</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"መሣሪያዎ ላይ የውስጣዊ ችግር አለ። ዝርዝሮችን ለማግኘት አምራችዎን ያነጋግሩ።"</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD ጥያቄ ወደ ደውል ጥያቄ ተሻሽሎዋል።"</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD ጥያቄ ወደ SS ጥያቄ ተሻሽሎዋል።"</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD ጥያቄ ወደ አዲስ USSD ጥያቄ ተሻሽሎዋል።"</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"የUSSD ጥያቄ ወደ የቪድዪኦ ደውል ጥያቄ ተቀይሯል።"</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS ጥያቄ ወደ ደውል ጥያቄ ተሻሽሎዋል።"</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"የSS ጥያቄ ወደ የቪዲዮ ደውል ጥያቄ ተቀይሯል።"</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS ጥያቄ ወደ USSD ጥያቄ ተሻሽሎዋል።"</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS ጥያቄ ወደ አዲስ SS ጥያቄ ተሻሽሎዋል።"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"የስራ መገለጫ"</string>
diff --git a/core/res/res/values-ar/required_apps_managed_device.xml b/core/res/res/values-ar/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-ar/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ar/required_apps_managed_profile.xml b/core/res/res/values-ar/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-ar/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ar/required_apps_managed_user.xml b/core/res/res/values-ar/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-ar/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 02345e2..2f69bd5 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -66,15 +66,15 @@
<string name="PwdMmi" msgid="7043715687905254199">"تغيير كلمة المرور"</string>
<string name="PinMmi" msgid="3113117780361190304">"تغيير رمز PIN"</string>
<string name="CnipMmi" msgid="3110534680557857162">"رقم الاتصال موجود"</string>
- <string name="CnirMmi" msgid="3062102121430548731">"رقم الاتصال مقيّد"</string>
+ <string name="CnirMmi" msgid="3062102121430548731">"رقم الاتصال محظور"</string>
<string name="ThreeWCMmi" msgid="9051047170321190368">"اتصال ثلاثي"</string>
<string name="RuacMmi" msgid="7827887459138308886">"رفض المكالمات المزعجة غير المرغوب فيها"</string>
<string name="CndMmi" msgid="3116446237081575808">"تسليم رقم الاتصال"</string>
<string name="DndMmi" msgid="1265478932418334331">"عدم الإزعاج"</string>
- <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"الإعداد الافتراضي لمعرف المتصل هو مقيّد. الاتصال التالي: مقيّد"</string>
- <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"الإعداد الافتراضي لمعرف المتصل هو مقيّد. الاتصال التالي: غير مقيّد"</string>
- <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"الإعداد الافتراضي لمعرف المتصل هو غير مقيّد. الاتصال التالي: مقيّد"</string>
- <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"الإعداد الافتراضي لمعرف المتصل هو غير مقيّد. الاتصال التالي: غير مقيّد"</string>
+ <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"الإعداد الافتراضي لمعرف المتصل هو محظور . الاتصال التالي: محظور"</string>
+ <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"الإعداد الافتراضي لمعرف المتصل هو محظور . الاتصال التالي: غير محظور"</string>
+ <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"الإعداد الافتراضي لمعرف المتصل هو غير محظور . الاتصال التالي: محظور"</string>
+ <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"الإعداد الافتراضي لمعرف المتصل هو غير محظور . الاتصال التالي: غير محظور"</string>
<string name="serviceNotProvisioned" msgid="8614830180508686666">"الخدمة غير متوفرة."</string>
<string name="CLIRPermanent" msgid="3377371145926835671">"لا يمكنك تغيير إعداد معرّف المتصل."</string>
<string name="RestrictedOnDataTitle" msgid="1322504692764166532">"ليست هناك خدمة بيانات"</string>
@@ -220,6 +220,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"إيقاف التشغيل"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"الطوارئ"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"تقرير الأخطاء"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"إنهاء الجلسة"</string>
<string name="bugreport_title" msgid="2667494803742548533">"إعداد تقرير بالأخطاء"</string>
<string name="bugreport_message" msgid="398447048750350456">"سيجمع هذا معلومات حول حالة جهازك الحالي لإرسالها كرسالة إلكترونية، ولكنه سيستغرق وقتًا قليلاً من بدء عرض تقرير بالأخطاء. وحتى يكون جاهزًا للإرسال، يُرجى الانتظار."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"تقرير تفاعلي"</string>
@@ -1594,6 +1595,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"عند تشغيل الاختصار، يؤدي الضغط على زرّي مستوى الصوت لمدة 3 ثوانٍ إلى تشغيل ميزة إمكانية الوصول.\n\n ميزة إمكانية الوصول الحالية:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n يمكنك تغيير الميزة من \"الإعدادات\" > \"إمكانية الوصول\"."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"إيقاف الاختصار"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"استخدام الاختصار"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"عكس الألوان"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"تصحيح الألوان"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"شغَّل اختصار إمكانية الوصول خدمة <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"أوقف اختصار إمكانية الوصول خدمة <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"يمكنك اختيار إحدى الميزات لاستخدامها عند النقر على زر إمكانية الوصول:"</string>
@@ -1816,13 +1819,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"ليلة يوم من أيام الأسبوع"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"نهاية الأسبوع"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"حدث"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"النوم"</string>
<string name="muted_by" msgid="6147073845094180001">"تم كتم الصوت بواسطة <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"حدثت مشكلة داخلية في جهازك، وقد لا يستقر وضعه حتى إجراء إعادة الضبط بحسب بيانات المصنع."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"حدثت مشكلة داخلية في جهازك. يمكنك الاتصال بالمصنِّع للحصول على تفاصيل."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"يتم تعديل طلب USSD لطلب الاتصال."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"يتم تعديل طلب USSD إلى طلب SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"يتم تعديل طلب USSD إلى طلب USSD الجديد."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"تم تعديل طلب USSD إلى طلب Video DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"يتم تعديل الطلب SS لطلب الاتصال."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"تم تعديل طلب SS إلى طلب Video DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"يتم تعديل طلب SS إلى طلب USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"يتم تعديل طلب SS إلى طلب SS الجديد."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"الملف الشخصي للعمل"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 6041eeb..a90ef03 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Söndür"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Təcili"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Baq hesabatı"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Sessiyanı sonlandırın"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Baqı xəbər verin"</string>
<string name="bugreport_message" msgid="398447048750350456">"Bu, sizin hazırkı cihaz durumu haqqında məlumat toplayacaq ki, elektron məktub şəklində göndərsin. Baq raportuna başlamaq üçün bir az vaxt lazım ola bilər, bir az səbr edin."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"İnteraktiv hesabat"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Qısayol aktiv olduqda hər iki səs düyməsinə 3 saniyə basıb saxlamaqla əlçatımlılıq funksiyası işə başlayacaq.\n\n Cari əlçatımlılıq funksiyası:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Funksiyanı Ayarlar və Əçatımlılıq bölməsində dəyişə bilərsiniz."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Qısayolu Deaktiv edin"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Qısayol İstifadə edin"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Rəng İnversiyası"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Rəng korreksiyası"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Əlçatımlıq Qısayolu <xliff:g id="SERVICE_NAME">%1$s</xliff:g> xidmətini aktiv etdi"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Əlçatımlıq Qısayolu <xliff:g id="SERVICE_NAME">%1$s</xliff:g> xidmətini deaktiv etdi"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Əlçatımlılıq düyməsinə kliklədikdə istifadə etmək üçün funksiya seçin:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Həftəiçi gecəsi"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Həftə sonu"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Tədbir"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Yuxu"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> tərəfindən susdurulub"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Cihazınızın daxili problemi var və istehsalçı sıfırlanması olmayana qədər qeyri-stabil ola bilər."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Cihazınızın daxili problemi var. Əlavə məlumat üçün istehsalçı ilə əlaqə saxlayın."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD sorğusu DIAL sorğusuna dəyişdirildi."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD sorğusu SS sorğusuna dəyişdirildi."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD sorğusu yeni USSD sorğusuna dəyişdirildi."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD sorğusu Video DIAL sorğusuna dəyişdirildi."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS sorğusu DIAL sorğusuna dəyişdirildi."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS sorğusu Video DIAL sorğusuna dəyişdirildi."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS sorğusu USSD sorğusuna dəyişdirildi."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS sorğusu yeni SS sorğusuna dəyişdirildi."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"İş profili"</string>
diff --git a/core/res/res/values-b+sr+Latn/required_apps_managed_device.xml b/core/res/res/values-b+sr+Latn/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-b+sr+Latn/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-b+sr+Latn/required_apps_managed_profile.xml b/core/res/res/values-b+sr+Latn/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-b+sr+Latn/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-b+sr+Latn/required_apps_managed_user.xml b/core/res/res/values-b+sr+Latn/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-b+sr+Latn/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 055bccb..6725865 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -214,6 +214,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Isključi"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Hitni poziv"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Izveštaj o grešci"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Završi sesiju"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Napravi izveštaj o grešci"</string>
<string name="bugreport_message" msgid="398447048750350456">"Ovim će se prikupiti informacije o trenutnom stanju uređaja kako bi bile poslate u poruci e-pošte. Od započinjanja izveštaja o grešci do trenutka za njegovo slanje proći će neko vreme; budite strpljivi."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktiv. izveštaj"</string>
@@ -1522,6 +1523,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Kada je prečica uključena, pritisnite oba dugmeta za jačinu zvuka da biste pokrenuli funkciju pristupačnosti.\n\n Aktuelna funkcija pristupačnosti:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Možete da promenite funkciju u odeljku Podešavanja > Pristupačnost."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Isključi prečicu"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Koristi prečicu"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inverzija boja"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Korekcija boja"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Prečica za pristupačnost je uključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Prečica za pristupačnost je isključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Izaberite funkciju koja će se koristiti kada dodirnete dugme za pristupačnost:"</string>
@@ -1717,13 +1720,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Radni dan uveče"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Vikend"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Događaj"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Spavanje"</string>
<string name="muted_by" msgid="6147073845094180001">"Zvuk je isključio/la <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Došlo je do internog problema u vezi sa uređajem i možda će biti nestabilan dok ne obavite resetovanje na fabrička podešavanja."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Došlo je do internog problema u vezi sa uređajem. Potražite detalje od proizvođača."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD zahtev je promenjen u DIAL zahtev."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD zahtev je promenjen u SS zahtev."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD zahtev je promenjen u novi USSD zahtev."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD zahtev je promenjen u Video DIAL zahtev."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS zahtev je promenjen u DIAL zahtev."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS zahtev je promenjen u Video DIAL zahtev."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS zahtev je promenjen u USSD zahtev."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS zahtev je promenjen u novi SS zahtev."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profil za Work"</string>
diff --git a/core/res/res/values-be/required_apps_managed_device.xml b/core/res/res/values-be/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-be/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-be/required_apps_managed_profile.xml b/core/res/res/values-be/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-be/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-be/required_apps_managed_user.xml b/core/res/res/values-be/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-be/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 9f07c7e..ca75fac 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -216,6 +216,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Выключыць"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"SOS-выклік"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Справаздача пра памылкі"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Скончыць сеанс"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Справаздача пра памылку"</string>
<string name="bugreport_message" msgid="398447048750350456">"Будзе збiрацца iнфармацыя пра бягучы стан прылады, якая будзе адпраўляцца на электронную пошту. Стварэнне справаздачы пра памылкi зойме некаторы час."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Інтэрактыўная справаздача"</string>
@@ -1546,6 +1547,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Калі камбінацыя хуткага доступу ўключана, вы можаце націснуць абедзве кнопкі гучнасці і ўтрымліваць іх 3 секунды, каб уключыць функцыю спецыяльных магчымасцей.\n\n Бягучая функцыя спецыяльных магчымасцей:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Вы можаце змяніць гэту функцыю ў меню \"Налады > Спецыяльныя магчымасці\"."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Дэактываваць камбінацыю хуткага доступу"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Выкарыстоўваць камбінацыю хуткага доступу"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Інверсія колеру"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Карэкцыя колеру"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> быў уключаны з дапамогай камбінацыі хуткага доступу для спецыяльных магчымасцей"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> быў адключаны з дапамогай камбінацыі хуткага доступу для спецыяльных магчымасцей"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Выберыце функцыю для выкарыстання пры націску кнопкі \"Спецыяльныя магчымасці\":"</string>
@@ -1750,13 +1753,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Будні вечар"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Выхадныя"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Падзея"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Рэжым сну"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> адключыў(-ла) гук"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"На вашай прыладзе ўзнікла ўнутраная праблема, і яна можа працаваць нестабільна, пакуль вы не зробіце скід да заводскіх налад."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"На вашай прыладзе ўзнікла ўнутраная праблема. Для атрымання дадатковай інфармацыі звярніцеся да вытворцы."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Запыт USSD зменены на запыт DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Запыт USSD зменены на запыт SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Запыт USSD зменены на новы запыт USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Запыт USSD зменены на запыт Video DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Запыт SS зменены на запыт DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Запыт SS зменены на запыт Video DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Запыт SS зменены на запыт USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Запыт SS зменены на новы запыт SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Працоўны профіль"</string>
diff --git a/core/res/res/values-bg/required_apps_managed_device.xml b/core/res/res/values-bg/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-bg/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-bg/required_apps_managed_profile.xml b/core/res/res/values-bg/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-bg/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-bg/required_apps_managed_user.xml b/core/res/res/values-bg/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-bg/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index ba6459b..44f183f 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Изключване"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Спешно обаждане"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Сигнал за програмна грешка"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Прекратяване на сесията"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Сигнал за програмна грешка"</string>
<string name="bugreport_message" msgid="398447048750350456">"По този начин ще се събере информация за текущото състояние на устройството ви, която да се изпрати като имейл съобщение. След стартирането на процеса ще мине известно време, докато сигналът за програмна грешка бъде готов за подаване. Моля, имайте търпение."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Интерактивен сигнал"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Когато прекият път е включен, можете да стартирате дадена функция за достъпност, като натиснете двата бутона за промяна на силата на звука и ги задържите 3 секунди.\n\n Текущата функция за достъпност е:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Можете да промените функцията от „Настройки“ > „Достъпност“."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Изключване на прекия път"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Използване на пряк път"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Инвертиране на цветовете"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Коригиране на цветовете"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Прекият път за достъпност включи <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Прекият път за достъпност изключи <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Изберете функция, която да използвате, когато докоснете бутона за достъпност:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Делнична нощ"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Събота и неделя"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Събитие"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Време за сън"</string>
<string name="muted_by" msgid="6147073845094180001">"Заглушено от <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Възникна вътрешен проблем с устройството ви. То може да е нестабилно, докато не възстановите фабричните настройки."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Възникна вътрешен проблем с устройството ви. За подробности се свържете с производителя."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD заявката е променена на DIAL заявка."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD заявката е променена на SS заявка."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD заявката е променена на нова USSD заявка."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD заявката е променена на DIAL заявка за видеообаждане."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS заявката е променена на DIAL заявка."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS заявката е променена на DIAL заявка за видеообаждане."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS заявката е променена на USSD заявка."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS заявката е променена на нова SS заявка."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Служебен потребителски профил"</string>
diff --git a/core/res/res/values-bn/required_apps_managed_device.xml b/core/res/res/values-bn/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-bn/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-bn/required_apps_managed_profile.xml b/core/res/res/values-bn/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-bn/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-bn/required_apps_managed_user.xml b/core/res/res/values-bn/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-bn/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 440b2690..fa3c9b1d 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -212,8 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"পাওয়ার বন্ধ করুন"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"জরুরী"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"ত্রুটির প্রতিবেদন"</string>
- <!-- no translation found for global_action_logout (935179188218826050) -->
- <skip />
+ <string name="global_action_logout" msgid="935179188218826050">"সেশন শেষ করুন"</string>
<string name="bugreport_title" msgid="2667494803742548533">"ত্রুটির অভিযোগ করুন"</string>
<string name="bugreport_message" msgid="398447048750350456">"এটি একটি ই-মেল মেসেজ পাঠানোর জন্য আপনার ডিভাইসের বর্তমান অবস্থা সম্পর্কে তথ্য সংগ্রহ করবে৷ ত্রুটির প্রতিবেদন শুরুর সময় থেকে এটি পাঠানোর জন্য প্রস্তুত হতে কিছুটা সময় নেবে; অনুগ্রহ করে ধৈর্য রাখুন৷"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ইন্টারেক্টিভ প্রতিবেদন"</string>
@@ -1501,10 +1500,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"শর্টকাটটি চালু থাকলে দুটি ভলিউম বোতাম একসাথে ৩ সেকেন্ড টিপে ধরে রাখলে একটি অ্যাকসেসিবিলিটি বৈশিষ্ট্য চালু হবে।\n\n বর্তমান অ্যাকসেসিবিলিটি বৈশিষ্ট্য:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n আপনি এই বৈশিষ্ট্যটি সেটিংস > অ্যাকসেসিবিলিটিতে গিয়ে পরিবর্তন করতে পারবেন।"</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"শর্টকাট বন্ধ করুন"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"শর্টকাট ব্যবহার করুন"</string>
- <!-- no translation found for color_inversion_feature_name (4231186527799958644) -->
- <skip />
- <!-- no translation found for color_correction_feature_name (6779391426096954933) -->
- <skip />
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"রঙ উল্টানো"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"রঙ সংশোধন"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"অ্যাক্সেসযোগ্যতা শর্টকাট <xliff:g id="SERVICE_NAME">%1$s</xliff:g> কে চালু করেছে"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"অ্যাক্সেসযোগ্যতা শর্টকাট <xliff:g id="SERVICE_NAME">%1$s</xliff:g> কে বন্ধ করেছে"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"অ্যাক্সেসযোগ্যতা বোতামের সাহায্যে যে বৈশিষ্ট্যটি নিয়ন্ত্রণ করতে চান, সেটি বেছে নিন:"</string>
@@ -1691,15 +1688,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"সপ্তাহান্তের রাত্রি"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"সপ্তাহান্ত"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"ইভেন্ট"</string>
- <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) -->
- <skip />
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"ঘুমানোর সময়"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> দ্বারা নিঃশব্দ করা হয়েছে"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"আপনার ডিভাইসে একটি অভ্যন্তরীন সমস্যা হয়েছে, এবং আপনি যতক্ষণ না পর্যন্ত এটিকে ফ্যাক্টরি ডেটা রিসেট করছেন ততক্ষণ এটি ঠিকভাবে কাজ নাও করতে পারে৷"</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"আপনার ডিভাইসে একটি অভ্যন্তরীন সমস্যা হয়েছে৷ বিস্তারিত জানার জন্য প্রস্তুতকারকের সাথে যোগাযোগ করুন৷"</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD অনুরোধটিকে ডায়াল অনুরোধে রুপান্তরিত করা হয়েছে৷"</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD অনুরোধটিকে SS অনুরোধে রুপান্তরিত করা হয়েছে৷"</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD অনুরোধটিকে নতুন USSD অনুরোধে রুপান্তরিত করা হয়েছে৷"</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD অনুরোধটিকে ভিডিও DIAL অনুরোধে পরিবর্তন করা হয়েছে।"</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS অনুরোধটিকে ডায়াল অনুরোধে রুপান্তরিত করা হয়েছে৷"</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS অনুরোধটিকে ভিডিও DIAL অনুরোধে পরিবর্তন করা হয়েছে।"</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS অনুরোধটিকে নতুন USSD অনুরোধে রুপান্তরিত করা হয়েছে৷"</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS অনুরোধটিকে নতুন SS অনুরোধে রুপান্তরিত করা হয়েছে৷"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"কর্মস্থলের প্রোফাইল"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index c9c98eb..c93a0e9 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -214,8 +214,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Isključi telefon"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Hitno"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Izvještaj o greškama"</string>
- <!-- no translation found for global_action_logout (935179188218826050) -->
- <skip />
+ <string name="global_action_logout" msgid="935179188218826050">"Završi sesiju"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Kreirajte izvještaj o greškama"</string>
<string name="bugreport_message" msgid="398447048750350456">"Ovim će se prikupljati informacije o trenutnom stanju uređaja, koji će biti poslani kao e-poruka. Može malo potrajati dok se izvještaj o greškama ne kreira i bude spreman za slanje. Budite strpljivi."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktivni izvještaj"</string>
@@ -1526,10 +1525,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Kada je prečica uključena, pritiskom na oba dugmeta za podešavanje jačine zvuka u trajanju od 3 sekunde pokrenut će se funkcija za pristupačnost.\n\n Trenutna funkcija za pristupačnost je:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Funkciju možete promijeniti ako odete u Postavke > Pristupačnost."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Isključi prečicu"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Koristi prečicu"</string>
- <!-- no translation found for color_inversion_feature_name (4231186527799958644) -->
- <skip />
- <!-- no translation found for color_correction_feature_name (6779391426096954933) -->
- <skip />
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inverzija boja"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Ispravka boja"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Prečica za pristupačnost je uključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Prečica za pristupačnost je isključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Odaberite funkciju koja će se koristiti kada dodirnete dugme Pristupačnost:"</string>
@@ -1725,15 +1722,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Radni dan uvečer"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Vikend"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Događaj"</string>
- <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) -->
- <skip />
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Spavanje"</string>
<string name="muted_by" msgid="6147073845094180001">"Ton isključila aplikacija <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Postoji problem u vašem uređaju i može biti nestabilan dok ga ne vratite na fabričke postavke."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Postoji problem u vašem uređaju. Za više informacija obratite se proizvođaču."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD zahtjev je izmijenjen u DIAL zahtjev."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD zahtjev je izmijenjen u SS zahtjev."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD zahtjev je izmijenjen u novi USSD zahtjev."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD zahtjev je izmijenjen u Video DIAL zahtjev."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS zahtjev je izmijenjen u DIAL zahtjev."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS zahtjev je izmijenjen u Video DIAL zahtjev."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS zahtjev je izmijenjen u USSD zahtjev."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS zahtjev je izmijenjen u novi SS zahtjev."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profil za posao"</string>
diff --git a/core/res/res/values-ca/required_apps_managed_device.xml b/core/res/res/values-ca/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-ca/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ca/required_apps_managed_profile.xml b/core/res/res/values-ca/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-ca/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ca/required_apps_managed_user.xml b/core/res/res/values-ca/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-ca/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 1799069..64d0ccd 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Apaga"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Emergències"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Informe d\'error"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Finalitza la sessió"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Crea informe d\'errors"</string>
<string name="bugreport_message" msgid="398447048750350456">"Es recopilarà informació sobre l\'estat actual del dispositiu i se t\'enviarà per correu electrònic. Passaran uns quants minuts des de l\'inici de l\'informe d\'errors fins al seu enviament, per la qual cosa et recomanem que tinguis paciència."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Informe interactiu"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Si la drecera està activada, prem els dos botons de volum durant 3 segons, per iniciar una funció d\'accessibilitat.\n\n Funció d\'accessibilitat actual:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Pots canviar la funció a Configuració > Accessibilitat."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Desactiva la drecera"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Utilitza la drecera"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inversió de color"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Correcció del color"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"La drecera d\'accessibilitat ha activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"La drecera d\'accessibilitat ha desactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Tria la funció que s\'utilitzarà quan toquis el botó Accessibilitat:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Nit entre setmana"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Cap de setmana"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Esdeveniment"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Mentre dormo"</string>
<string name="muted_by" msgid="6147073845094180001">"Silenciat per <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"S\'ha produït un error intern al dispositiu i és possible que funcioni de manera inestable fins que restableixis les dades de fàbrica."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"S\'ha produït un error intern al dispositiu. Contacta amb el fabricant del dispositiu per obtenir més informació."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"La sol·licitud USSD s\'ha transformat en una sol·licitud DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"La sol·licitud USSD s\'ha transformat en una sol·licitud SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"La sol·licitud USSD s\'ha transformat en una sol·licitud USSD nova."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"La sol·licitud USSD s\'ha transformat en una sol·licitud DIAL de vídeo."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"La sol·licitud SS s\'ha transformat en una sol·licitud DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"La sol·licitud SS s\'ha transformat en una sol·licitud DIAL de vídeo."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"La sol·licitud SS s\'ha transformat en una sol·licitud USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"La sol·licitud SS s\'ha transformat en una sol·licitud SS nova."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Perfil professional"</string>
diff --git a/core/res/res/values-cs/required_apps_managed_device.xml b/core/res/res/values-cs/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-cs/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-cs/required_apps_managed_profile.xml b/core/res/res/values-cs/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-cs/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-cs/required_apps_managed_user.xml b/core/res/res/values-cs/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-cs/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index ee47b43..0f24965 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -216,6 +216,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Vypnout"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Stav nouze"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Hlášení chyb"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Ukončit relaci"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Vytvořit chybové hlášení"</string>
<string name="bugreport_message" msgid="398447048750350456">"Shromažďuje informace o aktuálním stavu zařízení. Tyto informace je následně možné poslat v e-mailové zprávě, chvíli však potrvá, než bude hlášení o chybě připraveno k odeslání. Buďte prosím trpěliví."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktivní přehled"</string>
@@ -1546,6 +1547,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Když je tato zkratka zapnutá, můžete funkci přístupnosti spustit tím, že na tři sekundy podržíte obě tlačítka hlasitosti.\n\n Aktuální funkce přístupnosti:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Funkci můžete změnit v Nastavení > Přístupnost."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Vypnout zkratku"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Použít zkratku"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Převrácení barev"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Oprava barev"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Zkratka přístupnosti zapnula službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Zkratka přístupnosti vypnula službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Určete, jakou funkci aktivujete klepnutím na tlačítko Přístupnost:"</string>
@@ -1750,13 +1753,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Večer v pracovním týdnu"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Víkend"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Událost"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Spánek"</string>
<string name="muted_by" msgid="6147073845094180001">"Ignorováno stranou <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"V zařízení došlo k internímu problému. Dokud neprovedete obnovení továrních dat, může být nestabilní."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"V zařízení došlo k internímu problému. Další informace vám sdělí výrobce."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Požadavek USSD byl změněn na požadavek DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Požadavek USSD byl změněn na požadavek SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Požadavek USSD byl změněn na nový požadavek USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Požadavek USSD byl změněn na požadavek Video DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Požadavek SS byl změněn na požadavek DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Požadavek SS byl změněn na požadavek Video DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Požadavek SS byl změněn na požadavek USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Požadavek SS byl změněn na nový požadavek SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Pracovní profil"</string>
diff --git a/core/res/res/values-da/required_apps_managed_device.xml b/core/res/res/values-da/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-da/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-da/required_apps_managed_profile.xml b/core/res/res/values-da/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-da/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-da/required_apps_managed_user.xml b/core/res/res/values-da/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-da/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 0a3892f..0581742 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Sluk"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Nødopkald"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Fejlrapport"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Afslut sessionen"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Lav fejlrapport"</string>
<string name="bugreport_message" msgid="398447048750350456">"Der indsamles oplysninger om din enheds aktuelle status, der efterfølgende sendes i en e-mail. Der går lidt tid, fra fejlrapporten påbegyndes, til den er klar til at blive sendt. Tak for tålmodigheden."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktiv rapport"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Når genvejen er slået til, kan du starte en hjælpefunktion ved at trykke på begge lydstyrkeknapper i tre sekunder.\n\n Nuværende hjælpefunktion:\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Du kan skifte funktion i Indstillinger > Hjælpefunktioner."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Deaktiver genvej"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Brug genvej"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Ombytning af farver"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Korriger farve"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Genvejen til hjælpefunktioner aktiverede <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Genvejen til hjælpefunktioner deaktiverede <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Vælg, hvilken funktion du vil bruge, når du trykker på knappen Hjælpefunktioner:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Hverdagsaften"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Begivenhed"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Sover"</string>
<string name="muted_by" msgid="6147073845094180001">"Lyden blev afbrudt af <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Der er et internt problem med enheden, og den vil muligvis være ustabil, indtil du gendanner fabriksdataene."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Der er et internt problem med enheden. Kontakt producenten for at få yderligere oplysninger."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD-anmodningen er ændret til en DIAL-anmodning."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD-anmodningen er ændret til en SS-anmodning."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD-anmodningen er ændret til en ny USSD-anmodning."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD-anmodningen er ændret til en Video DIAL-anmodning."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-anmodningen er ændret til en DIAL-anmodning."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS-anmodningen er ændret til en Vidieo DIAL-anmodning."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-anmodningen er ændret til en USSD-anmodning."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-anmodningen er ændret til en ny SS-anmodning."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Arbejdsprofil"</string>
diff --git a/core/res/res/values-de/required_apps_managed_device.xml b/core/res/res/values-de/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-de/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-de/required_apps_managed_profile.xml b/core/res/res/values-de/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-de/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-de/required_apps_managed_user.xml b/core/res/res/values-de/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-de/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index c85c268..f694064 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Ausschalten"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Notfall"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Fehlerbericht"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Sitzung beenden"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Fehlerbericht abrufen"</string>
<string name="bugreport_message" msgid="398447048750350456">"Bei diesem Fehlerbericht werden Daten zum aktuellen Status deines Geräts erfasst und als E-Mail versandt. Vom Start des Berichts bis zu seinem Versand kann es eine Weile dauern. Bitte habe etwas Geduld."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktiver Bericht"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Wenn die Verknüpfung aktiviert ist, kannst du die beiden Lautstärketasten drei Sekunden lang gedrückt halten, um eine Bedienungshilfe zu starten.\n\n Aktuelle Bedienungshilfe:\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Du kannst die Bedienungshilfe unter \"Einstellungen\" > \"Bedienungshilfen\" ändern."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Verknüpfung deaktivieren"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Verknüpfung verwenden"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Farbumkehr"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Farbkorrektur"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> wurde durch die Bedienungshilfenverknüpfung aktiviert"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> wurde durch die Bedienungshilfenverknüpfung deaktiviert"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Wähle eine Funktion aus, die verwendet wird, wenn du auf die Schaltfläche für die Bedienungshilfen tippst:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Abends unter der Woche"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Wochenende"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Termin"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Beim Schlafen"</string>
<string name="muted_by" msgid="6147073845094180001">"Stummgeschaltet durch <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Es liegt ein internes Problem mit deinem Gerät vor. Möglicherweise verhält es sich instabil, bis du es auf die Werkseinstellungen zurücksetzt."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Es liegt ein internes Problem mit deinem Gerät vor. Bitte wende dich diesbezüglich an den Hersteller."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD-Anfrage wird in DIAL-Anfrage geändert."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD-Anfrage wird in SS-Anfrage geändert."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD-Anfrage wird in neue USSD-Anfrage geändert."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD-Anfrage wird in Video-DIAL-Anfrage geändert."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-Anfrage wird in DIAL-Anfrage geändert."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS-Anfrage wird in Video-DIAL-Anfrage geändert."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-Anfrage wird in USSD-Anfrage geändert."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-Anfrage wird in neue SS-Anfrage geändert."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Arbeitsprofil"</string>
diff --git a/core/res/res/values-el/required_apps_managed_device.xml b/core/res/res/values-el/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-el/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-el/required_apps_managed_profile.xml b/core/res/res/values-el/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-el/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-el/required_apps_managed_user.xml b/core/res/res/values-el/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-el/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 1fcaf97..c20a03f 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Απενεργοποίηση"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Κλήση έκτακτης ανάγκης"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Αναφορά σφαλμάτων"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Λήξη περιόδου σύνδεσης"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Λήψη αναφοράς σφάλματος"</string>
<string name="bugreport_message" msgid="398447048750350456">"Θα συλλέξει πληροφορίες σχετικά με την τρέχουσα κατάσταση της συσκευής σας και θα τις στείλει μέσω μηνύματος ηλεκτρονικού ταχυδρομείου. Απαιτείται λίγος χρόνος για τη σύνταξη της αναφοράς σφάλματος και την αποστολή της. Κάντε λίγη υπομονή."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Διαδραστική αναφορά"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Όταν η συντόμευση είναι ενεργοποιημένη, το πάτημα και των δύο κουμπιών έντασης ήχου για 3 δευτερόλεπτα θα ξεκινήσει μια λειτουργία προσβασιμότητας.\n\n Τρέχουσα λειτουργία προσβασιμότητας:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Μπορείτε να αλλάξετε τη λειτουργία από τις Ρυθμίσεις > Προσβασιμότητα."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Απενεργοποίηση συντόμευσης"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Χρήση συντόμευσης"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Αντιστροφή χρωμάτων"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Διόρθωση χρωμάτων"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Η συντόμευση προσβασιμότητας ενεργοποίησε την υπηρεσία <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Η συντόμευση προσβασιμότητας απενεργοποίησε την υπηρεσία <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Επιλέξτε μια λειτουργία που θα χρησιμοποιείται κατά το πάτημα του κουμπιού \"Προσβασιμότητα\"."</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Νύχτα καθημερινής"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Σαββατοκύριακο"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Συμβάν"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Ύπνος"</string>
<string name="muted_by" msgid="6147073845094180001">"Σίγαση από <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Υπάρχει ένα εσωτερικό πρόβλημα με τη συσκευή σας και ενδέχεται να είναι ασταθής μέχρι την επαναφορά των εργοστασιακών ρυθμίσεων."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Υπάρχει ένα εσωτερικό πρόβλημα με τη συσκευή σας. Επικοινωνήστε με τον κατασκευαστή σας για λεπτομέρειες."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Το αίτημα USSD τροποποιήθηκε σε αίτημα DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Το αίτημα USSD τροποποιήθηκε σε αίτημα SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Το αίτημα USSD τροποποιήθηκε σε νέο αίτημα USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Το αίτημα USSD τροποποιήθηκε σε αίτημα Video DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Το αίτημα SS τροποποιήθηκε σε αίτημα DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Το αίτημα SS τροποποιήθηκε σε αίτημα Video DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Το αίτημα SS τροποποιήθηκε σε αίτημα USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Το αίτημα SS τροποποιήθηκε σε νέο αίτημα SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Προφίλ εργασίας"</string>
diff --git a/core/res/res/values-es-rUS/required_apps_managed_device.xml b/core/res/res/values-es-rUS/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-es-rUS/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-es-rUS/required_apps_managed_profile.xml b/core/res/res/values-es-rUS/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-es-rUS/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-es-rUS/required_apps_managed_user.xml b/core/res/res/values-es-rUS/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-es-rUS/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 22fe10d..58268eb 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Apagar"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Emergencias"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Informe de errores"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Finalizar sesión"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Iniciar informe de errores"</string>
<string name="bugreport_message" msgid="398447048750350456">"Se recopilará información sobre el estado actual de tu dispositivo, que se enviará por correo. Pasarán unos minutos desde que se inicie el informe de errores hasta que se envíe, por lo que te recomendamos que tengas paciencia."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Informe interactivo"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Cuando el acceso directo está activado, puedes presionar los botones de volumen durante 3 segundos para iniciar una función de accesibilidad.\n\n Función de accesibilidad actual:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Puedes cambiar la función en Configuración > Accesibilidad."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Desactivar acceso directo"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Usar acceso directo"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inversión de color"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Corrección de color"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"El acceso directo de accesibilidad activó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"El acceso directo de accesibilidad desactivó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Elige una función para usar cuando presionas el botón Accesibilidad:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Noche, en la semana"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Fin de semana"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Evento"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Dormir"</string>
<string name="muted_by" msgid="6147073845094180001">"Silenciados por <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Existe un problema interno con el dispositivo, de modo que el dispositivo puede estar inestable hasta que restablezcas la configuración de fábrica."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Existe un problema interno con el dispositivo. Comunícate con el fabricante para obtener más información."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"La solicitud USSD cambió por una solicitud DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"La solicitud USSD cambió por una solicitud SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"La solicitud USSD cambió por una nueva solicitud USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Se cambió la solicitud USSD por una solicitud DIAL de video."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"La solicitud SS cambió por una solicitud DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Se cambió la solicitud SS por una solicitud DIAL de video."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"La solicitud SS cambió por una solicitud USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"La solicitud SS cambió por una nueva solicitud SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Perfil de trabajo"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index d9e3920..d03498c 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Apagar"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Emergencia"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Informe de error"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Finalizar sesión"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Crear informe de errores"</string>
<string name="bugreport_message" msgid="398447048750350456">"Se recopilará información sobre el estado actual de tu dispositivo y se enviará por correo electrónico. Pasarán unos minutos desde que empiece a generarse el informe de errores hasta que se envíe."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Informe interactivo"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Si el acceso directo está activado, pulsa los dos botones de volumen durante tres segundos para iniciar una función de accesibilidad.\n\n Función de accesibilidad actual:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Puedes cambiar la función en Ajustes > Accesibilidad."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Desactivar acceso directo"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Utilizar acceso directo"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inversión de color"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Corrección de color"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"El acceso directo a accesibilidad ha activado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"El acceso directo a accesibilidad ha desactivado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Selecciona la función que se utilizará cuando toques el botón Accesibilidad:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Noche de entre semana"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Fin de semana"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Evento"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Dormir"</string>
<string name="muted_by" msgid="6147073845094180001">"Silenciado por <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Se ha producido un problema interno en el dispositivo y es posible que este no sea estable hasta que restablezcas el estado de fábrica."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Se ha producido un problema interno en el dispositivo. Ponte en contacto con el fabricante para obtener más información."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"La solicitud USSD se ha modificado para la solicitud DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"La solicitud USSD se ha modificado para la solicitud SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"La solicitud USSD se ha modificado para la nueva solicitud USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"La solicitud USSD se ha modificado para la solicitud DIAL de vídeo."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"La solicitud SS se ha modificado para la solicitud DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"La solicitud SS se ha modificado para la solicitud DIAL de vídeo."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"La solicitud SS se ha modificado para la solicitud USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"La solicitud SS se ha modificado para la nueva solicitud SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Perfil de trabajo"</string>
diff --git a/core/res/res/values-et/required_apps_managed_device.xml b/core/res/res/values-et/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-et/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-et/required_apps_managed_profile.xml b/core/res/res/values-et/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-et/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-et/required_apps_managed_user.xml b/core/res/res/values-et/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-et/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 7de0c3b..754cdb3 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Lülita välja"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Hädaabi"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Veaaruanne"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Seansi lõpp"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Veaaruande võtmine"</string>
<string name="bugreport_message" msgid="398447048750350456">"Nii kogutakse teavet teie seadme praeguse oleku kohta, et saata see meilisõnumina. Enne kui saate veaaruande ära saata, võtab selle loomine natuke aega; varuge kannatust."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interakt. aruanne"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Kui otsetee on sisse lülitatud, käivitab mõlema helitugevuse nupu kolm sekundit all hoidmine juurdepääsetavuse funktsiooni.\n\n Praegune juurdepääsetavuse funktsioon:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Saate seda funktsiooni muuta valikutega Seaded > Juurdepääsetavus."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Lülita otsetee välja"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Kasuta otseteed"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Värvide ümberpööramine"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Värviparandus"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Juurdepääsetavuse otsetee lülitas teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> sisse"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Juurdepääsetavuse otsetee lülitas teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> välja"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Valige, millist funktsiooni kasutada, kui vajutate nuppu Juurdepääsetavus:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Argiõhtu"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Nädalavahetus"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Sündmus"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Magamine"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> vaigistas"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Seadmes ilmnes sisemine probleem ja seade võib olla ebastabiilne seni, kuni lähtestate seadme tehase andmetele."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Seadmes ilmnes sisemine probleem. Üksikasjaliku teabe saamiseks võtke ühendust tootjaga."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD-päring muudeti DIAL-päringuks."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD-päring muudeti SS-päringuks."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD-päring muudeti uueks USSD-päringuks."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD-taotlus muudeti video DIAL-taotluseks."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-päring muudeti DIAL-päringuks."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS-taotlus muudeti video DIAL-taotluseks."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-päring muudeti USSD-päringuks."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-päring muudeti uueks SS-päringuks."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Tööprofiil"</string>
diff --git a/core/res/res/values-eu/required_apps_managed_device.xml b/core/res/res/values-eu/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-eu/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-eu/required_apps_managed_profile.xml b/core/res/res/values-eu/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-eu/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-eu/required_apps_managed_user.xml b/core/res/res/values-eu/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-eu/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index e62a7f6..2e10fa2 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Itzali"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Larrialdi-deiak"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Akatsen txostena"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Amaitu saioa"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Sortu akatsen txostena"</string>
<string name="bugreport_message" msgid="398447048750350456">"Gailuaren uneko egoerari buruzko informazioa bilduko da, mezu elektroniko gisa bidaltzeko. Minutu batzuk igaroko dira akatsen txostena sortzen hasten denetik bidaltzeko prest egon arte. Itxaron, mesedez."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Txosten dinamikoa"</string>
@@ -1499,6 +1500,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Lasterbidea aktibatuta dagoenean, bi bolumen-botoiak hiru segundoz sakatuta abiaraziko da erabilerraztasun-eginbidea.\n\n Uneko erabilerraztasun-eginbidea:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Eginbidea aldatzeko, joan Ezarpenak > Erabilerraztasuna atalera."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Desaktibatu lasterbidea"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Erabili lasterbidea"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Koloreak alderantzikatzeko aukera"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Kolorearen zuzenketa"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Erabilerraztasun-lasterbideak <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktibatu du"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Erabilerraztasun-lasterbideak <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desaktibatu du"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Aukeratu zein eginbide erabili nahi duzun Erabilerraztasuna botoia sakatzean:"</string>
@@ -1685,13 +1688,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Lanegunetako gaua"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Asteburua"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Gertaera"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Lo egin bitartean"</string>
<string name="muted_by" msgid="6147073845094180001">"Audioa desaktibatu da (<xliff:g id="THIRD_PARTY">%1$s</xliff:g>)"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Barneko arazo bat dago zure gailuan eta agian ezegonkor egongo da jatorrizko datuak berrezartzen dituzun arte."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Barneko arazo bat dago zure gailuan. Xehetasunak jakiteko, jarri fabrikatzailearekin harremanetan."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD eskaera DIAL eskaerara aldatu da."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD eskaera SS eskaerara aldatu da."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD eskaera USSD eskaera berrira aldatu da."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD eskaera Video DIAL eskaerara aldatu da."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS eskaera DIAL eskaerara aldatu da."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS eskaera Video DIAL eskaerara aldatu da."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS eskaera USSD eskaerara aldatu da."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS eskaera SS eskaera berrira aldatu da."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Work profila"</string>
diff --git a/core/res/res/values-fa/required_apps_managed_device.xml b/core/res/res/values-fa/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-fa/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-fa/required_apps_managed_profile.xml b/core/res/res/values-fa/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-fa/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-fa/required_apps_managed_user.xml b/core/res/res/values-fa/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-fa/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index e1b4e0d..dfa350b 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -212,8 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"خاموش کردن"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"اضطراری"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"گزارش اشکال"</string>
- <!-- no translation found for global_action_logout (935179188218826050) -->
- <skip />
+ <string name="global_action_logout" msgid="935179188218826050">"پایان جلسه"</string>
<string name="bugreport_title" msgid="2667494803742548533">"گرفتن گزارش اشکال"</string>
<string name="bugreport_message" msgid="398447048750350456">"این گزارش اطلاعات مربوط به وضعیت دستگاه کنونی شما را جمعآوری میکند تا به صورت یک پیام رایانامه ارسال شود. از زمان شروع گزارش اشکال تا آماده شدن برای ارسال اندکی زمان میبرد؛ لطفاً شکیبا باشید."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"گزارش تعاملی"</string>
@@ -1500,10 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"وقتی میانبر روشن است، اگر هر دو دکمه صدا را ۳ ثانیه فشار دهید یکی از قابلیتهای دسترسپذیری شروع میشود.\n\n قابلیت دسترسپذیری کنونی:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n میتوانید در «تنظیمات > دسترسپذیری»، قابلیت را تغییر دهید."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"خاموش کردن میانبر"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"استفاده از میانبر"</string>
- <!-- no translation found for color_inversion_feature_name (4231186527799958644) -->
- <skip />
- <!-- no translation found for color_correction_feature_name (6779391426096954933) -->
- <skip />
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"وارونگی رنگ"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"تصحیح رنگ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"«میانبر دسترسپذیری» <xliff:g id="SERVICE_NAME">%1$s</xliff:g> را روشن کرد"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"«میانبر دسترسپذیری» <xliff:g id="SERVICE_NAME">%1$s</xliff:g> را خاموش کرد"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"قابلیتی را انتخاب کنید که هنگام ضربه زدن روی دکمه «دسترسپذیری» استفاده میشود:"</string>
@@ -1690,15 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"شب آخر هفته"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"آخر هفته"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"رویداد"</string>
- <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) -->
- <skip />
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"خوابیدن"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> آن را بیصدا کرد"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"دستگاهتان یک مشکل داخلی دارد، و ممکن است تا زمانی که بازنشانی دادههای کارخانه انجام نگیرد، بیثبات بماند."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"دستگاهتان یک مشکل داخلی دارد. برای جزئیات آن با سازندهتان تماس بگیرید."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"درخواست USSD به درخواست DIAL اصلاح میشود."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"درخواست USSD به درخواست SS اصلاح میشود."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"درخواست USSD به درخواست USSD جدید اصلاح میشود."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"درخواست USSD به درخواست Video DIAL تغییر کرد."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"درخواست SS به درخواست DIAL اصلاح میشود."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"درخواست SS به درخواست Video DIAL تغییر کرد."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"درخواست SS به درخواست USSD اصلاح میشود."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"درخواست SS به درخواست SS جدید اصلاح میشود."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"نمایه کاری"</string>
diff --git a/core/res/res/values-fi/required_apps_managed_device.xml b/core/res/res/values-fi/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-fi/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-fi/required_apps_managed_profile.xml b/core/res/res/values-fi/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-fi/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-fi/required_apps_managed_user.xml b/core/res/res/values-fi/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-fi/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 68b29df..5696b5a 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Katkaise virta"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Hätäpuhelu"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Virheraportti"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Lopeta käyttökerta"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Luo virheraportti"</string>
<string name="bugreport_message" msgid="398447048750350456">"Toiminto kerää tietoja laitteen tilasta ja lähettää ne sähköpostitse. Virheraportti on valmis lähetettäväksi hetken kuluttua - kiitos kärsivällisyydestäsi."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktiivinen"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Kun pikanäppäin on käytössä, voit käynnistää esteettömyystoiminnon pitämällä molempia äänenvoimakkuuspainikkeita painettuna kolmen sekunnin ajan.\n\n Tällä hetkellä valittu esteettömyystoiminto:\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Voit vaihtaa toimintoa valitsemalla Asetukset > Esteettömyys."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Poista pikanäppäin käytöstä"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Käytä pikanäppäintä"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Käänteiset värit"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Värinkorjaus"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> otettiin käyttöön esteettömyystilan pikanäppäimellä."</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> poistettiin käytöstä esteettömyystilan pikanäppäimellä."</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Valitse toiminto, jonka Esteettömyys-painike aktivoi:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Arki-iltaisin"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Viikonloppuna"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Tapahtuma"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Nukkuminen"</string>
<string name="muted_by" msgid="6147073845094180001">"Mykistänyt <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Laitteellasi on sisäinen ongelma, joka aiheuttaa epävakautta. Voit korjata tilanteen palauttamalla tehdasasetukset."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Laitteesi yhdistäminen ei onnistu sisäisen virheen takia. Saat lisätietoja valmistajalta."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD-pyyntö muutettiin DIAL-pyynnöksi."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD-pyyntö muutettiin SS-pyynnöksi."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD-pyyntö muutettiin uudeksi USSD-pyynnöksi."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD-pyyntö muutettiin Video DIAL ‑pyynnöksi."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-pyyntö muutettiin DIAL-pyynnöksi."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS-pyyntö muutettiin Video DIAL ‑pyynnöksi."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-pyyntö muutettiin USSD-pyynnöksi."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-pyyntö muutettiin uudeksi SS-pyynnöksi."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Työprofiili"</string>
diff --git a/core/res/res/values-fr-rCA/required_apps_managed_device.xml b/core/res/res/values-fr-rCA/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-fr-rCA/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-fr-rCA/required_apps_managed_profile.xml b/core/res/res/values-fr-rCA/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-fr-rCA/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-fr-rCA/required_apps_managed_user.xml b/core/res/res/values-fr-rCA/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-fr-rCA/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index a4c0a0f..6e97844 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Éteindre"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Urgence"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Rapport de bogue"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Fermer la session"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Créer un rapport de bogue"</string>
<string name="bugreport_message" msgid="398447048750350456">"Cela permet de recueillir des informations concernant l\'état actuel de votre appareil. Ces informations sont ensuite envoyées sous forme de courriel. Merci de patienter pendant la préparation du rapport de bogue. Cette opération peut prendre quelques instants."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Rapport interactif"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Quand le raccourci est activé, appuyez sur les deux boutons de volume pendant trois secondes pour lancer une fonctionnalité d\'accessibilité.\n\n Fonctionnalité d\'accessibilité utilisée actuellement :\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Vous pouvez changer de fonctionnalité sous Paramètres > Accessibilité."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Désactiver le raccourci"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Utiliser le raccourci"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inversion des couleurs"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Correction des couleurs"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Le raccourci d\'accessibilité a activé la fonction <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Le raccourci d\'accessibilité a désactivé la fonction <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Choisissez une fonctionnalité à utiliser lorsque vous touchez le bouton d\'accessibilité :"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Soirs de semaine"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Fin de semaine"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Événement"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Dormir"</string>
<string name="muted_by" msgid="6147073845094180001">"Mis en sourdine par <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Un problème interne est survenu avec votre appareil. Il se peut qu\'il soit instable jusqu\'à ce que vous le réinitialisiez à sa configuration d\'usine."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Un problème interne est survenu avec votre appareil. Communiquez avec le fabricant pour obtenir plus de détails."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"La demande USSD a été modifiée et est maintenant une demande DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"La demande USSD a été modifiée et est maintenant une demande SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"La demande USSD a été modifiée et est maintenant une nouvelle demande USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"La demande USSD a été modifiée et est maintenant une demande Video DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"La demande SS a été modifiée et est maintenant une demande DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"La demande SS a été modifiée et est maintenant une demande Video DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"La demande SS a été modifiée et est maintenant une demande USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"La demande SS a été modifiée et est maintenant une nouvelle demande SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profil professionnel"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c57684c..7dc33c4 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Éteindre"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Urgences"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Rapport de bug"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Fermer la session"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Créer un rapport de bug"</string>
<string name="bugreport_message" msgid="398447048750350456">"Cela permet de recueillir des informations concernant l\'état actuel de votre appareil. Ces informations sont ensuite envoyées sous forme d\'e-mail. Merci de patienter pendant la préparation du rapport de bug. Cette opération peut prendre quelques instants."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Rapport interactif"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Quand le raccourci est activé, appuyez sur les deux boutons de volume pendant trois secondes pour lancer une fonctionnalité d\'accessibilité.\n\n Fonctionnalité d\'accessibilité utilisée actuellement :\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Vous pouvez changer de fonctionnalité dans Paramètres > Accessibilité."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Désactiver le raccourci"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Utiliser le raccourci"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inversion des couleurs"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Correction des couleurs"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Le raccourci d\'accessibilité a activé <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Le raccourci d\'accessibilité a désactivé <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Choisissez une fonctionnalité à utiliser lorsque vous appuyez sur le bouton d\'accessibilité :"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Soirée de semaine"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Week-end"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Événement"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Sommeil"</string>
<string name="muted_by" msgid="6147073845094180001">"Son coupé par : <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Un problème interne lié à votre appareil est survenu. Ce dernier risque d\'être instable jusqu\'à ce que vous rétablissiez la configuration d\'usine."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Un problème interne lié à votre appareil est survenu. Veuillez contacter le fabricant pour en savoir plus."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"La requête USSD a été remplacée par une requête DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"La requête USSD a été remplacée par une requête SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"La requête USSD a été remplacée par une autre requête USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"La requête SS a été remplacée par une requête vidéo DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"La requête SS a été remplacée par une requête DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"La requête SS a été remplacée par une requête vidéo DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"La requête SS a été remplacée par une requête USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"La requête SS a été remplacée par une autre requête SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profil professionnel"</string>
diff --git a/core/res/res/values-gl/required_apps_managed_device.xml b/core/res/res/values-gl/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-gl/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-gl/required_apps_managed_profile.xml b/core/res/res/values-gl/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-gl/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-gl/required_apps_managed_user.xml b/core/res/res/values-gl/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-gl/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 684b909..b4e3904 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Apagar"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Urxencias"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Informe de erros"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Finalizar a sesión"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Crear informe de erros"</string>
<string name="bugreport_message" msgid="398447048750350456">"Este informe recompilará información acerca do estado actual do teu dispositivo para enviala en forma de mensaxe de correo electrónico. O informe de erros tardará un pouco en completarse desde o seu inicio ata que estea preparado para enviarse, polo que che recomendamos que teñas paciencia."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Informe interactivo"</string>
@@ -976,7 +977,7 @@
<string name="textSelectionCABTitle" msgid="5236850394370820357">"Selección de texto"</string>
<string name="addToDictionary" msgid="4352161534510057874">"Engadir ao dicionario"</string>
<string name="deleteText" msgid="6979668428458199034">"Eliminar"</string>
- <string name="inputMethod" msgid="1653630062304567879">"Método de entrada"</string>
+ <string name="inputMethod" msgid="1653630062304567879">"Método de introdución de texto"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"Accións de texto"</string>
<string name="email" msgid="4560673117055050403">"Correo electrónico"</string>
<string name="dial" msgid="1253998302767701559">"Chamar"</string>
@@ -1287,7 +1288,7 @@
<string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"Permiso solicitado\npara a conta <xliff:g id="ACCOUNT">%s</xliff:g>."</string>
<string name="forward_intent_to_owner" msgid="1207197447013960896">"Estás usando esta aplicación fóra do teu perfil de traballo"</string>
<string name="forward_intent_to_work" msgid="621480743856004612">"Estás usando esta aplicación no teu perfil de traballo"</string>
- <string name="input_method_binding_label" msgid="1283557179944992649">"Método de entrada"</string>
+ <string name="input_method_binding_label" msgid="1283557179944992649">"Método de introdución de texto"</string>
<string name="sync_binding_label" msgid="3687969138375092423">"Sincronizar"</string>
<string name="accessibility_binding_label" msgid="4148120742096474641">"Accesibilidade"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fondo de pantalla"</string>
@@ -1499,6 +1500,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Cando o atallo está activado, podes premer os dous botóns de volume durante 3 segundos para iniciar unha función de accesibilidade.\n\n Función de accesibilidade actual:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Podes cambiar a función en Configuración > Accesibilidade."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Desactivar atallo"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Utilizar atallo"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inversión de cor"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Corrección de cor"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"O atallo de accesibilidade activou <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"O atallo de accesibilidade desactivou <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Escolle que función queres utilizar cando toques o botón Accesibilidade:"</string>
@@ -1685,13 +1688,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Noite da semana"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Fin de semana"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Evento"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Durmindo"</string>
<string name="muted_by" msgid="6147073845094180001">"Silenciado por <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Produciuse un erro interno no teu dispositivo e quizais funcione de maneira inestable ata o restablecemento dos datos de fábrica."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Produciuse un erro interno co teu dispositivo. Contacta co teu fabricante para obter máis información."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"A solicitude USSD transformouse nunha solicitude DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"A solicitude USSD transformouse nunha solicitude SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"A solicitude USSD transformouse nunha nova solicitude USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"A solicitude USSD transformouse nunha solicitude DIAL de vídeo."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"A solicitude SS transformouse nunha solicitude DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"A solicitude SS transformouse nunha solicitude DIAL de vídeo."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"A solicitude SS transformouse nunha solicitude USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"A solicitude SS transformouse nunha nova solicitude SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Perfil de traballo"</string>
diff --git a/core/res/res/values-gu/required_apps_managed_device.xml b/core/res/res/values-gu/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-gu/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-gu/required_apps_managed_profile.xml b/core/res/res/values-gu/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-gu/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-gu/required_apps_managed_user.xml b/core/res/res/values-gu/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-gu/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index a6343ec..eada633 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"પાવર બંધ"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"કટોકટી"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"બગ રિપોર્ટ"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"સત્ર સમાપ્ત કરો"</string>
<string name="bugreport_title" msgid="2667494803742548533">"બગ રિપોર્ટ લો"</string>
<string name="bugreport_message" msgid="398447048750350456">"આ, એક ઇ-મેઇલ સંદેશ તરીકે મોકલવા માટે, તમારા વર્તમાન ઉપકરણ સ્થિતિ વિશેની માહિતી એકત્રિત કરશે. એક બગ રિપોર્ટ પ્રારંભ કરીને તે મોકલવા માટે તૈયાર ન થઈ જાય ત્યાં સુધી તેમાં થોડો સમય લાગશે; કૃપા કરીને ધીરજ રાખો."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ક્રિયાપ્રતિક્રિયાત્મક રિપોર્ટ"</string>
@@ -1051,8 +1052,7 @@
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"આને સિસ્ટમ સેટિંગ્સ > ઍપ્લિકેશનો > ડાઉનલોડ કરેલમાં ફરીથી સક્ષમ કરો."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> વર્તમાન પ્રદર્શન કદની સેટિંગનું સમર્થન કરતું નથી અને અનપેક્ષિત રીતે વર્તી શકે છે."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"હંમેશાં બતાવો"</string>
- <!-- no translation found for unsupported_compile_sdk_message (4253168368781441759) -->
- <skip />
+ <string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g>ને Android OSના અસંગત વર્ઝન માટે બનાવવામાં આવ્યું હતું અને તે અનપેક્ષિત રીતે કાર્ય કરી શકે છે. ઍપનું અપડેટ કરેલ વર્ઝન ઉપલબ્ધ હોઈ શકે છે."</string>
<string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"હંમેશાં બતાવો"</string>
<string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"અપડેટ માટે તપાસો"</string>
<string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> ઍપ્લિકેશન (<xliff:g id="PROCESS">%2$s</xliff:g> પ્રક્રિયા)એ તેની સ્વ-લાગુ કરેલ StrictMode નીતિનું ઉલ્લંઘન કર્યું છે."</string>
@@ -1500,6 +1500,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"જ્યારે શૉર્ટકટ ચાલુ હોય, ત્યારે બન્ને વૉલ્યૂમ બટનને 3 સેકન્ડ સુધી દબાવી રાખવાથી ઍક્સેસિબિલિટી સુવિધા શરૂ થઈ જશે.\n\n વર્તમાન ઍક્સેસિબિલિટી સુવિધા:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n તમે સેટિંગ્સ > ઍક્સેસિબિલિટીમાં જઈને આ સુવિધા બદલી શકો છો."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"શૉર્ટકટ બંધ કરો"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"શૉર્ટકટનો ઉપયોગ કરો"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"રંગનો વ્યુત્ક્રમ"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"રંગ સુધારણા"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ઍક્સેસિબિલિટી શૉર્ટકટે <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ચાલુ કરી"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ઍક્સેસિબિલિટી શૉર્ટકટે <xliff:g id="SERVICE_NAME">%1$s</xliff:g> બંધ કરી"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"જ્યારે તમે ઍક્સેસિબિલિટી બટન પર ટૅપ કરો, ત્યારે ઉપયોગ કરવાની સુવિધા પસંદ કરો:"</string>
@@ -1639,8 +1641,7 @@
<string name="package_installed_device_owner" msgid="6875717669960212648">"તમારા વ્યવસ્થાપક દ્વારા ઇન્સ્ટૉલ કરવામાં આવેલ છે"</string>
<string name="package_updated_device_owner" msgid="1847154566357862089">"તમારા વ્યવસ્થાપક દ્વારા અપડેટ કરવામાં આવેલ છે"</string>
<string name="package_deleted_device_owner" msgid="2307122077550236438">"તમારા વ્યવસ્થાપક દ્વારા કાઢી નાખવામાં આવેલ છે"</string>
- <!-- no translation found for battery_saver_description (5394663545060026162) -->
- <skip />
+ <string name="battery_saver_description" msgid="5394663545060026162">"બૅટરી આવરદા વધુ સારી કરવામાં સહાય કરવા માટે, બૅટરી સેવર તમારા ઉપકરણના કાર્યપ્રદર્શનને ઘટાડે છે અને વાઇબ્રેશન, સ્થાન સેવાઓ અને મોટાભાગના બૅકગ્રાઉન્ડ ડેટાને સીમિત કરે છે. ઇમેઇલ, મેસેજિંગ અને અન્ય ઍપ જે સિંક થવા પર આધાર રાખે છે, તેમને કદાચ તમે ખોલશો નહીં ત્યાં સુધી અપડેટ થઈ શકશે નહીં.\n\nજ્યારે તમારું ઉપકરણ ચાર્જ થઈ રહ્યું હોય ત્યારે બૅટરી સેવર આપમેળે બંધ થઈ જાય છે."</string>
<string name="data_saver_description" msgid="6015391409098303235">"ડેટા વપરાશને ઘટાડવામાં સહાય માટે, ડેટા સેવર કેટલીક ઍપ્લિકેશનોને પૃષ્ઠભૂમિમાં ડેટા મોકલવા અથવા પ્રાપ્ત કરવાથી અટકાવે છે. તમે હાલમાં ઉપયોગ કરી રહ્યાં છો તે ઍપ્લિકેશન ડેટાને ઍક્સેસ કરી શકે છે, પરંતુ તે આ ક્યારેક જ કરી શકે છે. આનો અર્થ એ હોઈ શકે છે, ઉદાહરણ તરીકે, છબીઓ ત્યાં સુધી પ્રદર્શિત થશે નહીં જ્યાં સુધી તમે તેને ટૅપ નહીં કરો."</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"ડેટા સેવર ચાલુ કરીએ?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"ચાલુ કરો"</string>
@@ -1687,13 +1688,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"સપ્તાહાંત રાત્રિ"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"સપ્તાહાંત"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"ઇવેન્ટ"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"નિષ્ક્રિય"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> દ્વારા મ્યૂટ કરાયું"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"તમારા ઉપકરણમાં આંતરિક સમસ્યા છે અને જ્યાં સુધી તમે ફેક્ટરી ડેટા ફરીથી સેટ કરશો નહીં ત્યાં સુધી તે અસ્થિર રહી શકે છે."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"તમારા ઉપકરણમાં આંતરિક સમસ્યા છે. વિગતો માટે તમારા નિર્માતાનો સંપર્ક કરો."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD વિનંતીને DIAL વિનંતી પર સંશોધિત કરી."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD વિનંતીને SS વિનંતી પર સંશોધિત કરી."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD વિનંતીને નવી USSD વિનંતી પર સંશોધિત કરી."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD વિનંતીને વીડિઓ DIAL વિનંતીમાં સંશોધિત કરી."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS વિનંતીને DIAL વિનંતી પર સંશોધિત કરી."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS વિનંતીને વીડિઓ DIAL વિનંતીમાં સંશોધિત કરી."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS વિનંતીને USSD વિનંતી પર સંશોધિત કરી."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS વિનંતીને નવી SS વિનંતી પર સંશોધિત કરી."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"કાર્યાલયની પ્રોફાઇલ"</string>
diff --git a/core/res/res/values-hi/required_apps_managed_device.xml b/core/res/res/values-hi/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-hi/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-hi/required_apps_managed_profile.xml b/core/res/res/values-hi/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-hi/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-hi/required_apps_managed_user.xml b/core/res/res/values-hi/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-hi/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 6f8fe0f..f9e8a03 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"पावर बंद"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"आपातकाल"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"गड़बड़ी की रिपोर्ट"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"सत्र खत्म करें"</string>
<string name="bugreport_title" msgid="2667494803742548533">"गड़बड़ी की रिपोर्ट लें"</string>
<string name="bugreport_message" msgid="398447048750350456">"इससे ईमेल भेजने के लिए, आपके डिवाइस की मौजूदा स्थिति से जुड़ी जानकारी इकट्ठा की जाएगी. गड़बड़ी की रिपोर्ट बनना शुरू होने से लेकर भेजने के लिए तैयार होने तक कुछ समय लगेगा; कृपया इंतज़ार करें."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"सहभागी रिपोर्ट"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"इस शॉर्टकट के चालू होने पर, दोनों वॉल्यूम बटनों को 3 सेकंड तक दबाने से सुलभता सुविधा शुरू हो जाएगी.\n\n मौजूदा सुलभता सुविधा:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n आप इस सुविधा को सेटिंग > सुलभता पर जाकर बदल सकते हैं."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"शॉर्टकट बंद करें"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"शॉर्टकट का उपयोग करें"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"रंग बदलने की सुविधा"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"रंग में सुधार करने की सुविधा"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"सुलभता शॉर्टकट ने <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को चालू किया"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"सुलभता शॉर्टकट ने <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को बंद किया"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"सुलभता बटन पर टैप करते समय इस्तेमाल की जाने वाली सुविधा चुनें:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"सप्ताह की रात"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"सप्ताहांत"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"इवेंट"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"सोते समय"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> द्वारा म्यूट किया गया"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"आपके डिवाइस में कोई अंदरूनी समस्या है और यह तब तक ठीक नहीं होगी जब तक आप फ़ैक्टरी डेटा रीसेट नहीं करते."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"आपके डिवाइस के साथ कोई आंतरिक गड़बड़ी हुई. विवरणों के लिए अपने निर्माता से संपर्क करें."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD अनुरोध को DIAL अनुरोध में बदल दिया गया है."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD अनुरोध को SS अनुरोध में बदल दिया गया है."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD अनुरोध को नए USSD अनुरोध में बदल दिया गया है."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD अनुरोध को वीडियो DIAL अनुरोध में बदल दिया गया है."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS अनुरोध को DIAL अनुरोध में बदल दिया गया है."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS अनुरोध को वीडियो DIAL अनुरोध में बदल दिया गया है."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS अनुरोध को USSD अनुरोध में बदल दिया गया है."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS अनुरोध को नए SS अनुरोध में बदल दिया गया है."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"कार्य प्रोफ़ाइल"</string>
diff --git a/core/res/res/values-hr/required_apps_managed_device.xml b/core/res/res/values-hr/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-hr/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-hr/required_apps_managed_profile.xml b/core/res/res/values-hr/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-hr/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-hr/required_apps_managed_user.xml b/core/res/res/values-hr/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-hr/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 1be072e..7e53209 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -214,6 +214,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Isključi"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Hitno"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Izvješće o bugovima"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Završi sesiju"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Izvješće o programskoj pogrešci"</string>
<string name="bugreport_message" msgid="398447048750350456">"Time će se prikupiti podaci o trenutačnom stanju vašeg uređaja koje ćete nam poslati u e-poruci. Za pripremu izvješća o programskoj pogrešci potrebno je nešto vremena pa vas molimo za strpljenje."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktivno izvješće"</string>
@@ -1522,6 +1523,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Kada je taj prečac uključen, pritiskom na obje tipke za glasnoću na 3 sekunde pokrenut će se značajka pristupačnosti.\n\n Trenutačna značajka pristupačnosti:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Značajku možete promijeniti u Postavkama > Pristupačnost."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Isključi prečac"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Upotrijebi prečac"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inverzija boja"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Korekcija boje"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Prečac pristupačnosti uključio je uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Prečac pristupačnosti isključio je uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Odaberite značajku koju ćete upotrebljavati kada dodirnete gumb Pristupačnost:"</string>
@@ -1717,13 +1720,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Noć radnog dana"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Vikend"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Događaj"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Spavanje"</string>
<string name="muted_by" msgid="6147073845094180001">"Zvuk je isklj. treća strana <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Na vašem uređaju postoji interni problem i možda neće biti stabilan dok ga ne vratite na tvorničko stanje."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Na vašem uređaju postoji interni problem. Obratite se proizvođaču za više pojedinosti."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD zahtjev izmijenjen je u DIAL zahtjev."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD zahtjev izmijenjen je u SS zahtjev."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD zahtjev izmijenjen je u novi USSD zahtjev."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD zahtjev izmijenjen je u Video DIAL zahtjev."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS zahtjev izmijenjen je u DIAL zahtjev."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS zahtjev izmijenjen je u Video DIAL zahtjev."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS zahtjev izmijenjen je u USSD zahtjev."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS zahtjev izmijenjen je u novi SS zahtjev."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Radni profil"</string>
diff --git a/core/res/res/values-hu/required_apps_managed_device.xml b/core/res/res/values-hu/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-hu/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-hu/required_apps_managed_profile.xml b/core/res/res/values-hu/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-hu/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-hu/required_apps_managed_user.xml b/core/res/res/values-hu/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-hu/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index d9b4f61c..b815035 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Kikapcsolás"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Segélyhívás"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Programhiba bejelentése"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Munkamenet befejezése"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Hibajelentés készítése"</string>
<string name="bugreport_message" msgid="398447048750350456">"Ezzel információt fog gyűjteni az eszköz jelenlegi állapotáról, amelyet a rendszer e-mailben fog elküldeni. Kérjük, legyen türelemmel, amíg a hibajelentés elkészül, és küldhető állapotba kerül."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktív jelentés"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Ha be van kapcsolva a billentyűparancs, a két hangerőgomb 3 másodpercig tartó lenyomásával elindíthatja a kisegítő lehetőségek egyik funkcióját.\n\n A kisegítő lehetőségek jelenleg beállított funkciója:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n A funkciót a Beállítások > Kisegítő lehetőségek menüpontban módosíthatja."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Billentyűparancs kikapcsolása"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Billentyűparancs használata"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Színek invertálása"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Színkorrekció"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"A Kisegítő lehetőségek gyorsparancsa bekapcsolta a következő szolgáltatást: <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"A Kisegítő lehetőségek gyorsparancsa kikapcsolta a következő szolgáltatást: <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Válassza ki a Kisegítő lehetőségek gombra koppintáskor használni kívánt funkciót:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Hétköznap éjszaka"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Hétvége"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Esemény"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Alvás"</string>
<string name="muted_by" msgid="6147073845094180001">"A(z) <xliff:g id="THIRD_PARTY">%1$s</xliff:g> elnémította"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Belső probléma van az eszközzel, és instabil lehet, amíg vissza nem állítja a gyári adatokat."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Belső probléma van az eszközzel. A részletekért vegye fel a kapcsolatot a gyártóval."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Az USSD-kérés módosítva DIAL-kérésre."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Az USSD-kérés módosítva SS-kérésre."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Az USSD-kérés módosítva új USSD-kérésre."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD-kérés módosítva Video DIAL-kérésre."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Az SS-kérés módosítva DIAL-kérésre."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS-kérés módosítva Video DIAL-kérésre."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Az SS-kérés módosítva USSD-kérésre."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Az SS-kérés módosítva új SS-kérésre."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Munkaprofil"</string>
diff --git a/core/res/res/values-hy/required_apps_managed_device.xml b/core/res/res/values-hy/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-hy/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-hy/required_apps_managed_profile.xml b/core/res/res/values-hy/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-hy/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-hy/required_apps_managed_user.xml b/core/res/res/values-hy/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-hy/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 094becd..ff615c50 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Անջատել"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Շտապ կանչ"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Վրիպակի զեկույց"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Ավարտել աշխատաշրջանը"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Գրել սխալի զեկույց"</string>
<string name="bugreport_message" msgid="398447048750350456">"Սա տեղեկություններ կհավաքագրի ձեր սարքի առկա կարգավիճակի մասին և կուղարկի այն էլեկտրոնային նամակով: Որոշակի ժամանակ կպահանջվի վրիպակի մասին զեկուցելու պահից սկսած մինչ ուղարկելը: Խնդրում ենք փոքր-ինչ համբերատար լինել:"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Ինտերակտիվ զեկույց"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Մատչելիության գործառույթն օգտագործելու համար սեղմեք և 3 վայրկյան սեղմած պահեք ձայնի ուժգնության երկու կոճակները, երբ գործառույթը միացված է։\n\n Մատչելիության ակտիվ գործառույթը՝\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Գործառույթը կարող եք փոփոխել՝ անցնելով Կարգավորումներ > Մատչելիություն։"</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Անջատել դյուրանցումը"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Օգտագործել դյուրանցումը"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Գունաշրջում"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Գունաշտկում"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Մատչելիության դյուրանցումն միացրել է <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունը"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Մատչելիության դյուրանցումն անջատել է <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունը"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Ընտրեք որևէ գործառույթ, որը կօգտագործվի Մատչելիություն կոճակին հպելու դեպքում՝"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Աշխատանքային օր"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Շաբաթ-կիրակի"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Միջոցառում"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Քնելիս"</string>
<string name="muted_by" msgid="6147073845094180001">"Համրեցվել է <xliff:g id="THIRD_PARTY">%1$s</xliff:g>-ի կողմից"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Սարքում ներքին խնդիր է առաջացել և այն կարող է կրկնվել, մինչև չվերականգնեք գործարանային կարգավորումները:"</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Սարքում ներքին խնդիր է առաջացել: Մանրամասների համար կապվեք արտադրողի հետ:"</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD հարցումը փոխվել է DIAL հարցման:"</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD հարցումը փոխվել է SS հարցման:"</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD հարցումը փոխվել է նոր USSD հարցման:"</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD հարցումը փոխվել է Video DIAL հարցման:"</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS հարցումը փոխվել է DIAL հարցման:"</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS հարցումը փոխվել է Video DIAL հարցման:"</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS հարցումը փոխվել է USSD հարցման:"</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS հարցումը փոխվել է նոր SS հարցման:"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Աշխատանքային պրոֆիլ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index c499ea7..b8b76a0 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -212,8 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Matikan perangkat"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Darurat"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Laporan bug"</string>
- <!-- no translation found for global_action_logout (935179188218826050) -->
- <skip />
+ <string name="global_action_logout" msgid="935179188218826050">"Akhiri sesi"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Ambil laporan bug"</string>
<string name="bugreport_message" msgid="398447048750350456">"Ini akan mengumpulkan informasi status perangkat Anda saat ini, untuk dikirimkan sebagai pesan email. Harap bersabar, mungkin perlu waktu untuk memulai laporan bug hingga siap dikirim."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Laporan interaktif"</string>
@@ -1500,10 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Saat pintasan aktif, menekan kedua tombol volume selama 3 detik akan memulai fitur aksesibilitas.\n\n Fitur aksesibilitas saat ini:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Anda dapat mengubah fitur di Setelan > Aksesibilitas."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Nonaktifkan Pintasan"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Gunakan Pintasan"</string>
- <!-- no translation found for color_inversion_feature_name (4231186527799958644) -->
- <skip />
- <!-- no translation found for color_correction_feature_name (6779391426096954933) -->
- <skip />
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inversi Warna"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Koreksi Warna"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Pintasan Aksesibilitas mengaktifkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Pintasan Aksesibilitas menonaktifkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Pilih fitur yang akan digunakan saat menge-tap tombol Aksesibilitas:"</string>
@@ -1690,15 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Malam hari kerja"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Akhir pekan"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Acara"</string>
- <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) -->
- <skip />
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Tidur"</string>
<string name="muted_by" msgid="6147073845094180001">"Dinonaktifkan oleh <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Ada masalah dengan perangkat. Hal ini mungkin membuat perangkat jadi tidak stabil dan perlu dikembalikan ke setelan pabrik."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Ada masalah dengan perangkat. Hubungi produsen perangkat untuk informasi selengkapnya."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Permintaan USSD diubah menjadi permintaan DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Permintaan USSD diubah menjadi permintaan SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Permintaan USSD diubah menjadi permintaan USSD baru."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Permintaan USSD diubah menjadi permintaan DIAL Video."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Permintaan SS diubah menjadi permintaan DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Permintaan SS diubah menjadi permintaan DIAL Video."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Permintaan SS diubah menjadi permintaan USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Permintaan SS diubah menjadi permintaan SS baru."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profil kerja"</string>
diff --git a/core/res/res/values-is/required_apps_managed_device.xml b/core/res/res/values-is/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-is/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-is/required_apps_managed_profile.xml b/core/res/res/values-is/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-is/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-is/required_apps_managed_user.xml b/core/res/res/values-is/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-is/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 4fcf2d8..76286e1 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Slökkva"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Neyðarsímtal"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Villutilkynning"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Ljúka lotu"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Útbúa villutilkynningu"</string>
<string name="bugreport_message" msgid="398447048750350456">"Þetta safnar upplýsingum um núverandi stöðu tækisins til að senda með tölvupósti. Það tekur smástund frá því villutilkynningin er ræst og þar til hún er tilbúin til sendingar – sýndu biðlund."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Gagnvirk skýrsla"</string>
@@ -1499,6 +1500,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Þegar flýtileiðin er virk er kveikt á aðgengiseiginleikanum með því að halda báðum hljóðstyrkshnöppunum inni í þrjár sekúndur.\n\n Virkur aðgengiseiginleiki:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Hægt er að skipta um eiginleika í Stillingar > Aðgengi."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Slökkva á flýtileið"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Nota flýtileið"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Umsnúningur lita"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Litaleiðrétting"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Flýtileið aðgengisstillingar kveikti á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Flýtileið aðgengisstillingar slökkti á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Veldu eiginleika sem á að nota þegar ýtt er á aðgengishnappinn:"</string>
@@ -1685,13 +1688,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Virkt kvöld"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Helgi"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Viðburður"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Svefn"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> tók hljóðið af"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Innra vandamál kom upp í tækinu og það kann að vera óstöðugt þangað til þú núllstillir það."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Innra vandamál kom upp í tækinu. Hafðu samband við framleiðanda til að fá nánari upplýsingar."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD-beiðni er breytt í DIAL-beiðni."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD-beiðni er breytt í SS-beiðni."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD-beiðni er breytt í nýja USSD-beiðni."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD-beiðni var breytt í Video DIAL-beiðni."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-beiðni er breytt í DIAL-beiðni."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS-beiðni var breytt í Video DIAL-beiðni."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-beiðni er breytt í USSD-beiðni."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-beiðni er breytt í nýja SS-beiðni."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Vinnusnið"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 5f85317..2116542 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Spegni"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Emergenza"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Segnalazione di bug"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Termina sessione"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Apri segnalazione bug"</string>
<string name="bugreport_message" msgid="398447048750350456">"Verranno raccolte informazioni sullo stato corrente del dispositivo che saranno inviate sotto forma di messaggio email. Passerà un po\' di tempo prima che la segnalazione di bug aperta sia pronta per essere inviata; ti preghiamo di avere pazienza."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Rapporto interattivo"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Quando la scorciatoia è attiva, puoi premere entrambi i pulsanti del volume per tre secondi per avviare una funzione di accessibilità.\n\n Funzione di accessibilità corrente:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Puoi cambiare la funzione in Impostazioni > Accessibilità."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Disattiva scorciatoia"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Usa scorciatoia"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inversione colori"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Correzione colore"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"La scorciatoia Accessibilità ha attivato <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"La scorciatoia Accessibilità ha disattivato <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Scegli una funzione da usare quando tocchi il pulsante Accessibilità:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Notte di un giorno feriale"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Fine settimana"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Evento"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Sleeping (Notte)"</string>
<string name="muted_by" msgid="6147073845094180001">"Audio disattivato da <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Si è verificato un problema interno con il dispositivo, che potrebbe essere instabile fino al ripristino dei dati di fabbrica."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Si è verificato un problema interno con il dispositivo. Per informazioni dettagliate, contatta il produttore."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"La richiesta USSD è stata modificata in richiesta DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"La richiesta USSD è stata modificata in richiesta SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"La richiesta USSD è stata modificata in nuova richiesta USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"La richiesta USSD è stata modificata in richiesta DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"La richiesta SS è stata modificata in richiesta DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"La richiesta SS è stata modificata in richiesta Video DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"La richiesta SS è stata modificata in richiesta USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"La richiesta SS è stata modificata in nuova richiesta SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profilo di lavoro"</string>
diff --git a/core/res/res/values-iw/required_apps_managed_device.xml b/core/res/res/values-iw/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-iw/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-iw/required_apps_managed_profile.xml b/core/res/res/values-iw/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-iw/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-iw/required_apps_managed_user.xml b/core/res/res/values-iw/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-iw/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 3ae2857..87e20c0 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -216,6 +216,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"כיבוי"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"חירום"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"דיווח על באג"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"סיום הפעלה"</string>
<string name="bugreport_title" msgid="2667494803742548533">"שלח דיווח על באג"</string>
<string name="bugreport_message" msgid="398447048750350456">"פעולה זו תאסוף מידע על מצב המכשיר הנוכחי שלך על מנת לשלוח אותו כהודעת אימייל. היא תימשך זמן קצר מרגע פתיחת דיווח הבאג ועד לשליחת ההודעה בפועל. אנא המתן בסבלנות."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"דוח אינטראקטיבי"</string>
@@ -1546,6 +1547,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"כשקיצור הדרך מופעל, לחיצה על שני לחצני עוצמת השמע למשך שלוש שניות מפעילה את תכונת הנגישות.\n\n תכונת הנגישות המוגדרת כרגע:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n אפשר לשנות את התכונה בקטע \'הגדרות ונגישות\'."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"כבה את קיצור הדרך"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"השתמש בקיצור הדרך"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"היפוך צבעים"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"תיקון צבעים"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> הופעל על-ידי קיצור הדרך לנגישות"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> הושבת על-ידי קיצור הדרך לנגישות"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"בחר תכונה שתופעל כשתלחץ על הלחצן \'נגישות\':"</string>
@@ -1750,13 +1753,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"ערב ביום חול"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"סוף השבוע"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"אירוע"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"שינה"</string>
<string name="muted_by" msgid="6147073845094180001">"הושתק על ידי <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"קיימת בעיה פנימית במכשיר שלך, וייתכן שהתפקוד שלו לא יהיה יציב עד שתבצע איפוס לנתוני היצרן."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"קיימת בעיה פנימית במכשיר שלך. לקבלת פרטים, צור קשר עם היצרן."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"בקשת USSD שונתה לבקשת DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"בקשת USSD שונתה לבקשת SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"בקשת USSD שונתה לבקשת USSD חדשה."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"בקשת USSD שונתה לבקשת Video DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"בקשת SS שונתה לבקשת DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"בקשת SS שונתה לבקשת Video DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"בקשת SS שונתה לבקשת USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"בקשת SS שונתה לבקשת SS חדשה."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"פרופיל עבודה"</string>
diff --git a/core/res/res/values-ja/required_apps_managed_device.xml b/core/res/res/values-ja/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-ja/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ja/required_apps_managed_profile.xml b/core/res/res/values-ja/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-ja/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ja/required_apps_managed_user.xml b/core/res/res/values-ja/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-ja/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 11b4a0f..c2cc154 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"電源を切る"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"緊急通報"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"バグレポート"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"セッションを終了"</string>
<string name="bugreport_title" msgid="2667494803742548533">"バグレポートを取得"</string>
<string name="bugreport_message" msgid="398447048750350456">"現在の端末の状態に関する情報が収集され、その内容がメールで送信されます。バグレポートが開始してから送信可能な状態となるまでには多少の時間がかかりますのでご了承ください。"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"対話型レポート"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"ショートカットが ON の場合、両方の音量ボタンを 3 秒間押し続けるとユーザー補助機能が起動します。\n\n現在のユーザー補助機能:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\nユーザー補助機能は [設定] > [ユーザー補助] で変更できます。"</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"ショートカットを OFF にする"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"ショートカットを使用"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"色反転"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"色補正"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ユーザー補助機能のショートカットにより <xliff:g id="SERVICE_NAME">%1$s</xliff:g> は ON になっています"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ユーザー補助機能のショートカットにより <xliff:g id="SERVICE_NAME">%1$s</xliff:g> は OFF になっています"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"[ユーザー補助] ボタンをタップした場合に使用する機能を選択してください。"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"平日の夜"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"週末"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"予定"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"睡眠中"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>によりミュートになっています"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"端末で内部的な問題が発生しました。データが初期化されるまで不安定になる可能性があります。"</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"端末で内部的な問題が発生しました。詳しくはメーカーにお問い合わせください。"</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSDリクエストはDIALリクエストに変更されました。"</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSDリクエストはSSリクエストに変更されました。"</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSDリクエストは新しいUSSDリクエストに変更されました。"</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD リクエストは Video DIAL リクエストに変更されました。"</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SSリクエストはDIALリクエストに変更されました。"</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS リクエストは Video DIAL リクエストに変更されました。"</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SSリクエストはUSSDリクエストに変更されました。"</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SSリクエストは新しいSSリクエストに変更されました。"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"仕事用プロファイル"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index ce07805..ae4fb6f 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -212,8 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"კვების გამორთვა"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"საგანგებო სამსახურები"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"ხარვეზის შესახებ ანგარიში"</string>
- <!-- no translation found for global_action_logout (935179188218826050) -->
- <skip />
+ <string name="global_action_logout" msgid="935179188218826050">"სესიის დასრულება"</string>
<string name="bugreport_title" msgid="2667494803742548533">"შექმენით შეცდომის ანგარიში"</string>
<string name="bugreport_message" msgid="398447048750350456">"იგი შეაგროვებს ინფორმაციას თქვენი მოწყობილობის ამჟამინდელი მდგომარეობის შესახებ, რათა ის ელფოსტის შეტყობინების სახით გააგზავნოს. ხარვეზის ანგარიშის მომზადებასა და შეტყობინების გაგზავნას გარკვეული დრო სჭირდება. გთხოვთ, მოითმინოთ."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ინტერაქტიული ანგარიში"</string>
@@ -1500,10 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"მალსახმობის ჩართვის შემთხვევაში, ხმის ორივე ღილაკზე 3 წამის განმავლობაში დაჭერით მარტივი წვდომის ფუნქცია ჩაირთვება.\n\n მარტივი წვდომის ამჟამინდელი ფუნქციაა:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ამ ფუნქციის შეცვლა შეგიძლიათ აქ: პარამეტრები > მარტივი წვდომა."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"მალსახმობის გამორთვა"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"მალსახმობის გამოყენება"</string>
- <!-- no translation found for color_inversion_feature_name (4231186527799958644) -->
- <skip />
- <!-- no translation found for color_correction_feature_name (6779391426096954933) -->
- <skip />
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"ფერთა ინვერსია"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"ფერთა კორექცია"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"მარტივი წვდომის მალსახმობმა ჩართო <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"მარტივი წვდომის მალსახმობმა გამორთო <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"აირჩიეთ მარტივი წვდომის ღილაკზე შეხებისას გამოსაყენებელი ფუნქცია:"</string>
@@ -1690,15 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"სამუშაო კვირის ღამე"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"შაბათ-კვირა"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"მოვლენა"</string>
- <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) -->
- <skip />
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"ძილისას"</string>
<string name="muted_by" msgid="6147073845094180001">"დადუმებულია <xliff:g id="THIRD_PARTY">%1$s</xliff:g>-ის მიერ"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"ფიქსირდება თქვენი მ ოწყობილობის შიდა პრობლემა და შეიძლება არასტაბილური იყოს, სანამ ქარხნულ მონაცემების არ განაახლებთ."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"ფიქსირდება თქვენი მოწყობილობის შიდა პრობლემა. დეტალებისათვის, მიმართეთ თქვენს მწარმოებელს."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD მოთხოვნა შეიცვლალა DIAL მოთხოვნით."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD მოთხოვნა შეიცვალა SS მოთხოვნით."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD მოთხოვნა შეიცვალა ახალი USSD მოთხოვნით."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD მოთხოვნა შეიცვალა Video DIAL მოთხოვნით."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS მოთხოვნა შეიცვალა DIAL მოთხოვნით."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS მოთხოვნა შეიცვალა Video DIAL მოთხოვნით."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS მოთხოვნა შეიცვალა USSD მოთხოვნით."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS მოთხოვნა შეიცვალა ახალი SS მოთხოვნით."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"სამსახურის პროფილი"</string>
diff --git a/core/res/res/values-kk/required_apps_managed_device.xml b/core/res/res/values-kk/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-kk/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-kk/required_apps_managed_profile.xml b/core/res/res/values-kk/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-kk/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-kk/required_apps_managed_user.xml b/core/res/res/values-kk/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-kk/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 0c5de0d..d56e6df 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Өшіру"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Төтенше жағдай"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Вирус туралы хабарлау"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Сеансты аяқтау"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Қате туралы есеп құру"</string>
<string name="bugreport_message" msgid="398447048750350456">"Құрылғының қазіргі күйі туралы ақпаратты жинап, электрондық хабармен жібереді. Есеп әзір болғанша біраз уақыт кетеді, шыдай тұрыңыз."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Интерактивті есеп"</string>
@@ -1499,6 +1500,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Бұл төте жол қосулы кезде дыбыс деңгейі түймелерінің екеуін де 3 секунд бойы басқанда арнайы мүмкіндік іске қосылады.\n\n Ағымдағы арнайы мүмкіндік:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Бұл мүмкіндікті \"Параметрлер\" > \"Арнайы мүмкіндіктер\" тармағында өзгертуге болады."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Төте жолды өшіру"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Төте жолды пайдалану"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Түстер инверсиясы"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Түс жөндеу"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Арнайы мүмкіндіктер таңбашасы <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қызметін қосты"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Арнайы мүмкіндіктер таңбашасы <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қызметін өшірді"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"\"Арнайы мүмкіндіктер\" түймесін түрткенде пайдаланатын мүмкіндікті таңдаңыз:"</string>
@@ -1685,13 +1688,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Жұмыс күндері кешке"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Демалыс күндері"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Оқиға"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Ұйқы режимі"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> үнін өшірген"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"There\'s an internal problem with your device, and it may be unstable until you factory data reset."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"There\'s an internal problem with your device. Contact your manufacturer for details."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD сұрауы DIAL сұрауына өзгертілді."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD сұрауы SS сұрауына өзгертілді."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD сұрауы жаңа USSD сұрауына өзгертілді."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD сұрауы бейне DIAL сұрауына өзгертілді."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS сұрауы DIAL сұрауына өзгертілді."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS сұрауы бейне DIAL сұрауына өзгертілді."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS сұрауы USSD сұрауына өзгертілді."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS сұрауы жаңа SS сұрауына өзгертілді."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Жұмыс профилі"</string>
diff --git a/core/res/res/values-km/required_apps_managed_device.xml b/core/res/res/values-km/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-km/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-km/required_apps_managed_profile.xml b/core/res/res/values-km/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-km/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-km/required_apps_managed_user.xml b/core/res/res/values-km/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-km/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index edf3441..7be15f1 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"បិទ"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"អាសន្ន"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"របាយការណ៍កំហុស"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"បញ្ចប់សម័យ"</string>
<string name="bugreport_title" msgid="2667494803742548533">"យករបាយការណ៍កំហុស"</string>
<string name="bugreport_message" msgid="398447048750350456">"វានឹងប្រមូលព័ត៌មានអំពីស្ថានភាពឧបករណ៍របស់អ្នក ដើម្បីផ្ញើជាសារអ៊ីមែល។ វានឹងចំណាយពេលតិចពីពេលចាប់ផ្ដើមរបាយការណ៍រហូតដល់ពេលវារួចរាល់ដើម្បីផ្ញើ សូមអត់ធ្មត់។"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"របាយការណ៍អន្តរកម្ម"</string>
@@ -1500,6 +1501,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"នៅពេលផ្លូវកាត់នេះបើក ការចុចប៊ូតុងកម្រិតសំឡេងទាំងពីរឲ្យជាប់រយៈពេល 3 វិនាទីនឹងចាប់ផ្តើមមុខងារភាពងាយស្រួល។\n\n មុខងារភាពងាយស្រួលបច្ចុប្បន្ន៖\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n អ្នកអាចផ្លាស់ប្តូរមុខងារនេះបាននៅក្នុងការ កំណត់ > ភាពងាយស្រួល។"</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"បិទផ្លូវកាត់"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"ប្រើប្រាស់ផ្លូវកាត់"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"បញ្ច្រាសពណ៌"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"ការកែពណ៌"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ផ្លូវកាត់ភាពងាយស្រួលបានបើក <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ផ្លូវកាត់ភាពងាយស្រួលបានបិទ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ជ្រើសរើសមុខងារដែលត្រូវប្រើ នៅពេលដែលអ្នកចុចប៊ូតុងភាពងាយស្រួល៖"</string>
@@ -1686,13 +1689,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"យប់ថ្ងៃធម្មតា"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"ចុងសប្ដាហ៍"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"ព្រឹត្តិការណ៍"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"កំពុងដេក"</string>
<string name="muted_by" msgid="6147073845094180001">"បានបិទសំឡេងដោយ <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"មានបញ្ហាខាងក្នុងឧបករណ៍របស់អ្នក ហើយវាអ្នកមិនមានស្ថេរភាព រហូតទាល់តែអ្នកកំណត់ដូចដើមវិញទាំងស្រុង។"</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"មានបញ្ហាខាងក្នុងឧបករណ៍របស់អ្នក ទំនាក់ទំនងក្រុមហ៊ុនផលិតឧបករណ៍របស់អ្នកសម្រាប់ព័ត៌មានបន្ថែម។"</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"សំណើរ USSD ត្រូវបានកែសម្រួលទៅតាមសំណើរការហៅទូរស័ព្ទ។"</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"សំណើរ USSD ត្រូវបានកែសម្រួលទៅតាមសំណើរ SS។"</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"សំណើរ USSD ត្រូវបានកែសម្រួលទៅតាមសំណើរ USSD ថ្មី។្"</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"សំណើ USSD ត្រូវបានកែប្រែទៅជាសំណើ DIAL ជាវីដេអូ។"</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"សំណើរ SS ត្រូវបានកែសម្រួលទៅតាមសំណើរការហៅទូរស័ព្ទ។"</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"សំណើ SS ត្រូវបានកែប្រែទៅជាសំណើ DIAL ជាវីដេអូ។"</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"សំណើរ SS ត្រូវបានកែសម្រួលទៅតាមសំណើរ USSD។"</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"សំណើរ SS ត្រូវបានកែសម្រួលទៅតាមសំណើរ SS ថ្មី។"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"ប្រវត្តិរូបការងារ"</string>
diff --git a/core/res/res/values-kn/required_apps_managed_device.xml b/core/res/res/values-kn/required_apps_managed_device.xml
new file mode 100644
index 0000000..e15b0b0
--- /dev/null
+++ b/core/res/res/values-kn/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.ಸೆಟ್ಟಿಂಗ್ಗಳು"</item>
+ <item msgid="7004798084799227194">"com.android.ಸಂಪರ್ಕಗಳು"</item>
+ <item msgid="5782220690863647256">"com.android.ಡಯಲರ್"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.ಒದಗಿಸುವವರು.ಡೌನ್ಲೋಡ್ಗಳು"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-kn/required_apps_managed_profile.xml b/core/res/res/values-kn/required_apps_managed_profile.xml
new file mode 100644
index 0000000..92ab682
--- /dev/null
+++ b/core/res/res/values-kn/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.ಸಂಪರ್ಕಗಳು"</item>
+ <item msgid="4633145750237794002">"com.android.ಸೆಟ್ಟಿಂಗ್ಗಳು"</item>
+ <item msgid="6518205098643077579">"com.android.ಒದಗಿಸುವವರು.ಡೌನ್ಲೋಡ್ಗಳು"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-kn/required_apps_managed_user.xml b/core/res/res/values-kn/required_apps_managed_user.xml
new file mode 100644
index 0000000..0c59edd
--- /dev/null
+++ b/core/res/res/values-kn/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.ಸೆಟ್ಟಿಂಗ್ಗಳು"</item>
+ <item msgid="2250259015310893915">"com.android.ಸಂಪರ್ಕಗಳು"</item>
+ <item msgid="7166574999426592423">"com.android.ಡಯಲರ್"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.ಒದಗಿಸುವವರು.ಡೌನ್ಲೋಡ್ಗಳು"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index ede8ebb..99db9e6 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"ಪವರ್ ಆಫ್ ಮಾಡು"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"ತುರ್ತು"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"ದೋಷದ ವರದಿ"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"ಸೆಷನ್ ಅಂತ್ಯಗೊಳಿಸಿ"</string>
<string name="bugreport_title" msgid="2667494803742548533">"ದೋಷ ವರದಿ ರಚಿಸಿ"</string>
<string name="bugreport_message" msgid="398447048750350456">"ನಿಮ್ಮ ಸಾಧನದ ಪ್ರಸ್ತುತ ಸ್ಥಿತಿಯ ಕುರಿತು ಮಾಹಿತಿಯನ್ನು ಸಂಗ್ರಹಿಸಿಕೊಳ್ಳುವುದರ ಜೊತೆ ಇ-ಮೇಲ್ ರೂಪದಲ್ಲಿ ನಿಮಗೆ ರವಾನಿಸುತ್ತದೆ. ಇದು ದೋಷ ವರದಿಯನ್ನು ಪ್ರಾರಂಭಿಸಿದ ಸಮಯದಿಂದ ಅದನ್ನು ಕಳುಹಿಸುವವರೆಗೆ ಸ್ವಲ್ಪ ಸಮಯವನ್ನು ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ; ದಯವಿಟ್ಟು ತಾಳ್ಮೆಯಿಂದಿರಿ."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ಪರಸ್ಪರ ಸಂವಹನ ವರದಿ"</string>
@@ -1499,6 +1500,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"ಶಾರ್ಟ್ಕಟ್ ಆನ್ ಆಗಿರುವಾಗ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯ ಆನ್ ಮಾಡಲು, ಎರಡೂ ವಾಲ್ಯೂಮ್ ಬಟನ್ಗಳನ್ನು ನೀವು 3 ಸೆಕೆಂಡುಗಳ ಕಾಲ ಒತ್ತಬೇಕು.\n\nಪ್ರಸ್ತುತ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯ: \n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಪ್ರವೇಶಿಸುವಿಕೆಯಲ್ಲಿ ನೀವು ವೈಶಿಷ್ಟ್ಯವನ್ನು ಬದಲಾಯಿಸಬಹುದು."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"ಶಾರ್ಟ್ಕಟ್ ಆಫ್ ಮಾಡಿ"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"ಶಾರ್ಟ್ಕಟ್ ಬಳಸಿ"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"ಬಣ್ಣ ವಿಲೋಮ"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"ಬಣ್ಣ ತಿದ್ದುಪಡಿ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ಪ್ರವೇಶಿಸುವಿಕೆ ಶಾರ್ಟ್ಕಟ್, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡಿದೆ"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ಪ್ರವೇಶಿಸುವಿಕೆ ಶಾರ್ಟ್ಕಟ್, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆಫ್ ಮಾಡಿದೆ"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ನೀವು ಪ್ರವೇಶಿಸುವಿಕೆ ಬಟನ್ ಟ್ಯಾಪ್ ಮಾಡಿದಾಗ ಬಳಸುವುದಕ್ಕಾಗಿ ವೈಶಿಷ್ಟ್ಯವನ್ನು ಆರಿಸಿ:"</string>
@@ -1685,13 +1688,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"ವಾರದ ರಾತ್ರಿ"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"ವಾರಾಂತ್ಯ"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"ಈವೆಂಟ್"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"ಮಲಗುತ್ತಿದ್ದಾರೆ"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ಅವರಿಂದ ಮ್ಯೂಟ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಆಂತರಿಕ ಸಮಸ್ಯೆಯಿದೆ ಹಾಗೂ ನೀವು ಫ್ಯಾಕ್ಟರಿ ಡೇಟಾವನ್ನು ಮರುಹೊಂದಿಸುವರೆಗೂ ಅದು ಅಸ್ಥಿರವಾಗಬಹುದು."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಆಂತರಿಕ ಸಮಸ್ಯೆಯಿದೆ. ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ತಯಾರಕರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD ವಿನಂತಿಯನ್ನು DIAL ವಿನಂತಿಗೆ ಮಾರ್ಪಡಿಸಲಾಗಿದೆ."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD ವಿನಂತಿಯನ್ನು SS ವಿನಂತಿಗೆ ಮಾರ್ಪಡಿಸಲಾಗಿದೆ."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD ವಿನಂತಿಯನ್ನು ಹೊಸ USSD ವಿನಂತಿಗೆ ಮಾರ್ಪಡಿಸಲಾಗಿದೆ."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD ವಿನಂತಿಯನ್ನು ವೀಡಿಯೊ DIAL ವಿನಂತಿಗೆ ಮಾರ್ಪಡಿಸಲಾಗಿದೆ."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS ವಿನಂತಿಯನ್ನು DIAL ವಿನಂತಿಗೆ ಮಾರ್ಪಡಿಸಲಾಗಿದೆ."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS ವಿನಂತಿಯನ್ನು ವೀಡಿಯೊ DIAL ವಿನಂತಿಗೆ ಮಾರ್ಪಡಿಸಲಾಗಿದೆ."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS ವಿನಂತಿಯನ್ನು USSD ವಿನಂತಿಗೆ ಮಾರ್ಪಡಿಸಲಾಗಿದೆ."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS ವಿನಂತಿಯನ್ನು ಹೊಸ SS ವಿನಂತಿಗೆ ಮಾರ್ಪಡಿಸಲಾಗಿದೆ."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್"</string>
diff --git a/core/res/res/values-ko/required_apps_managed_device.xml b/core/res/res/values-ko/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-ko/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ko/required_apps_managed_profile.xml b/core/res/res/values-ko/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-ko/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ko/required_apps_managed_user.xml b/core/res/res/values-ko/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-ko/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 679ca75..650d980 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"종료"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"긴급 전화"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"버그 신고"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"세션 끝내기"</string>
<string name="bugreport_title" msgid="2667494803742548533">"버그 신고"</string>
<string name="bugreport_message" msgid="398447048750350456">"현재 기기 상태에 대한 정보를 수집하여 이메일 메시지로 전송합니다. 버그 신고를 시작하여 전송할 준비가 되려면 약간 시간이 걸립니다."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"대화형 보고서"</string>
@@ -867,7 +868,7 @@
<item quantity="other">지난 <xliff:g id="COUNT_1">%d</xliff:g>일</item>
<item quantity="one">지난 <xliff:g id="COUNT_0">%d</xliff:g>일</item>
</plurals>
- <string name="last_month" msgid="3959346739979055432">"지난 달"</string>
+ <string name="last_month" msgid="3959346739979055432">"지난달"</string>
<string name="older" msgid="5211975022815554840">"이전"</string>
<string name="preposition_for_date" msgid="9093949757757445117">"<xliff:g id="DATE">%s</xliff:g>"</string>
<string name="preposition_for_time" msgid="5506831244263083793">"<xliff:g id="TIME">%s</xliff:g>"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"단축키가 사용 설정된 경우 두 개의 볼륨 버튼을 3초간 누르면 접근성 기능이 시작됩니다.\n\n 현재 접근성 기능:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n \'설정 > 접근성\'에서 기능을 변경할 수 있습니다."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"단축키 사용 중지"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"단축키 사용"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"색상 반전"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"색상 보정"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"접근성 단축키로 인해 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 설정되었습니다."</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"접근성 단축키로 인해 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 중지되었습니다."</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"접근성 버튼을 탭할 때 사용할 기능을 선택하세요."</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"평일 밤"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"주말"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"일정"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"수면 중"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>에서 알림음 음소거"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"사용 중인 기기 내부에 문제가 발생했습니다. 초기화할 때까지 불안정할 수 있습니다."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"사용 중인 기기 내부에 문제가 발생했습니다. 자세한 내용은 제조업체에 문의하세요."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD 요청이 DIAL 요청으로 수정됩니다."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD 요청이 SS 요청으로 수정됩니다."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD 요청이 새로운 USSD 요청으로 수정됩니다."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD 요청이 동영상 DIAL 요청으로 수정됩니다."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS 요청이 DIAL 요청으로 수정됩니다."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS 요청이 동영상 DIAL 요청으로 수정됩니다."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS 요청이 USSD 요청으로 수정됩니다."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS 요청이 새로운 SS 요청으로 수정됩니다."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"직장 프로필"</string>
diff --git a/core/res/res/values-ky/required_apps_managed_device.xml b/core/res/res/values-ky/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-ky/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ky/required_apps_managed_profile.xml b/core/res/res/values-ky/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-ky/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ky/required_apps_managed_user.xml b/core/res/res/values-ky/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-ky/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index daa824c..cfa05de 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Кубатын өчүрүү"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Тез жардам"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Ката тууралуу билдирүү"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Сеансты бүтүрүү"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Ката тууралуу билдирүү түзүү"</string>
<string name="bugreport_message" msgid="398447048750350456">"Ушуну менен түзмөгүңүздүн учурдагы абалы тууралуу маалымат топтолуп, электрондук почта аркылуу жөнөтүлөт. Отчет даяр болгуча бир аз күтө туруңуз."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Интерактивдүү кабар"</string>
@@ -1500,6 +1501,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн, анын кыска жолу күйгүзүлгөндө, үндү катуулатуу/акырындатуу баскычын үч секунддай кое бербей басып туруңуз.\n\n Учурдагы атайын мүмкүнчүлүктөрдүн жөндөөлөрү:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\nЖөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнөн өзгөртө аласыз."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Кыска жолду өчүрүү"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Кыска жолду колдонуу"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Түстү инверсиялоо"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Түсүн тууралоо"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Атайын мүмкүнчүлүктөр кыска жолу <xliff:g id="SERVICE_NAME">%1$s</xliff:g> кызматын күйгүздү"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Атайын мүмкүнчүлүктөр кыска жолу <xliff:g id="SERVICE_NAME">%1$s</xliff:g> кызматын өчүрдү"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Атайын мүмкүнчүлүктөр баскычын таптаганыңызда иштетиле турган функцияны тандаңыз:"</string>
@@ -1686,13 +1689,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Иш күндөрүнүн кечтери"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Дем алыш"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Иш-чара"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Уйку режими"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> тарабынан үнсүздөлдү"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Түзмөгүңүздө ички көйгөй бар жана ал баштапкы абалга кайтарылмайынча туруктуу иштебей коюшу мүмкүн."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Түзмөгүңүздө ички көйгөй бар. Анын чоо-жайын билүү үчүн өндүрүүчүңүзгө кайрылыңыз."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD сурамы DIAL сурамына өзгөртүлдү."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD сурамы SS сурамына өзгөртүлдү."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD сурамы жаңы USSD сурамына өзгөртүлдү."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD сурамы Видео DIAL сурамына өзгөртүлдү."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS сурамы DIAL сурамына өзгөртүлдү."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS сурамы Видео DIAL сурамына өзгөртүлдү."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS сурамы USSD сурамына өзгөртүлдү."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS сурамы жаңы SS сурамына өзгөртүлдү."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Жумуш профили"</string>
diff --git a/core/res/res/values-lo/required_apps_managed_device.xml b/core/res/res/values-lo/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-lo/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-lo/required_apps_managed_profile.xml b/core/res/res/values-lo/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-lo/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-lo/required_apps_managed_user.xml b/core/res/res/values-lo/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-lo/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 3cf1d1e..feedbf3 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"ປິດ"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"ສຸກເສີນ"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"ລາຍງານຂໍ້ຜິດພາດ"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"ສິ້ນສຸດເຊດຊັນ"</string>
<string name="bugreport_title" msgid="2667494803742548533">"ໃຊ້ລາຍງານຂໍ້ບົກພ່ອງ"</string>
<string name="bugreport_message" msgid="398447048750350456">"ນີ້ຈະເປັນການເກັບກຳຂໍ້ມູນກ່ຽວກັບ ສະຖານະປັດຈຸບັນຂອງອຸປະກອນທ່ານ ເພື່ອສົ່ງເປັນຂໍ້ຄວາມທາງອີເມວ. ມັນຈະໃຊ້ເວລາໜ້ອຍນຶ່ງ ໃນການເລີ່ມຕົ້ນການລາຍງານຂໍ້ຜິດພາດ ຈົນກວ່າຈະພ້ອມທີ່ຈະສົ່ງໄດ້, ກະລຸນາລໍຖ້າ."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ລາຍງານແບບໂຕ້ຕອບ"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"ເມື່ອເປີດໃຊ້ປຸ່ມລັດແລ້ວ, ໃຫ້ກົດປຸ່ມສຽງທັງສອງຄ້າງໄວ້ 3 ວິນາທີເພື່ອເລີ່ມຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ.\n\n ຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງປັດຈຸບັນ:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ທ່ານສາມາດປ່ຽນຄຸນສົມບັດໄດ້ໃນການຕັ້ງຄ່າ > ການຊ່ວຍເຂົ້າເຖິງ."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"ປິດປຸ່ມລັດ"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"ໃຊ້ປຸ່ມລັດ"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"ການປີ້ນສີ"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"ການແກ້ໄຂຄ່າສີ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> on"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Accessibility Shortcut turned <xliff:g id="SERVICE_NAME">%1$s</xliff:g> off"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ເລືອກຄຸນສົມບັດທີ່ຈະໃຊ້ເມື່ອທ່ານແຕະປຸ່ມການຊ່ວຍເຂົ້າເຖິງ:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"ຄ່ຳຄືນໃນອາທິດ"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"ທ້າຍອາທິດ"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"ການນັດໝາຍ"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"ການນອນ"</string>
<string name="muted_by" msgid="6147073845094180001">"ຖືກປິດສຽງໂດຍ <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"ມີບັນຫາພາຍໃນກັບອຸປະກອນຂອງທ່ານ, ແລະມັນອາດຈະບໍ່ສະຖຽນຈົນກວ່າທ່ານຕັ້ງເປັນຂໍ້ມູນໂຮງງານຄືນແລ້ວ."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"ມີບັນຫາພາຍໃນກັບອຸປະກອນຂອງທ່ານ. ຕິດຕໍ່ຜູ້ຜະລິດຂອງທ່ານສຳລັບລາຍລະອຽດຕ່າງໆ."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"ການຂໍ USSD ຖືກດັດແປງເປັນການຂໍ DIAL ແລ້ວ."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"ການຂໍ USSD ຖືກດັດແປງເປັນການຂໍ SS ແລ້ວ."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"ການຂໍ USSD ຖືກດັດແປງເປັນການຂໍ USSD ໃໝ່ແລ້ວ."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"ການຂໍ USSD ຖືກດັດແປງເປັນການຂໍ DIAL ວິດີໂອແລ້ວ."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"ການຂໍ SS ຖືກດັດແປງເປັນການຂໍ DIAL ແລ້ວ."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"ການຂໍ SS ຖືກດັດແປງເປັນການຂໍ DIAL ວິດີໂອແລ້ວ."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"ການຂໍ SS ຖືກດັດແປງເປັນການຂໍ USSD ແລ້ວ."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"ການຂໍ SS ຖືກດັດແປງເປັນການຂໍ SS ໃໝ່ແລ້ວ."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
diff --git a/core/res/res/values-lt/required_apps_managed_device.xml b/core/res/res/values-lt/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-lt/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-lt/required_apps_managed_profile.xml b/core/res/res/values-lt/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-lt/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-lt/required_apps_managed_user.xml b/core/res/res/values-lt/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-lt/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index e0d9771..95e4862 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -216,6 +216,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Išjungiamas maitinimas"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Skambutis pagalbos numeriu"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Pranešimas apie riktą"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Baigti seansą"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Pranešti apie riktą"</string>
<string name="bugreport_message" msgid="398447048750350456">"Bus surinkta informacija apie dabartinę įrenginio būseną ir išsiųsta el. pašto pranešimu. Šiek tiek užtruks, kol pranešimas apie riktą bus paruoštas siųsti; būkite kantrūs."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interakt. ataskaita"</string>
@@ -1546,6 +1547,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Kai spartusis klavišas įjungtas, spaudžiant abu garsumo mygtukus 3 sekundes bus paleista pritaikymo neįgaliesiems funkcija.\n\n Dabartinė pritaikymo neįgaliesiems funkcija:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>„\n“\n Funkciją galite pakeisti skiltyje „Nustatymai“ > „Pritaikymas neįgaliesiems“."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Išjungti spartųjį klavišą"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Naudoti spartųjį klavišą"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Spalvų inversija"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Spalvų taisymas"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Pritaikymo neįgaliesiems sparčiuoju klavišu buvo įjungta „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Pritaikymo neįgaliesiems sparčiuoju klavišu buvo išjungta „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Pasirinkite funkciją, kuri bus naudojama, kai paliesite pritaikymo neįgaliesiems mygtuką:"</string>
@@ -1750,13 +1753,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Darbo dienos vakarą"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Savaitgalį"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Įvykis"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Miegas"</string>
<string name="muted_by" msgid="6147073845094180001">"Nutildė <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Iškilo vidinė su jūsų įrenginiu susijusi problema, todėl įrenginys gali veikti nestabiliai, kol neatkursite gamyklinių duomenų."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Iškilo vidinė su jūsų įrenginiu susijusi problema. Jei reikia išsamios informacijos, susisiekite su gamintoju."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD užklausa pakeista į DIAL užklausą."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD užklausa pakeista į SS užklausą."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD užklausa pakeista į naują USSD užklausą."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD užklausa pakeista į „Video DIAL“ užklausą."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS užklausa pakeista į DIAL užklausą."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS užklausa pakeista į „Video DIAL“ užklausą."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS užklausa pakeista į USSD užklausą."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS užklausa pakeista į naują SS užklausą."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Darbo profilis"</string>
diff --git a/core/res/res/values-lv/required_apps_managed_device.xml b/core/res/res/values-lv/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-lv/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-lv/required_apps_managed_profile.xml b/core/res/res/values-lv/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-lv/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-lv/required_apps_managed_user.xml b/core/res/res/values-lv/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-lv/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 573baf3..12bb04b 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -214,6 +214,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Strāvas padeve ir izslēgta."</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Ārkārtas"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Kļūdu ziņojums"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Beigt sesiju"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Kļūdu ziņojuma sagatavošana"</string>
<string name="bugreport_message" msgid="398447048750350456">"Veicot šo darbību, tiks apkopota informācija par jūsu ierīces pašreizējo stāvokli un nosūtīta e-pasta ziņojuma veidā. Kļūdu ziņojuma pabeigšanai un nosūtīšanai var būt nepieciešams laiks. Lūdzu, esiet pacietīgs."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktīvs pārskats"</string>
@@ -1522,6 +1523,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Ja saīsne ir iespējota, vienlaikus nospiežot abas skaļuma regulēšanas pogas un trīs sekundes turot tās, tiks palaista pieejamības funkcija.\n\n Pašreiz iestatītā pieejamības funkcija:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Šo funkciju var mainīt sadaļā Iestatījumi > Pieejamība."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Izslēgt saīsni"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Izmantot saīsni"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Krāsu inversija"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Krāsu korekcija"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Pieejamības saīsne aktivizēja lietotni <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Pieejamības saīsne deaktivizēja lietotni <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Izvēlieties funkciju, ko izmantot, kad pieskaraties pogai Pieejamība."</string>
@@ -1717,13 +1720,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Darbadienas vakarā"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Nedēļas nogalē"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Pasākums"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Gulēšana"</string>
<string name="muted_by" msgid="6147073845094180001">"Skaņu izslēdza <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Jūsu ierīcē ir radusies iekšēja problēma, un ierīce var darboties nestabili. Lai to labotu, veiciet rūpnīcas datu atiestatīšanu."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Jūsu ierīcē ir radusies iekšēja problēma. Lai iegūtu plašāku informāciju, lūdzu, sazinieties ar ražotāju."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD pieprasījums ir mainīts uz DIAL pieprasījumu."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD pieprasījums ir mainīts uz SS pieprasījumu."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD pieprasījums ir mainīts uz jaunu USSD pieprasījumu."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD pieprasījums ir mainīts uz Video DIAL pieprasījumu."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS pieprasījums ir mainīts uz DIAL pieprasījumu."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS pieprasījums ir mainīts uz Video DIAL pieprasījumu."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS pieprasījums ir mainīts uz USSD pieprasījumu."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS pieprasījums ir mainīts uz jaunu SS pieprasījumu."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Darba profils"</string>
diff --git a/core/res/res/values-mk/required_apps_managed_device.xml b/core/res/res/values-mk/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-mk/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mk/required_apps_managed_profile.xml b/core/res/res/values-mk/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-mk/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mk/required_apps_managed_user.xml b/core/res/res/values-mk/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-mk/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index d3fa3d1..c5773f8 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Исклучи"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Итен случај"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Извештај за грешка"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Завршете ја сесијата"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Земи извештај за грешки"</string>
<string name="bugreport_message" msgid="398447048750350456">"Ова ќе собира информации за моменталната состојба на вашиот уред, за да ги испрати како порака по е-пошта. Тоа ќе одземе малку време почнувајќи од извештајот за грешки додека не се подготви за праќање; бидете трпеливи."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Интерактивен извештај"</string>
@@ -1501,6 +1502,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Кога е вклучена кратенката, ако ги притиснете двете копчиња за јачина на звук во времетраење од 3 секунди, ќе се стартува функција на пристапност.\n\n Тековна функција на пристапност:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Функцијата може да ја промените во „Поставки“ > „Пристапност“."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Исклучи ја кратенката"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Користи кратенка"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Инверзија на бои"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Корекција на бои"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Кратенката за пристапност ја вклучи <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Кратенката за пристапност ја исклучи <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Изберете функција за користење кога ќе го допрете копчето за „Пристапност“."</string>
@@ -1687,13 +1690,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Вечер од седмицата"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Викенд"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Настан"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Спиење"</string>
<string name="muted_by" msgid="6147073845094180001">"Звукот го исклучи <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Настана внатрешен проблем со уредот и може да биде нестабилен сè додека не ресетирате на фабричките податоци."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Настана внатрешен проблем со уредот. Контактирајте го производителот за детали."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Барањето USSD е изменето во барање DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Барањето USSD е изменето во барање SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Барањето USSD е изменето во ново барање USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Барањето USSD е изменето во барање видео DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Барањето SS е изменето во барање DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Барањето SS е изменето во барање видео DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Барањето SS е изменето во барање USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Барањето SS е изменето во ново барање SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Работен профил"</string>
diff --git a/core/res/res/values-ml/required_apps_managed_device.xml b/core/res/res/values-ml/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-ml/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ml/required_apps_managed_profile.xml b/core/res/res/values-ml/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-ml/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ml/required_apps_managed_user.xml b/core/res/res/values-ml/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-ml/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 5f9f137..fc958fa 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"പവർ ഓഫാക്കുക"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"അടിയന്തിരാവശ്യം"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"ബഗ് റിപ്പോർട്ട്"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"സെഷൻ അവസാനിപ്പിക്കുക"</string>
<string name="bugreport_title" msgid="2667494803742548533">"ബഗ് റിപ്പോർട്ട് എടുക്കുക"</string>
<string name="bugreport_message" msgid="398447048750350456">"ഒരു ഇമെയിൽ സന്ദേശമായി അയയ്ക്കുന്നതിന്, ഇത് നിങ്ങളുടെ നിലവിലെ ഉപകരണ നിലയെക്കുറിച്ചുള്ള വിവരങ്ങൾ ശേഖരിക്കും. ബഗ് റിപ്പോർട്ട് ആരംഭിക്കുന്നതിൽ നിന്ന് ഇത് അയയ്ക്കാനായി തയ്യാറാകുന്നതുവരെ അൽപ്പസമയമെടുക്കും; ക്ഷമയോടെ കാത്തിരിക്കുക."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ഇന്റരാക്റ്റീവ് റിപ്പോർട്ട്"</string>
@@ -1499,6 +1500,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"കുറുക്കുവഴി ഓണാണെങ്കിൽ, രണ്ട് വോളിയം ബട്ടണുകളും 3 സെക്കൻഡ് നേരത്തേക്ക് അമർത്തുന്നത് ഉപയോഗസഹായി ഫീച്ചർ ആരംഭിക്കും.\n\n നിലവിലെ ഉപയോഗസഹായി ഫീച്ചർ:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ക്രമീകരണം > ഉപയോഗസഹായി എന്നതിൽ ഏത് സമയത്തും നിങ്ങൾക്ക് ഫീച്ചർ മാറ്റാവുന്നതാണ്."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"കുറുക്കുവഴി ഓഫാക്കുക"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"കുറുക്കുവഴി ഉപയോഗിക്കുക"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"വർണ്ണ വിപര്യയം"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"വർണ്ണം ക്രമീകരിക്കൽ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ഉപയോഗസഹായിക്കുള്ള കുറുക്കുവഴി <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓൺ ചെയ്തിരിക്കുന്നു"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ഉപയോഗസഹായിക്കുള്ള കുറുക്കുവഴി <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓഫ് ചെയ്തിരിക്കുന്നു"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"നിങ്ങൾ ഉപയോഗസഹായി ബട്ടൺ ടാപ്പുചെയ്യുമ്പോൾ ഉപയോഗിക്കുന്നതിന് ഒരു ഫീച്ചർ തിരഞ്ഞെടുക്കുക:"</string>
@@ -1685,13 +1688,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"പ്രവൃത്തിദിനരാവ്"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"വാരാന്ത്യം"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"ഇവന്റ്"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"ഉറക്കം"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>, മ്യൂട്ടുചെയ്തു"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"നിങ്ങളുടെ ഉപകരണത്തിൽ ഒരു ആന്തരിക പ്രശ്നമുണ്ട്, ഫാക്ടറി വിവര പുനഃസജ്ജീകരണം ചെയ്യുന്നതുവരെ ഇതു അസ്ഥിരമായിരിക്കാനിടയുണ്ട്."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"നിങ്ങളുടെ ഉപകരണത്തിൽ ഒരു ആന്തരിക പ്രശ്നമുണ്ട്. വിശദാംശങ്ങൾക്കായി നിർമ്മാതാവിനെ ബന്ധപ്പെടുക."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD അഭ്യർത്ഥന, DIAL അഭ്യർത്ഥനയായി പരിഷ്ക്കരിച്ചു."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD അഭ്യർത്ഥന, SS അഭ്യർത്ഥനയായി പരിഷ്ക്കരിച്ചു."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD അഭ്യർത്ഥന, പുതിയ USSD അഭ്യർത്ഥനയായി പരിഷ്ക്കരിച്ചു."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD അഭ്യർത്ഥന, വീഡിയോ DIAL അഭ്യർത്ഥനയായി പരിഷ്ക്കരിച്ചു."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS അഭ്യർത്ഥന, DIAL അഭ്യർത്ഥനയായി പരിഷ്ക്കരിച്ചു."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS അഭ്യർത്ഥന, വീഡിയോ DIAL അഭ്യർത്ഥനയായി പരിഷ്ക്കരിച്ചു."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS അഭ്യർത്ഥന, USSD അഭ്യർത്ഥനയായി പരിഷ്ക്കരിച്ചു."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS അഭ്യർത്ഥന, പുതിയ SS അഭ്യർത്ഥനയായി പരിഷ്ക്കരിച്ചു."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
diff --git a/core/res/res/values-mn/required_apps_managed_device.xml b/core/res/res/values-mn/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-mn/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mn/required_apps_managed_profile.xml b/core/res/res/values-mn/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-mn/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mn/required_apps_managed_user.xml b/core/res/res/values-mn/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-mn/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 16681dc..9f26a3b 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Унтраах"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Яаралтай тусламж"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Алдаа мэдээлэх"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Гаргах харилцан үйлдэл"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Согог репорт авах"</string>
<string name="bugreport_message" msgid="398447048750350456">"Энэ таны төхөөрөмжийн одоогийн статусын талаарх мэдээллийг цуглуулах ба имэйл мессеж болгон илгээнэ. Алдааны мэдэгдлээс эхэлж илгээхэд бэлэн болоход хэсэг хугацаа зарцуулагдана тэвчээртэй байна уу."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Интерактив тайлан"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Товчлолыг асаасан үед дуун товчлуурыг 3 секунд дарснаар хүртээмжийн онцлогийг эхлүүлнэ.\n\n Одоогийн хүртээмжийн онцлог:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Онцлогийг Тохиргоо > Хүртээмж хэсэгт өөрчлөх боломжтой."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Товчлолыг унтраах"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Товчлол ашиглах"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Өнгө хувиргалт"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Өнгөний засвар"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Хүртээмжийн товчлол <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г асаасан"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Хүртээмжийн товчлол <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г унтраасан"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Хүртээмжийн товчлуурыг товших үедээ ашиглах онцлогийг сонгоно уу:"</string>
@@ -1682,13 +1685,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Ажлын өдрийн шөнө"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Амралтын өдөр"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Үйл явдал"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Идэвхгүй"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>-с хаасан"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Таны төхөөрөмжид дотоод алдаа байна.Та төхөөрөмжөө үйлдвэрээс гарсан төлөвт шилжүүлэх хүртэл таны төхөөрөмж чинь тогтворгүй байж болох юм."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Таны төхөөрөмжид дотоод алдаа байна. Дэлгэрэнгүй мэдээлэл авахыг хүсвэл үйлдвэрлэгчтэйгээ холбоо барина уу."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD хүсэлтийг DIAL хүсэлт болгон өөрчилсөн байна."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD хүсэлтийг SS хүсэлт болгон өөрчилсөн байна."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD хүсэлтийг шинэ USSD хүсэлт болгон өөрчилсөн байна."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD хүсэлтийг Видео DIAL хүсэлт болгон өөрчилсөн байна."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS хүсэлтийг DIAL хүсэлт болгон өөрчилсөн байна"</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS хүсэлтийг Видео DIAL хүсэлт болгон өөрчилсөн байна."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS хүсэлтийг USSD хүсэлт болгон өөрчилсөн байна."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS хүсэлтийг шинэ SS хүсэлт болгон өөрчилсөн байна."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Ажлын профайл"</string>
diff --git a/core/res/res/values-mr/required_apps_managed_device.xml b/core/res/res/values-mr/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-mr/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mr/required_apps_managed_profile.xml b/core/res/res/values-mr/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-mr/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mr/required_apps_managed_user.xml b/core/res/res/values-mr/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-mr/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index bd00d46..a757849 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -144,7 +144,7 @@
<string name="httpErrorAuth" msgid="1435065629438044534">"प्रमाणीकृत करू शकलो नाही."</string>
<string name="httpErrorProxyAuth" msgid="1788207010559081331">"प्रॉक्सी सर्व्हरद्वारे प्रमाणीकरण यशस्वी झाले."</string>
<string name="httpErrorConnect" msgid="8714273236364640549">"सर्व्हरशी कनेक्ट करू शकलो नाही."</string>
- <string name="httpErrorIO" msgid="2340558197489302188">"या सर्व्हरशी संप्रेषण करू शकलो नाही. नंतर पुन्हा प्रयत्न करा."</string>
+ <string name="httpErrorIO" msgid="2340558197489302188">"या सर्व्हरशी संवाद प्रस्थापित करू शकलो नाही. नंतर पुन्हा प्रयत्न करा."</string>
<string name="httpErrorTimeout" msgid="4743403703762883954">"सर्व्हरवरील कनेक्शन टाइमआउट झाले."</string>
<string name="httpErrorRedirectLoop" msgid="8679596090392779516">"पृष्ठामध्ये बरीच सर्व्हर पुनर्निर्देशने आहेत."</string>
<string name="httpErrorUnsupportedScheme" msgid="5015730812906192208">"प्रोटोकॉल समर्थित नाही."</string>
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"बंद"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"आणीबाणी"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"बग रीपोर्ट"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"सेशन समाप्त करा"</string>
<string name="bugreport_title" msgid="2667494803742548533">"बग रीपोर्ट घ्या"</string>
<string name="bugreport_message" msgid="398447048750350456">"ई-मेल संदेश म्हणून पाठविण्यासाठी, हे तुमच्या सद्य डिव्हाइस स्थितीविषयी माहिती संकलित करेल. बग रीपोर्ट सुरू करण्यापासून तो पाठविण्यापर्यंत थोडा वेळ लागेल; कृपया धीर धरा."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"परस्परसंवादी अहवाल"</string>
@@ -361,13 +362,13 @@
<string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"रोचक प्रसारणे पाठविण्यास अॅपला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर तसेच रहाते. अतिरिक्त वापर टीव्ही धीमा किंवा यासाठी बरीच मेमरी वापरली जात असल्यामुळे तो अस्थिर करू शकतो."</string>
<string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"रोचक प्रसारणे पाठविण्यासाठी अॅप ला अनुमती देते, जे प्रसारण समाप्त झाल्यानंतर देखील तसेच राहते. अत्याधिक वापरामुळे बरीच मेमरी वापरली जाऊन तो फोनला धीमा किंवा अस्थिर करू शकतो."</string>
<string name="permlab_readContacts" msgid="8348481131899886131">"आपले संपर्क वाचा"</string>
- <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"आपण कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संप्रेषण केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर संचयित केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी आपला संपर्क डेटा जतन करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा सामायिक करू शकतात."</string>
+ <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"आपण कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर संचयित केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी आपला संपर्क डेटा जतन करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा सामायिक करू शकतात."</string>
<string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"आपण विशिष्ट लोकांना इतर मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संप्रेषित केलेल्या फ्रिक्वेन्सीसह, आपल्या टीव्हीवर संचयित केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप्सला अनुमती देतात. ही परवागनी अॅप्सला आपला संपर्क डेटा जतन करण्यासाठी अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्याला न कळविता संपर्क डेटा सामायिक करू शकतात."</string>
- <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"आपण कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संप्रेषण केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या फोनवर संचयित केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी आपला संपर्क डेटा जतन करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा सामायिक करू शकतात."</string>
+ <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"आपण कॉल केलेल्या, ईमेल केलेल्या किंवा विशिष्ट लोकांशी अन्य मार्गांनी संवाद प्रस्थापित केलेल्या लोकांच्या फ्रिक्वेन्सीसह, आपल्या फोनवर संचयित केलेल्या आपल्या संपर्कांविषयीचा डेटा वाचण्यासाठी अॅप ला अनुमती देते. ही परवानगी आपला संपर्क डेटा जतन करण्याची अॅप्स ला अनुमती देते आणि दुर्भावनापूर्ण अॅप्स आपल्या माहितीशिवाय संपर्क डेटा सामायिक करू शकतात."</string>
<string name="permlab_writeContacts" msgid="5107492086416793544">"आपले संपर्क सुधारित करा"</string>
- <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"आपण विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संप्रेषण केलेल्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर संचयित केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string>
- <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"आपण विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संप्रेषण केलेल्या फ्रिक्वेन्सीसह, आपल्या टीव्हीवर संचयित केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅपला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅपला अनुमती देते."</string>
- <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"आपण विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संप्रेषण केलेल्या फ्रिक्वेन्सीसह, आपल्या फोनवर संचयित केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"आपण विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या टॅब्लेटवर संचयित केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string>
+ <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"आपण विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या टीव्हीवर संचयित केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅपला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅपला अनुमती देते."</string>
+ <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"आपण विशिष्ट संपर्कांशी अन्य मार्गांनी कॉल केलेल्या, ईमेल केलेल्या किंवा संवाद प्रस्थापित केलेल्या फ्रिक्वेन्सीसह, आपल्या फोनवर संचयित केलेल्या आपल्या संपर्कांविषयीचा डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. ही परवानगी संपर्क डेटा हटविण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_readCallLog" msgid="3478133184624102739">"कॉल लॉग वाचा"</string>
<string name="permdesc_readCallLog" msgid="3204122446463552146">"हा अॅप आपला कॉल इतिहास वाचू शकता."</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"कॉल लॉग लिहा"</string>
@@ -1051,8 +1052,7 @@
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"सिस्टम सेटिंग्ज > Apps > डाउनलोड केलेले मध्ये हे पुन्हा-सक्षम करा."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> वर्तमान डिस्प्ले आकार सेटिंगला समर्थन देत नाही आणि अनपेक्षित वर्तन करू शकते."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"नेहमी दर्शवा"</string>
- <!-- no translation found for unsupported_compile_sdk_message (4253168368781441759) -->
- <skip />
+ <string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> हे Android OS च्या विसंगत आवृत्तीसाठी तयार केले होते आणि ते अनपेक्षित पद्धतीने काम करू शकते. अॅपची अपडेट केलेली आवृत्ती उपलब्ध असू शकते."</string>
<string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"नेहमी दर्शवा"</string>
<string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"अपडेट आहे का ते तपासा"</string>
<string name="smv_application" msgid="3307209192155442829">"अॅप <xliff:g id="APPLICATION">%1$s</xliff:g> (प्रक्रिया <xliff:g id="PROCESS">%2$s</xliff:g>) ने तिच्या स्वयं-लागू केलेल्या StrictMode धोरणाचे उल्लंघन केले आहे."</string>
@@ -1500,6 +1500,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"शॉर्टकट चालू असताना, दोन्ही आवाज बटणे 3 सेकंद दाबल्याने प्रवेशयोग्यता वैशिष्ट्य सुरू होईल.\n\n वर्तमान प्रवेशयोग्यता वैशिष्ट्य:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n आपण सेटिंग्ज > प्रवेशयोग्यता मध्ये वैशिष्ट्य बदलू शकता."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"शॉर्टकट बंद करा"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"शॉर्टकट वापरा"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"रंगांची उलटापालट"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"रंग सुधारणा"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"प्रवेशयोग्यता शॉर्टकटने <xliff:g id="SERVICE_NAME">%1$s</xliff:g> चालू केली"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"प्रवेशयोग्यता शॉर्टकटने <xliff:g id="SERVICE_NAME">%1$s</xliff:g> बंद केली"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"आपण प्रवेशयोग्यता बटण दाबल्यावर वापरण्यासाठी वैशिष्ट्य निवडा:"</string>
@@ -1639,8 +1641,7 @@
<string name="package_installed_device_owner" msgid="6875717669960212648">"आपल्या प्रशासकाने इंस्टॉल केले"</string>
<string name="package_updated_device_owner" msgid="1847154566357862089">"आपल्या प्रशासकाने अपडेट केले"</string>
<string name="package_deleted_device_owner" msgid="2307122077550236438">"आपल्या प्रशासकाने हटवले"</string>
- <!-- no translation found for battery_saver_description (5394663545060026162) -->
- <skip />
+ <string name="battery_saver_description" msgid="5394663545060026162">"बॅटरी लाइफ सुधारण्यासाठी, बॅटरी सेव्हर तुमच्या डिव्हाइस ची कामगिरी कमी करतो आणि कंपन, स्थान सेवा आणि बराचसा पार्श्वभूमी डेटा मर्यादित करतो. सिंकवर अवलंबून असणारे ईमेल, मेसेजिंग आणि इतर अॅप्स तुम्ही उघडल्याशिवाय अपडेट होऊ शकत नाहीत.\n\nतुमचे डिव्हाइस चार्ज होत असते तेव्हा बॅटरी सेव्हर आपोआप बंद होतो."</string>
<string name="data_saver_description" msgid="6015391409098303235">"डेटा वापर कमी करण्यात मदत करण्यासाठी, डेटा सर्व्हर काही अॅप्सना पार्श्वभूमीमध्ये डेटा पाठविण्यास किंवा प्राप्त करण्यास प्रतिबंधित करतो. आपण सध्या वापरत असलेला अॅप डेटामध्ये प्रवेश करू शकतो परंतु तसे तो खूप कमी वेळा करू शकतो. याचा अर्थ, उदाहरणार्थ, आपण इमेज टॅप करेपर्यंत त्या प्रदर्शित करणार नाहीत असा असू शकतो."</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"डेटा बचतकर्ता चालू करायचा?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"चालू करा"</string>
@@ -1687,13 +1688,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"आठवड्याची शेवटची रात्र"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"आठवड्याच्या शेवटी"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"इव्हेंट"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"झोपलेले आहे"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> द्वारे नि:शब्द केले"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"आपल्या डिव्हाइसमध्ये अंतर्गत समस्या आहे आणि आपला फॅक्टरी डेटा रीसेट होईपर्यंत ती अस्थिर असू शकते."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"आपल्या डिव्हाइसमध्ये अंतर्गत समस्या आहे. तपशीलांसाठी आपल्या निर्मात्याशी संपर्क साधा."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD विनंती डायल विनंतीवर सुधारित केली आहे."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD विनंती SS विनंतीवर सुधारित केली आहे."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD विनंती नवीन USSD विनंतीवर सुधारित केली आहे."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD विनंतीमध्ये बदल करून ती व्हिडिओ डायल विनंतीमध्ये बदलली आहे."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS विनंती डायल विनंतीवर सुधारित केली आहे."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS विनंतीमध्ये बदल करून ती व्हिडिओ डायल विनंतीमध्ये बदलली आहे."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS विनंती USSD विनंतीवर सुधारित केली आहे."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS विनंती नवीन SS विनंतीवर सुधारित केली आहे."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"कार्य प्रोफाईल"</string>
@@ -1749,7 +1753,7 @@
<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>
+ <string name="app_category_social" msgid="5842783057834965912">"सामाजिक आणि संवाद प्रस्थापित"</string>
<string name="app_category_news" msgid="7496506240743986873">"बातम्या आणि मासिके"</string>
<string name="app_category_maps" msgid="5878491404538024367">"नकाशे आणि नेव्हिगेशन"</string>
<string name="app_category_productivity" msgid="3742083261781538852">"उत्पादनक्षमता"</string>
diff --git a/core/res/res/values-ms/required_apps_managed_device.xml b/core/res/res/values-ms/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-ms/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ms/required_apps_managed_profile.xml b/core/res/res/values-ms/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-ms/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ms/required_apps_managed_user.xml b/core/res/res/values-ms/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-ms/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 26837b3..aee2000 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Matikan kuasa"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Kecemasan"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Laporan pepijat"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Tamatkan sesi"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Ambil laporan pepijat"</string>
<string name="bugreport_message" msgid="398447048750350456">"Ini akan mengumpul maklumat tentang keadaan peranti semasa anda untuk dihantarkan sebagai mesej e-mel. Harap bersabar, mungkin perlu sedikit masa untuk memulakan laporan sehingga siap untuk dihantar."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Laporan interaktif"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Apabila pintasan dihidupkan, tindakan menekan kedua-dua butang kelantangan selama 3 saat akan memulakan ciri kebolehaksesan.\n\n Ciri kebolehaksesan semasa:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Anda boleh menukar ciri itu dalam Tetapan > Kebolehaksesan."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Matikan pintasan"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Gunakan Pintasan"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Penyongsangan Warna"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Pembetulan Warna"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Pintasan kebolehaksesan menghidupkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Pintasan Kebolehaksesan mematikan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Pilih ciri yang hendak digunakan apabila anda mengetik butang Kebolehaksesan:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Malam selain hujung minggu"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Hujung minggu"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Acara"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Tidur"</string>
<string name="muted_by" msgid="6147073845094180001">"Diredam oleh <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Terdapat masalah dalaman dengan peranti anda. Peranti mungkin tidak stabil sehingga anda membuat tetapan semula data kilang."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Terdapat masalah dalaman dengan peranti anda. Hubungi pengilang untuk mengetahui butirannya."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Permintaan USSD diubah kepada permintaan DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Permintaan USSD diubah kepada permintaan SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Permintaan USSD diubah kepada permintaan USSD baharu."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Permintaan USSD diubah suai kepada permintaan DAIL Video."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Permintaan SS diubah kepada permintaan DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Permintaan SS diubah suai kepada permintaan DAIL Video."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Permintaan SS diubah kepada permintaan USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Permintaan SS diubah kepada permintaan SS baharu."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profil kerja"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 0a2118e..fec37ab 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"ပါဝါပိတ်ရန်"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"အရေးပေါ်"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်း"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"သတ်မှတ်ပေးထားသည့်အချိန် ပြီးဆုံးပြီ"</string>
<string name="bugreport_title" msgid="2667494803742548533">"အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်းအား ယူရန်"</string>
<string name="bugreport_message" msgid="398447048750350456">"သင့်ရဲ့ လက်ရှိ စက်အခြေအနေ အချက်အလက်များကို အီးမေးလ် အနေဖြင့် ပေးပို့ရန် စုဆောင်းပါမည်။ အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်းမှ ပေးပို့ရန် အသင့်ဖြစ်သည်အထိ အချိန် အနည်းငယ်ကြာမြင့်မှာ ဖြစ်သဖြင့် သည်းခံပြီး စောင့်ပါရန်"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"လက်ငင်းတုံ့ပြန်နိုင်သည့် အစီရင်ခံချက်"</string>
@@ -1499,6 +1500,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"ဖြတ်လမ်းလင့်ခ်ကို ဖွင့်ထားစဉ် အသံအတိုးအလျှော့ခလုတ် နှစ်ခုစလုံးကို ၃ စက္ကန့်ခန့် ဖိထားခြင်းဖြင့် အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုကို ဖွင့်နိုင်သည်။\n\n လက်ရှိ အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှု−\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ဝန်ဆောင်မှုကို ဆက်တင်များ > အများသုံးစွဲနိုင်မှုတွင် ပြောင်းလဲနိုင်ပါသည်။"</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"ဖြတ်လမ်းလင့်ခ်ကို ပိတ်ရန်"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"ဖြတ်လမ်းလင့်ခ်ကို သုံးရန်"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"အရောင် ပြောင်းပြန်လှန်ခြင်း"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"အရောင်ပြင်ဆင်ခြင်း"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"အများသုံးစွဲနိုင်မှု ဖြတ်လမ်းလင့်ခ်သည် <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ကို ဖွင့်လိုက်ပါသည်"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"အများသုံးစွဲနိုင်မှု ဖြတ်လမ်းလင့်ခ်သည် <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ကို ပိတ်လိုက်ပါသည်"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"အများသုံးစွဲနိုင်မှု ခလုတ်ကို တို့သည့်အခါ အသုံးပြုမည့် ဝန်ဆောင်မှုကို ရွေးချယ်ပါ−"</string>
@@ -1685,13 +1688,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"ကြားရက်ည"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"စနေ၊ တနင်္ဂနွေ"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"ဖြစ်ရပ်"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"အိပ်နေချိန်"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> အသံပိတ်သည်"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"သင့်ကိရိယာအတွင်းပိုင်းတွင် ပြဿနာရှိနေပြီး၊ မူလစက်ရုံထုတ်အခြေအနေအဖြစ် ပြန်လည်ရယူနိုင်သည်အထိ အခြေအနေမတည်ငြိမ်နိုင်ပါ။"</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"သင့်ကိရိယာအတွင်းပိုင်းတွင် ပြဿနာရှိနေ၏။ အသေးစိတ်သိရန်အတွက် ပစ္စည်းထုတ်လုပ်သူအား ဆက်သွယ်ပါ။"</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"DIAL တောင်းဆိုချက်အရ USSD တောင်းဆိုချက်အား ပြင်ဆင်ထား၏။"</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"SS တောင်းဆိုချက် အရ USSD တောင်းဆိုချက်အား ပြင်ဆင်ထား၏။"</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD တောင်းဆိုချက် အသစ်အရ USSD တောင်းဆိုချက်အား ပြင်ဆင်ထား၏။"</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD တောင်းဆိုချက်အား Video DIAL တောင်းဆိုချက်သို့ ပြုပြင်ထားသည်။"</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"DIAL တောင်းဆိုချက်အရ SS တောင်းဆိုချက်အား ပြင်ဆင်ထား၏။"</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS တောင်းဆိုချက်အား Video DIAL တောင်းဆိုချက်သို့ ပြုပြင်ထားသည်။"</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"USSD တောင်းဆိုချက်အရ SS တောင်းဆိုချက်အား ပြင်ဆင်ထား၏။"</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS တောင်းဆိုချက်အရ SS တောင်းဆိုချက်အား ပြင်ဆင်ထား၏။"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"အလုပ်ကိုယ်ရေးအချက်အလက်"</string>
diff --git a/core/res/res/values-nb/required_apps_managed_device.xml b/core/res/res/values-nb/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-nb/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-nb/required_apps_managed_profile.xml b/core/res/res/values-nb/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-nb/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-nb/required_apps_managed_user.xml b/core/res/res/values-nb/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-nb/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 98535c5..915701c 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Slå av"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Nødsituasjon"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Feilrapport"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Avslutt økten"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Utfør feilrapport"</string>
<string name="bugreport_message" msgid="398447048750350456">"Informasjon om tilstanden til enheten din samles inn og sendes som en e-post. Det tar litt tid fra du starter feilrapporten til e-posten er klar, så vær tålmodig."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktiv rapport"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Når snarveien er på, starter en tilgjengelighetsfunksjon når du trykker inn begge volumknappene i tre sekunder.\n\n Nåværende tilgjengelighetsfunksjon:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Du kan endre funksjonen i Innstillinger > Tilgjengelighet."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Slå av snarveien"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Bruk snarveien"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Fargeinvertering"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Fargekorrigering"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Snarveien for tilgjengelighet slo på <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Snarveien for tilgjengelighet slo av <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Velg en funksjon du vil bruke når du trykker på Tilgjengelighet-knappen:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Hverdagskveld"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Helg"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Aktivitet"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Sover"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> har kuttet lyden"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Det har oppstått et internt problem på enheten din, og den kan være ustabil til du tilbakestiller den til fabrikkdata."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Det har oppstått et internt problem på enheten din. Ta kontakt med produsenten for mer informasjon."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD-forespørselen er endret til en RINGE-forespørsel."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD-forespørselen er endret til en SS-forespørsel."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD-forespørsel er endret til en ny USSD-forespørsel."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD-forespørselen er endret til en RINGE-forespørsel med video."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-forespørselen er endret til en RINGE-forespørsel."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS-forespørselen er endret til en RINGE-forespørsel med video."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-forespørselen er endret til en USSD-forespørsel."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-forespørselen er endret til en ny SS-forespørsel."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Arbeidsprofil"</string>
diff --git a/core/res/res/values-ne/required_apps_managed_device.xml b/core/res/res/values-ne/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-ne/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ne/required_apps_managed_profile.xml b/core/res/res/values-ne/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-ne/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ne/required_apps_managed_user.xml b/core/res/res/values-ne/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-ne/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index f9016de..f0a45a3 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"बन्द गर्नुहोस्"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"आपतकालीन"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"बग रिपोर्ट"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"सत्रको अन्त्य गर्नुहोस्"</string>
<string name="bugreport_title" msgid="2667494803742548533">"बग रिपोर्ट लिनुहोस्"</string>
<string name="bugreport_message" msgid="398447048750350456">"एउटा इमेल सन्देशको रूपमा पठाउनलाई यसले तपाईँको हालैको उपकरणको अवस्थाको बारेमा सूचना जम्मा गर्ने छ। बग रिपोर्ट सुरु गरेदेखि पठाउन तयार नभएसम्म यसले केही समय लिन्छ; कृपया धैर्य गर्नुहोस्।"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"अन्तरक्रियामूलक रिपोर्ट"</string>
@@ -1231,8 +1232,8 @@
<string name="ext_media_unmountable_notification_message" msgid="2343202057122495773">"<xliff:g id="NAME">%s</xliff:g> त्रुटिपूर्ण छ। समाधान गर्न ट्याप गर्नुहोस्।"</string>
<string name="ext_media_unmountable_notification_message" product="tv" msgid="3941179940297874950">"<xliff:g id="NAME">%s</xliff:g> बिग्रेको छ। समाधान गर्न चयन गर्नुहोस्।"</string>
<string name="ext_media_unsupported_notification_title" msgid="3797642322958803257">"असमर्थित <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="ext_media_unsupported_notification_message" msgid="6121601473787888589">"यस यन्त्रले यस <xliff:g id="NAME">%s</xliff:g> लाई समर्थन गर्दैन। एक समर्थित ढाँचामा सेट अप गर्न ट्याप गर्नुहोस्।"</string>
- <string name="ext_media_unsupported_notification_message" product="tv" msgid="3725436899820390906">"यो यन्त्रले यस <xliff:g id="NAME">%s</xliff:g> लाई समर्थन गर्दैन। एक समर्थित ढाँचामा सेट अप गर्न चयन गर्नुहोस्।"</string>
+ <string name="ext_media_unsupported_notification_message" msgid="6121601473787888589">"यस यन्त्रले यस <xliff:g id="NAME">%s</xliff:g> लाई समर्थन गर्दैन। एक समर्थित ढाँचामा सेटअप गर्न ट्याप गर्नुहोस्।"</string>
+ <string name="ext_media_unsupported_notification_message" product="tv" msgid="3725436899820390906">"यो यन्त्रले यस <xliff:g id="NAME">%s</xliff:g> लाई समर्थन गर्दैन। एक समर्थित ढाँचामा सेटअप गर्न चयन गर्नुहोस्।"</string>
<string name="ext_media_badremoval_notification_title" msgid="3206248947375505416">"<xliff:g id="NAME">%s</xliff:g> अप्रत्याशित रूपमा निकालियो"</string>
<string name="ext_media_badremoval_notification_message" msgid="380176703346946313">"डेटा हराउनबाट जोगाउन निकाल्नु अघि <xliff:g id="NAME">%s</xliff:g> अनमाउन्ट गर्नुहोस्"</string>
<string name="ext_media_nomedia_notification_title" msgid="1704840188641749091">"निकालियो <xliff:g id="NAME">%s</xliff:g>"</string>
@@ -1317,7 +1318,7 @@
<string name="car_mode_disable_notification_title" msgid="3164768212003864316">"कार मोड सक्षम पारियो।"</string>
<string name="car_mode_disable_notification_message" msgid="6301524980144350051">"कार मोडबाट बाहिर निस्कन ट्याप गर्नुहोस्।"</string>
<string name="tethered_notification_title" msgid="3146694234398202601">"टेथर गर्ने वा हटस्पट सक्रिय"</string>
- <string name="tethered_notification_message" msgid="2113628520792055377">"सेट अप गर्न ट्याप गर्नुहोस्।"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"सेटअप गर्न ट्याप गर्नुहोस्।"</string>
<string name="disable_tether_notification_title" msgid="7526977944111313195">"टेदरिङलाई असक्षम पारिएको छ"</string>
<string name="disable_tether_notification_message" msgid="2913366428516852495">"विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्"</string>
<string name="back_button_label" msgid="2300470004503343439">"पछाडि"</string>
@@ -1504,6 +1505,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"सर्टकट सक्रिय हुँदा, भोल्युमका दुवै बटनहरूलाई ३ सेकेन्डसम्म थिची राख्नाले पहुँच सम्बन्धी कुनै सुविधा सुरु हुनेछ।\n\n हाल व्यवहारमा रहेको पहुँच सम्बन्धी सुविधा:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n तपाईं सेटिङहरू अन्तर्गतको पहुँच सम्बन्धी विकल्पमा गई उक्त सुविधालाई बदल्न सक्नुहुन्छ।"</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"सर्टकटलाई निष्क्रिय पार्नुहोस्"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"सर्टकट प्रयोग गर्नुहोस्"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"रङ्ग उल्टाउने सुविधा"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"रङ सच्याउने सुविधा"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"पहुँचको सर्टकटले <xliff:g id="SERVICE_NAME">%1$s</xliff:g> लाई सक्रिय पार्यो"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"पहुँचको सर्टकटले <xliff:g id="SERVICE_NAME">%1$s</xliff:g> लाई निष्क्रिय पार्यो"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"तपाईंले पहुँच सम्बन्धी बटनलाई ट्याप गर्दा प्रयोग गर्नुपर्ने सुविधा रोज्नुहोस्:"</string>
@@ -1690,13 +1693,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"हरेक हप्तादिनको राति"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"शनिबार"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"घटना"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"शयन अवस्था"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> द्वारा मौन गरिएको"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"तपाईंको यन्त्रसँग आन्तरिक समस्या छ, र तपाईंले फ्याक्ट्री डाटा रिसेट नगर्दासम्म यो अस्थिर रहन्छ।"</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"तपाईंको यन्त्रसँग आन्तरिक समस्या छ। विवरणहरूको लागि आफ्नो निर्मातासँग सम्पर्क गर्नुहोस्।"</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD अनुरोध DIAL अनुरोधमा परिमार्जन गरिएको छ।"</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD अनुरोध SS अनुरोधमा परिमार्जन गरिएको छ।"</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD अनुरोध नयाँ USSD अनुरोधमा परिमार्जन गरिएको छ।"</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD अनुरोधलाई भिडियो DIAL अनुरोधमा परिमार्जन गरियो।"</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS अनुरोध DIAL अनुरोधमा परिमार्जन गरिएको छ।"</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS अनुरोधलाई भिडियो DIAL अनुरोधमा परिमार्जन गरियो।"</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS अनुरोध USSD अनुरोधमा परिमार्जन गरिएको छ।"</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS अनुरोध नयाँ SS अनुरोधमा परिमार्जन गरिएको छ।"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"कार्य प्रोफाइल"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index b06728a..5ae838e 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Uitschakelen"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Noodgeval"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Bugrapport"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Sessie beëindigen"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Bugrapport genereren"</string>
<string name="bugreport_message" msgid="398447048750350456">"Hiermee worden gegevens over de huidige status van je apparaat verzameld en als e-mail verzonden. Wanneer u een bugrapport start, duurt het even voordat het kan worden verzonden. Even geduld alstublieft."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interactief rapport"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Wanneer de snelkoppeling is ingeschakeld, kun je drie seconden op beide volumeknoppen drukken om een toegankelijkheidsfunctie te starten.\n\n Huidige toegankelijkheidsfunctie:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Je kunt de functie wijzigen in Instellingen > Toegankelijkheid."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Sneltoets uitschakelen"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Sneltoets gebruiken"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Kleurinversie"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Kleurcorrectie"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"\'Snelle link voor toegankelijkheid\' heeft <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ingeschakeld"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"\'Snelle link voor toegankelijkheid\' heeft <xliff:g id="SERVICE_NAME">%1$s</xliff:g> uitgeschakeld"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Kies een functie om te gebruiken wanneer je op de knop Toegankelijkheid tikt:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Doordeweekse avond"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Evenement"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Slapen"</string>
<string name="muted_by" msgid="6147073845094180001">"Gedempt door <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Er is een intern probleem met je apparaat. Het apparaat kan instabiel zijn totdat u het apparaat terugzet naar de fabrieksinstellingen."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Er is een intern probleem met je apparaat. Neem contact op met de fabrikant voor meer informatie."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD-verzoek is gewijzigd in DIAL-verzoek."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD-verzoek is gewijzigd in SS-verzoek."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD-verzoek is gewijzigd in nieuw USSD-verzoek."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD-verzoek is gewijzigd in Video DIAL-verzoek."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-verzoek is gewijzigd in DIAL-verzoek."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS-verzoek is gewijzigd in Video DIAL-verzoek."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-verzoek is gewijzigd in USSD-verzoek."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-verzoek is gewijzigd in nieuw SS-verzoek."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Werkprofiel"</string>
diff --git a/core/res/res/values-pa/required_apps_managed_device.xml b/core/res/res/values-pa/required_apps_managed_device.xml
new file mode 100644
index 0000000..faadc50
--- /dev/null
+++ b/core/res/res/values-pa/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.download"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-pa/required_apps_managed_profile.xml b/core/res/res/values-pa/required_apps_managed_profile.xml
new file mode 100644
index 0000000..537a80c
--- /dev/null
+++ b/core/res/res/values-pa/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.download"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-pa/required_apps_managed_user.xml b/core/res/res/values-pa/required_apps_managed_user.xml
new file mode 100644
index 0000000..e69bbbc
--- /dev/null
+++ b/core/res/res/values-pa/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.download"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index b5811ac..b5b6a00 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"ਪਾਵਰ ਬੰਦ"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"ਸੰਕਟਕਾਲ"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"ਬਗ ਰਿਪੋਰਟ"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"ਸੈਸ਼ਨ ਸਮਾਪਤ ਕਰੋ"</string>
<string name="bugreport_title" msgid="2667494803742548533">"ਬਗ ਰਿਪੋਰਟ ਲਓ"</string>
<string name="bugreport_message" msgid="398447048750350456">"ਇਹ ਇੱਕ ਈਮੇਲ ਸੁਨੇਹਾ ਭੇਜਣ ਲਈ, ਤੁਹਾਡੇ ਵਰਤਮਾਨ ਡੀਵਾਈਸ ਬਾਰੇ ਜਾਣਕਾਰੀ ਇਕੱਠੀ ਕਰੇਗਾ। ਬੱਗ ਰਿਪੋਰਟ ਸ਼ੁਰੂ ਕਰਨ ਵਿੱਚ ਥੋੜ੍ਹਾ ਸਮਾਂ ਲੱਗੇਗਾ ਜਦੋਂ ਤੱਕ ਇਹ ਭੇਜੇ ਜਾਣ ਲਈ ਤਿਆਰ ਨਾ ਹੋਵੇ, ਕਿਰਪਾ ਕਰਕੇ ਧੀਰਜ ਰੱਖੋ।"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ਅੰਤਰਕਿਰਿਆਤਮਕ ਰਿਪੋਰਟ"</string>
@@ -1051,8 +1052,7 @@
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"ਸਿਸਟਮ ਸੈਟਿੰਗਾਂ > ਐਪਾਂ > ਡਾਊਨਲੋਡ ਕੀਤਿਆਂ ਵਿੱਚ ਇਸਨੂੰ ਮੁੜ-ਸਮਰੱਥ ਬਣਾਓ।"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਵਰਤਮਾਨ ਡਿਸਪਲੇ ਆਕਾਰ ਸੈਟਿੰਗ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ ਹੈ ਅਤੇ ਅਣਕਿਆਸੇ ਤੌਰ \'ਤੇ ਵਿਹਾਰ ਕਰ ਸਕਦੀ ਹੈ।"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"ਹਮੇਸ਼ਾ ਦਿਖਾਓ"</string>
- <!-- no translation found for unsupported_compile_sdk_message (4253168368781441759) -->
- <skip />
+ <string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ Android OS ਦਾ ਇੱਕ ਗੈਰ-ਅਨੁਕੂਲ ਵਰਜਨ ਬਣਾਇਆ ਗਿਆ ਸੀ ਅਤੇ ਅਣਕਿਆਸੇ ਤੌਰ \'ਤੇ ਵਿਹਾਰ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਦਾ ਇੱਕ ਅੱਪਡੇਟ ਕੀਤਾ ਹੋਇਆ ਵਰਜਨ ਉਪਲਬਧ ਹੋ ਸਕਦਾ ਹੈ।"</string>
<string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"ਹਮੇਸਾਂ ਦਿਖਾਓ"</string>
<string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"ਅੱਪਡੇਟ ਦੀ ਜਾਂਚ ਕਰੋ"</string>
<string name="smv_application" msgid="3307209192155442829">"ਐਪ <xliff:g id="APPLICATION">%1$s</xliff:g> (ਪ੍ਰਕਿਰਿਆ <xliff:g id="PROCESS">%2$s</xliff:g>) ਨੇ ਆਪਣੀ ਖੁਦ-ਲਾਗੂ ਕੀਤੀ ਸਟ੍ਰਿਕਟਮੋਡ ਨੀਤੀ ਦੀ ਉਲੰਘਣਾ ਕੀਤੀ ਹੈ।"</string>
@@ -1500,6 +1500,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"ਸ਼ਾਰਟਕੱਟ ਚਾਲੂ ਹੋਣ \'ਤੇ, ਕਿਸੇ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਅਵਾਜ਼ ਬਟਨਾਂ ਨੂੰ 3 ਸਕਿੰਟ ਲਈ ਦਬਾ ਕੇ ਰੱਖੋ।\n\n ਵਰਤਮਾਨ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾ:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ਤੁਸੀਂ ਸੈਟਿੰਗਾਂ > ਪਹੁੰਚਯੋਗਤਾ ਵਿੱਚ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਬਦਲ ਸਕਦੇ ਹੋ।"</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"ਸ਼ਾਰਟਕੱਟ ਬੰਦ ਕਰੋ"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"ਸ਼ਾਰਟਕੱਟ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"ਰੰਗ ਦੀ ਉਲਟੀ ਤਰਤੀਬ"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"ਰੰਗ ਸੁਧਾਈ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ਪਹੁੰਚਯੋਗਤਾ ਸ਼ਾਰਟਕੱਟ ਨੇ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਚਾਲੂ ਕੀਤਾ"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ਪਹੁੰਚਯੋਗਤਾ ਸ਼ਾਰਟਕੱਟ ਨੇ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਬੰਦ ਕੀਤਾ"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਪਹੁੰਚਯੋਗਤਾ ਬਟਨ ਨੂੰ ਟੈਪ ਕੀਤੇ ਜਾਣ \'ਤੇ ਵਰਤਣ ਲਈ ਕੋਈ ਵਿਸ਼ੇਸ਼ਤਾ ਚੁਣੋ:"</string>
@@ -1639,8 +1641,7 @@
<string name="package_installed_device_owner" msgid="6875717669960212648">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਸਥਾਪਤ ਕੀਤਾ ਗਿਆ"</string>
<string name="package_updated_device_owner" msgid="1847154566357862089">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ"</string>
<string name="package_deleted_device_owner" msgid="2307122077550236438">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਮਿਟਾਇਆ ਗਿਆ"</string>
- <!-- no translation found for battery_saver_description (5394663545060026162) -->
- <skip />
+ <string name="battery_saver_description" msgid="5394663545060026162">"ਬੈਟਰੀ ਲਾਈਫ਼ ਨੂੰ ਬਿਹਤਰ ਬਣਾਉਣ ਵਿੱਚ ਸਹਾਇਤਾ ਕਰਨ ਲਈ, ਬੈਟਰੀ ਸੇਵਰ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਦਰਸ਼ਨ ਘਟਾਉਂਦਾ ਹੈ ਅਤੇ ਥਰਥਰਾਹਟ, ਟਿਕਾਣਾ ਸੇਵਾਵਾਂ ਅਤੇ ਜ਼ਿਆਦਾਤਰ ਬੈਕਗ੍ਰਾਊਂਡ ਡਾਟੇ ਨੂੰ ਸੀਮਤ ਕਰਦਾ ਹੈ। ਈਮੇਲ, ਸੁਨੇਹਾ ਭੇਜਣ ਅਤੇ ਹੋਰ ਐਪਾਂ, ਜੋ ਸਮਕਾਲੀਕਰਨ \'ਤੇ ਨਿਰਭਰ ਹਨ, ਉਹ ਉਦੋਂ ਤੱਕ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਉਹਨਾਂ ਨੂੰ ਖੋਲ੍ਹਦੇ ਨਹੀਂ।\n\nਬੈਟਰੀ ਸੇਵਰ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਬੰਦ ਹੁੰਦਾ ਹੈ ਜਦੋਂ ਤੁਹਾਡਾ ਡੀਵਾਈਸ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੁੰਦਾ ਹੈ।"</string>
<string name="data_saver_description" msgid="6015391409098303235">"ਡਾਟਾ ਵਰਤੋਂ ਘਟਾਉਣ ਵਿੱਚ ਮਦਦ ਲਈ, ਡਾਟਾ ਸੇਵਰ ਕੁਝ ਐਪਾਂ ਨੂੰ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਡਾਟਾ ਭੇਜਣ ਜਾਂ ਪ੍ਰਾਪਤ ਕਰਨ ਤੋਂ ਰੋਕਦਾ ਹੈ। ਤੁਹਾਡੇ ਵੱਲੋਂ ਵਰਤਮਾਨ ਤੌਰ \'ਤੇ ਵਰਤੀ ਜਾ ਰਹੀ ਐਪ ਡਾਟਾ \'ਤੇ ਪਹੁੰਚ ਕਰ ਸਕਦੀ ਹੈ, ਪਰ ਉਹ ਇੰਝ ਕਦੇ-ਕਦਾਈਂ ਕਰ ਸਕਦੀ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਇਸ ਦਾ ਮਤਲਬ ਇਹ ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਚਿੱਤਰ ਤਦ ਤੱਕ ਨਹੀਂ ਪ੍ਰਦਰਸ਼ਿਤ ਕੀਤੇ ਜਾਂਦੇ, ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਉਹਨਾਂ \'ਤੇ ਟੈਪ ਨਹੀਂ ਕਰਦੇ।"</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"ਕੀ ਡਾਟਾ ਸੇਵਰ ਚਾਲੂ ਕਰਨਾ ਹੈ?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"ਚਾਲੂ ਕਰੋ"</string>
@@ -1687,13 +1688,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"ਵੀਕਨਾਈਟ"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"ਹਫ਼ਤੇ ਦਾ ਅੰਤਲਾ ਦਿਨ"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"ਇਵੈਂਟ"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"ਸੌਣ ਵੇਲੇ"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ਵੱਲੋਂ ਮਿਊਟ ਕੀਤਾ ਗਿਆ"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਇੱਕ ਅੰਦਰੂਨੀ ਸਮੱਸਿਆ ਹੈ ਅਤੇ ਇਹ ਅਸਥਿਰ ਹੋ ਸਕਦੀ ਹੈ ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਨਹੀਂ ਕਰਦੇ।"</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਇੱਕ ਅੰਦਰੂਨੀ ਸਮੱਸਿਆ ਸੀ। ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਨਿਰਮਾਤਾ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।"</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD ਬੇਨਤੀ DIAL ਬੇਨਤੀ ਵਿੱਚ ਸੰਸ਼ੋਧਿਤ ਕੀਤੀ ਗਈ ਹੈ।"</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD ਬੇਨਤੀ SS ਬੇਨਤੀ ਵਿੱਚ ਸੰਸ਼ੋਧਿਤ ਕੀਤੀ ਗਈ ਹੈ।"</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD ਬੇਨਤੀ ਨਵੀਂ USSD ਬੇਨਤੀ ਵਿੱਚ ਸੰਸ਼ੋਧਿਤ ਕੀਤੀ ਗਈ ਹੈ।"</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD ਬੇਨਤੀ ਨੂੰ ਸੋਧ ਕੇ ਵੀਡੀਓ ਡਾਇਲ ਬੇਨਤੀ ਵਿੱਚ ਬਦਲਿਆ ਗਿਆ।"</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS ਬੇਨਤੀ DIAL ਬੇਨਤੀ ਵਿੱਚ ਸੰਸ਼ੋਧਿਤ ਕੀਤੀ ਗਈ ਹੈ।"</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS ਬੇਨਤੀ ਨੂੰ ਸੋਧ ਕੇ ਵੀਡੀਓ ਡਾਇਲ ਬੇਨਤੀ ਵਿੱਚ ਬਦਲਿਆ ਗਿਆ।"</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS ਬੇਨਤੀ USSD ਬੇਨਤੀ ਵਿੱਚ ਸੰਸ਼ੋਧਿਤ ਕੀਤੀ ਗਈ ਹੈ।"</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS ਬੇਨਤੀ ਨਵੀਂ SS ਵਿੱਚ ਸੰਸ਼ੋਧਿਤ ਕੀਤੀ ਗਈ ਹੈ।"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
diff --git a/core/res/res/values-pl/required_apps_managed_device.xml b/core/res/res/values-pl/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-pl/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-pl/required_apps_managed_profile.xml b/core/res/res/values-pl/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-pl/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-pl/required_apps_managed_user.xml b/core/res/res/values-pl/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-pl/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 9e8fb3c..77d706a 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -216,6 +216,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Wyłącz"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Alarmowy"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Zgłoszenie błędu"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Zakończ sesję"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Zgłoś błąd"</string>
<string name="bugreport_message" msgid="398447048750350456">"Informacje o bieżącym stanie urządzenia zostaną zebrane i wysłane e-mailem. Przygotowanie zgłoszenia błędu do wysłania chwilę potrwa, więc zachowaj cierpliwość."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Raport interaktywny"</string>
@@ -1546,6 +1547,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Gdy skrót jest włączony, jednoczesne naciśnięcie przez trzy sekundy obu klawiszy sterowania głośnością uruchomi funkcję ułatwień dostępu.\n\nBieżąca funkcja ułatwień dostępu:\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\nFunkcję możesz zmienić, wybierając Ustawienia > Ułatwienia dostępu."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Wyłącz skrót"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Użyj skrótu"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inwersja kolorów"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Korekcja kolorów"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Skrót ułatwień dostępu wyłączył usługę <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Skrót ułatwień dostępu wyłączył usługę <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Wybierz funkcję używaną po kliknięciu przycisku ułatwień dostępu."</string>
@@ -1750,13 +1753,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Noc poza weekendem"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Wydarzenie"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Sen"</string>
<string name="muted_by" msgid="6147073845094180001">"Ściszone przez: <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"W Twoim urządzeniu wystąpił problem wewnętrzny. Może być ono niestabilne, dopóki nie przywrócisz danych fabrycznych."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"W Twoim urządzeniu wystąpił problem wewnętrzny. Skontaktuj się z jego producentem, by otrzymać szczegółowe informacje."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Żądanie USSD zostało zmienione na żądanie DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Żądanie USSD zostało zmienione na żądanie SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Żądanie USSD zostało zmienione na nowe żądanie USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Żądanie USSD zostało zmienione na żądanie Video DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Żądanie SS zostało zmienione na żądanie DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Żądanie SS zostało zmienione na żądanie Video DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Żądanie SS zostało zmienione na żądanie USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Żądanie SS zostało zmienione na nowe żądanie SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profil do pracy"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index a6b389e..cce4bf0 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Desligar"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de bugs"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Finalizar sessão"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Obter relatório de bugs"</string>
<string name="bugreport_message" msgid="398447048750350456">"Isto coletará informações sobre o estado atual do dispositivo para enviá-las em uma mensagem de e-mail. Após iniciar o relatório de bugs, será necessário aguardar algum tempo até que esteja pronto para ser enviado."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Relatório interativo"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Quando o atalho está ativado, pressione os dois botões de volume por três segundos para iniciar um recurso de acessibilidade.\n\n Recurso de acessibilidade atual:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n É possível alterar o recurso em Configurações > Acessibilidade."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Desativar atalho"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Usar atalho"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inversão de cores"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Correção de cor"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"O atalho de acessibilidade ativou o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"O atalho de acessibilidade desativou o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Escolha um recurso a ser usado ao tocar no botão Acessibilidade:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Durante a semana à noite"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Fim de semana"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Evento"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Dormindo"</string>
<string name="muted_by" msgid="6147073845094180001">"Som desativado por <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Há um problema interno com seu dispositivo. Ele pode ficar instável até que você faça a redefinição para configuração original."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Há um problema interno com seu dispositivo. Entre em contato com o fabricante para saber mais detalhes."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"A solicitação USSD foi modificada para a solicitação DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"A solicitação USSD foi modificada para a solicitação SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"A solicitação USSD foi modificada para a nova solicitação USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"A solicitação USSD foi modificada para a solicitação DIAL de vídeo."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"A solicitação SS foi modificada para a solicitação DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"A solicitação SS foi modificada para a solicitação DIAL de vídeo."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"A solicitação SS foi modificada para a solicitação USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"A solicitação SS foi modificada para a nova solicitação SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Perfil de trabalho"</string>
diff --git a/core/res/res/values-pt-rPT/required_apps_managed_device.xml b/core/res/res/values-pt-rPT/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-pt-rPT/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-pt-rPT/required_apps_managed_profile.xml b/core/res/res/values-pt-rPT/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-pt-rPT/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-pt-rPT/required_apps_managed_user.xml b/core/res/res/values-pt-rPT/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-pt-rPT/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 3329961..dc2d6e4 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -212,8 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Desligar"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de erros"</string>
- <!-- no translation found for global_action_logout (935179188218826050) -->
- <skip />
+ <string name="global_action_logout" msgid="935179188218826050">"Terminar sessão"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Criar relatório de erros"</string>
<string name="bugreport_message" msgid="398447048750350456">"Será recolhida informação sobre o estado atual do seu dispositivo a enviar através de uma mensagem de email. Demorará algum tempo até que o relatório de erro esteja pronto para ser enviado. Aguarde um pouco."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Relatório interativo"</string>
@@ -382,7 +381,7 @@
<string name="permdesc_readCalendar" product="tablet" msgid="4993979255403945892">"Esta aplicação pode ler todos os eventos do calendário armazenados no seu tablet e partilhar ou guardar os dados do calendário."</string>
<string name="permdesc_readCalendar" product="tv" msgid="8837931557573064315">"Esta aplicação pode ler todos os eventos do calendário armazenados na sua TV e partilhar ou guardar os dados do calendário."</string>
<string name="permdesc_readCalendar" product="default" msgid="4373978642145196715">"Esta aplicação pode ler todos os eventos do calendário armazenados no seu telemóvel e partilhar ou guardar os dados do calendário."</string>
- <string name="permlab_writeCalendar" msgid="8438874755193825647">"adicionar ou modificar eventos do calendário e enviar e-mail a convidados sem o conhecimento dos proprietários"</string>
+ <string name="permlab_writeCalendar" msgid="8438874755193825647">"adicionar ou modificar eventos do calendário e enviar email a convidados sem o conhecimento dos proprietários"</string>
<string name="permdesc_writeCalendar" product="tablet" msgid="1675270619903625982">"Esta aplicação pode adicionar, remover ou alterar eventos do calendário no seu tablet. Esta aplicação pode enviar mensagens que parecem vir de proprietários do calendário ou alterar eventos sem notificar os respetivos proprietários."</string>
<string name="permdesc_writeCalendar" product="tv" msgid="9017809326268135866">"Esta aplicação pode adicionar, remover ou alterar eventos do calendário na sua TV. Esta aplicação pode enviar mensagens que parecem vir de proprietários do calendário ou alterar eventos sem notificar os respetivos proprietários."</string>
<string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Esta aplicação pode adicionar, remover ou alterar eventos do calendário no seu telemóvel. Esta aplicação pode enviar mensagens que parecem vir de proprietários do calendário ou alterar eventos sem notificar os respetivos proprietários."</string>
@@ -757,7 +756,7 @@
<string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"Desbloqueio da conta"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"Demasiadas tentativas para desenhar sequência"</string>
<string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"Para desbloquear, inicie sessão com a Conta Google."</string>
- <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Nome de utilizador (e-mail)"</string>
+ <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Nome de utilizador (email)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Palavra-passe"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Iniciar sessão"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Nome de utilizador ou palavra-passe inválidos."</string>
@@ -1500,10 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Quando o atalho está ativado, premir ambos os botões de volume durante 3 segundos inicia uma funcionalidade de acessibilidade.\n\n Funcionalidade de acessibilidade atual:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Pode alterar a funcionalidade em Definições > Acessibilidade."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Desativar atalho"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Utilizar atalho"</string>
- <!-- no translation found for color_inversion_feature_name (4231186527799958644) -->
- <skip />
- <!-- no translation found for color_correction_feature_name (6779391426096954933) -->
- <skip />
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inversão de cores"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Correção da cor"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"O Atalho de acessibilidade ativou o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"O Atalho de acessibilidade desativou o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Escolha uma funcionalidade para utilizar quando tocar no botão Acessibilidade:"</string>
@@ -1690,15 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Dias da semana à noite"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Fim de semana"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Evento"</string>
- <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) -->
- <skip />
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"A dormir"</string>
<string name="muted_by" msgid="6147073845094180001">"Som desativado por <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Existe um problema interno no seu dispositivo e pode ficar instável até efetuar uma reposição de dados de fábrica."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Existe um problema interno no seu dispositivo. Contacte o fabricante para obter mais informações."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"O pedido USSD foi modificado para um pedido DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"O pedido USSD foi modificado para um pedido SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"O pedido USSD foi modificado para um novo pedido USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"O pedido USSD foi modificado para um pedido Video DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"O pedido SS foi modificado para um pedido DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"O pedido SS foi modificado para um pedido Video DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"O pedido SS foi modificado para um pedido USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"O pedido SS foi modificado para um novo pedido SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Perfil de trabalho"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index a6b389e..cce4bf0 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Desligar"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de bugs"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Finalizar sessão"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Obter relatório de bugs"</string>
<string name="bugreport_message" msgid="398447048750350456">"Isto coletará informações sobre o estado atual do dispositivo para enviá-las em uma mensagem de e-mail. Após iniciar o relatório de bugs, será necessário aguardar algum tempo até que esteja pronto para ser enviado."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Relatório interativo"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Quando o atalho está ativado, pressione os dois botões de volume por três segundos para iniciar um recurso de acessibilidade.\n\n Recurso de acessibilidade atual:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n É possível alterar o recurso em Configurações > Acessibilidade."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Desativar atalho"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Usar atalho"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inversão de cores"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Correção de cor"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"O atalho de acessibilidade ativou o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"O atalho de acessibilidade desativou o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Escolha um recurso a ser usado ao tocar no botão Acessibilidade:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Durante a semana à noite"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Fim de semana"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Evento"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Dormindo"</string>
<string name="muted_by" msgid="6147073845094180001">"Som desativado por <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Há um problema interno com seu dispositivo. Ele pode ficar instável até que você faça a redefinição para configuração original."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Há um problema interno com seu dispositivo. Entre em contato com o fabricante para saber mais detalhes."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"A solicitação USSD foi modificada para a solicitação DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"A solicitação USSD foi modificada para a solicitação SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"A solicitação USSD foi modificada para a nova solicitação USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"A solicitação USSD foi modificada para a solicitação DIAL de vídeo."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"A solicitação SS foi modificada para a solicitação DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"A solicitação SS foi modificada para a solicitação DIAL de vídeo."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"A solicitação SS foi modificada para a solicitação USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"A solicitação SS foi modificada para a nova solicitação SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Perfil de trabalho"</string>
diff --git a/core/res/res/values-ro/required_apps_managed_device.xml b/core/res/res/values-ro/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-ro/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ro/required_apps_managed_profile.xml b/core/res/res/values-ro/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-ro/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ro/required_apps_managed_user.xml b/core/res/res/values-ro/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-ro/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 8e3726b..eece63e 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -214,6 +214,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Opriți alimentarea"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Urgență"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Raport despre erori"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Încheiați sesiunea"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Executați un raport despre erori"</string>
<string name="bugreport_message" msgid="398447048750350456">"Acest raport va colecta informații despre starea actuală a dispozitivului, pentru a le trimite într-un e-mail. Aveți răbdare după pornirea raportului despre erori până când va fi gata de trimis."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Raport interactiv"</string>
@@ -1522,6 +1523,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Când comanda rapidă este activată, dacă apăsați ambele butoane de volum timp de 3 secunde, veți lansa o funcție de accesibilitate.\n\n Funcția actuală de accesibilitate:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Puteți schimba funcția în Setări > Accesibilitate."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Dezactivați comanda rapidă"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Utilizați comanda rapidă"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inversarea culorilor"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Corecția culorii"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Comanda rapidă de accesibilitate a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Comanda rapidă de accesibilitate a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Alegeți o funcție pe care să o folosiți când atingeți butonul Accesibilitate:"</string>
@@ -1717,13 +1720,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Nopțile din zilele lucrătoare"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Eveniment"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Somn"</string>
<string name="muted_by" msgid="6147073845094180001">"Dezactivate de <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"A apărut o problemă internă pe dispozitiv, iar acesta poate fi instabil până la revenirea la setările din fabrică."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"A apărut o problemă internă pe dispozitiv. Pentru detalii, contactați producătorul."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Solicitarea USSD este modificată într-o solicitare DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Solicitarea USSD este modificată într-o solicitare SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Solicitarea USSD este modificată într-o nouă solicitare USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Solicitarea USSD este modificată într-o solicitare DIAL video."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Solicitarea SS este modificată într-o solicitare DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Solicitarea SS este modificată într-o solicitare DIAL video."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Solicitarea SS este modificată într-o solicitare USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Solicitarea SS este modificată într-o nouă solicitare SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profil de serviciu"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index f8ebf0d..53930c1 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -216,6 +216,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Выключить"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Экстренный вызов"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Отчет об ошибке"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Закончить сеанс"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Отчет об ошибке"</string>
<string name="bugreport_message" msgid="398447048750350456">"Информация о текущем состоянии вашего устройства будет собрана и отправлена по электронной почте. Подготовка отчета займет некоторое время."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Интерактивный отчет"</string>
@@ -1546,6 +1547,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Чтобы использовать функцию специальных возможностей, когда она включена, нажмите и удерживайте три секунды обе кнопки регулировки громкости.\n\nТекущая функция специальных возможностей:\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\nВы можете изменить ее в разделе \"Настройки > Специальные возможности\"."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Деактивировать быстрое включение"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Использовать быстрое включение"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Инверсия цветов"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Коррекция цвета"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Сервис <xliff:g id="SERVICE_NAME">%1$s</xliff:g> включен"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Сервис <xliff:g id="SERVICE_NAME">%1$s</xliff:g> отключен"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Выберите функцию, которая запускается при нажатии кнопки специальных возможностей:"</string>
@@ -1750,13 +1753,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Будний вечер"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Выходные"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Мероприятие"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Время сна"</string>
<string name="muted_by" msgid="6147073845094180001">"Звук отключен приложением \"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>\""</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Произошла внутренняя ошибка, и устройство может работать нестабильно, пока вы не выполните сброс настроек."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Произошла внутренняя ошибка. Обратитесь к производителю устройства за подробными сведениями."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD-запрос преобразован в DIAL-запрос"</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD-запрос преобразован в SS-запрос."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD-запрос преобразован в новый USSD-запрос."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD-запрос преобразован в запрос Video DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-запрос преобразован в DIAL-запрос."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS-запрос преобразован в запрос Video DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-запрос преобразован в USSD-запрос."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-запрос преобразован в новый SS-запрос."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Рабочий профиль"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index af1f8ba..2a1d635 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"බල රහිත කරන්න"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"හදිසි"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"දෝෂ වර්තාව"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"සැසිය අවසන් කරන්න"</string>
<string name="bugreport_title" msgid="2667494803742548533">"දෝෂ වාර්තාවක් ගන්න"</string>
<string name="bugreport_message" msgid="398447048750350456">"ඊ-තැපැල් පණිවිඩයක් ලෙස යැවීමට මෙය ඔබගේ වත්මන් උපාංග තත්වය ගැන තොරතුරු එකතු කරනු ඇත. දෝෂ වාර්තාව ආරම්භ කර එය යැවීමට සූදානම් කරන තෙක් එයට කිසියම් කාලයක් ගතවනු ඇත; කරුණාකර ඉවසන්න."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"අන්තර්ක්රියා වාර්."</string>
@@ -1500,6 +1501,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"කෙටිමඟ සක්රිය විට, හඬ බොත්තම් දෙකම තත්පර 3ක් අල්ලාගෙන සිටීමෙන් ප්රවේශ්යත අංගයක් ඇරඹේ.\n\n වත්මන් ප්රවේශ්යතා අංගය:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n සැකසීම් > ප්රවේශ්යතාව තුළ ඔබට අංගය වෙනස් කළ හැක."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"කෙටිමඟ ක්රියාවිරහිත කරන්න"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"කෙටිමඟ භාවිතා කරන්න"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"වර්ණ අපවර්තනය"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"වර්ණ නිවැරදි කිරීම"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ප්රවේශ්යතා කෙටි මග <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්රියාත්මක කරන ලදී"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ප්රවේශ්යතා කෙටි මග <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්රියාවිරහිත කරන ලදී"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ඔබ ප්රවේශ්යතා බොත්තම තට්ටු කරන විට භාවිතා කිරීමට අංගයක් තෝරාගන්න:"</string>
@@ -1686,13 +1689,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Weeknight"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"සති අන්තය"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"සිදුවීම"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"නිදා ගනිමින්"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> විසින් නිශ්ශබ්ද කරන ලදි"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"ඔබේ උපාංගය සමගින් ගැටලුවක් ඇති අතර, ඔබේ කර්මාන්තශාලා දත්ත යළි සකසන තෙක් එය අස්ථායි විය හැකිය."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"ඔබේ උපාංගය සමගින් අභ්යන්තර ගැටලුවක් ඇත. විස්තර සඳහා ඔබේ නිෂ්පාදක අමතන්න."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD ඉල්ලීම DIAL ඉල්ලීම වෙත විකරණය කරන ලදී."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD ඉල්ලීම SS ඉල්ලීම වෙත විකරණය කරන ලදී."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD ඉල්ලීම නව USSD ඉල්ලීම වෙත විකරණය කරන ලදී."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD ඉල්ලීම Video DIAL ඉල්ලීම ලෙස වෙනස් කර ඇත."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS ඉල්ලීම DIAL ඉල්ලීම වෙත විකරණය කරන ලදී."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS ඉල්ලීම Video DIAL ඉල්ලීම ලෙස වෙනස් කර ඇත."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS ඉල්ලීම USSD ඉල්ලීම වෙත විකරණය කරන ලදී."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS ඉල්ලීම නව DIAL ඉල්ලීම වෙත විකරණය කරන ලදී."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"කාර්යාල පැතිකඩ"</string>
diff --git a/core/res/res/values-sk/required_apps_managed_device.xml b/core/res/res/values-sk/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-sk/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sk/required_apps_managed_profile.xml b/core/res/res/values-sk/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-sk/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sk/required_apps_managed_user.xml b/core/res/res/values-sk/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-sk/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 40979f9..a0de5eb 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -216,6 +216,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Vypnúť"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Tiesňové volanie"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Hlásenie o chybách"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Ukončiť reláciu"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Vytvoriť hlásenie chyby"</string>
<string name="bugreport_message" msgid="398447048750350456">"Týmto zhromaždíte informácie o aktuálnom stave zariadenia. Informácie je potom možné odoslať e-mailom, chvíľu však potrvá, kým bude hlásenie chyby pripravené na odoslanie. Prosíme vás preto o trpezlivosť."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktívne nahlásenie"</string>
@@ -1546,6 +1547,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Keď je skratka zapnutá, stlačením obidvoch tlačidiel hlasitosti na tri sekundy spustíte funkciu dostupnosti.\n\n Aktuálna funkcia dostupnosti:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Funkciu môžete zmeniť v časti Nastavenia > Dostupnosť."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Vypnúť skratku"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Použiť skratku"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inverzia farieb"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Úprava farieb"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Skratka dostupnosti zapla službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Skratka dostupnosti vypla službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Klepnutím na tlačidlo dostupnosti vyberte požadovanú funkciu:"</string>
@@ -1750,13 +1753,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Noc pracovného dňa"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Víkend"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Udalosť"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Spánok"</string>
<string name="muted_by" msgid="6147073845094180001">"Stlmené aplikáciou <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Vo vašom zariadení došlo k internému problému. Môže byť nestabilné, kým neobnovíte jeho výrobné nastavenia."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Vo vašom zariadení došlo k internému problému. Ak chcete získať podrobné informácie, obráťte sa na jeho výrobcu."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Žiadosť USSD bola upravená na žiadosť DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Žiadosť USSD bola upravená na žiadosť SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Žiadosť USSD bola upravená na novú žiadosť USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Žiadosť USSD bola upravená na žiadosť Video DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Žiadosť SS bola upravená na žiadosť DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Žiadosť SS bola upravená na žiadosť Video DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Žiadosť SS bola upravená na žiadosť USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Žiadosť SS bola upravená na novú žiadosť SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Pracovný profil"</string>
diff --git a/core/res/res/values-sl/required_apps_managed_device.xml b/core/res/res/values-sl/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-sl/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sl/required_apps_managed_profile.xml b/core/res/res/values-sl/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-sl/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sl/required_apps_managed_user.xml b/core/res/res/values-sl/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-sl/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index d8b2745..fb96279 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -216,6 +216,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Izklopi"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Klic v sili"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Poročilo o napakah"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Končaj sejo"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Ustvari poročilo o napakah"</string>
<string name="bugreport_message" msgid="398447048750350456">"S tem bodo zbrani podatki o trenutnem stanju naprave, ki bodo poslani v e-poštnem sporočilu. Izvedba poročila o napakah in priprava trajata nekaj časa, zato bodite potrpežljivi."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktivno poročilo"</string>
@@ -1546,6 +1547,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Ko je bližnjica vklopljena, pritisnite gumba za glasnost in ju pridržite tri sekunde, če želite zagnati funkcijo za ljudi s posebnimi potrebami.\n\n Trenutna funkcija za ljudi s posebnimi potrebami:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Funkcijo lahko spremenite v »Nastavitve > Funkcije za ljudi s posebnimi potrebami«."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Izklopi bližnjico"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Uporabi bližnjico"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inverzija barv"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Popravljanje barv"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Bližnjica funkcij za ljudi s posebnimi potrebami je vklopila <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Bližnjica funkcij za ljudi s posebnimi potrebami je izklopila <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Izberite funkcijo, ki jo želite uporabljati, ko se dotaknete gumba »Dostopnost«:"</string>
@@ -1750,13 +1753,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Noč med tednom"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Konec tedna"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Dogodek"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Spanje"</string>
<string name="muted_by" msgid="6147073845094180001">"Izklop zvoka: <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Vaša naprava ima notranjo napako in bo morda nestabilna, dokler je ne ponastavite na tovarniške nastavitve."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Vaša naprava ima notranjo napako. Če želite več informacij, se obrnite na proizvajalca."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Zahteva USSD je spremenjena v zahtevo DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Zahteva USSD je spremenjena v zahtevo SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Zahteva USSD je spremenjena v novo zahtevo USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Zahteva USSD je spremenjena v zahtevo Video DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Zahteva SS je spremenjena v zahtevo DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Zahteva SS je spremenjena v zahtevo Video DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Zahteva SS je spremenjena v zahtevo USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Zahteva SS je spremenjena v novo zahtevo SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Delovni profil"</string>
diff --git a/core/res/res/values-sq/required_apps_managed_device.xml b/core/res/res/values-sq/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-sq/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sq/required_apps_managed_profile.xml b/core/res/res/values-sq/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-sq/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sq/required_apps_managed_user.xml b/core/res/res/values-sq/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-sq/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 8bf34dd..4aee9bc 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Fik"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Urgjenca"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Raporti i defekteve në kod"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Jepi fund sesionit"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Merr raportin e defekteve në kod"</string>
<string name="bugreport_message" msgid="398447048750350456">"Ky funksion mundëson mbledhjen e informacioneve mbi gjendjen aktuale të pajisjes për ta dërguar si mesazh mail-i. Do të duhet pak kohë nga nisja e raportit të defekteve në kod. Faleminderit për durimin."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Raport interaktiv"</string>
@@ -1499,6 +1500,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Kur shkurtorja është e aktivizuar, shtypja e të dy butonave për 3 sekonda do të nisë një funksion qasshmërie.\n\n Funksioni aktual i qasshmërisë:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Mund ta ndryshosh funksionin te Cilësimet > Qasshmëria."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Çaktivizo shkurtoren"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Përdor shkurtoren"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Kthimi i ngjyrës"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Korrigjimi i ngjyrës"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Shkurtorja e qasshmërisë e aktivizoi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Shkurtorja e qasshmërisë e çaktivizoi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Zgjidh një funksion për ta përdorur kur troket butonin e \"Qasshmërisë\":"</string>
@@ -1685,13 +1688,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Netët e javës"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Fundjava"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Ngjarje"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Në gjumë"</string>
<string name="muted_by" msgid="6147073845094180001">"Lënë në heshtje nga <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Ka një problem të brendshëm me pajisjen tënde. Ajo mund të jetë e paqëndrueshme derisa të rivendosësh të dhënat në gjendje fabrike."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Ka një problem të brendshëm me pajisjen tënde. Kontakto prodhuesin tënd për detaje."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Kërkesa USSD është modifikuar në kërkesën DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Kërkesa USSD është modifikuar në kërkesën SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Kërkesa USSD është modifikuar në kërkesën e re USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Kërkesa USSD është modifikuar në kërkesën Video DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Kërkesa SS është e modifikuar në kërkesën DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Kërkesa SS është e modifikuar në kërkesën Video DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Kërkesa SS është modifikuar në kërkesën USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Kërkesa SS është e modifikuar në kërkesën e re SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profili i punës"</string>
diff --git a/core/res/res/values-sr/required_apps_managed_device.xml b/core/res/res/values-sr/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-sr/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sr/required_apps_managed_profile.xml b/core/res/res/values-sr/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-sr/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sr/required_apps_managed_user.xml b/core/res/res/values-sr/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-sr/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 7c6460be..146f8b3 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -214,6 +214,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Искључи"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Хитни позив"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Извештај о грешци"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Заврши сесију"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Направи извештај о грешци"</string>
<string name="bugreport_message" msgid="398447048750350456">"Овим ће се прикупити информације о тренутном стању уређаја како би биле послате у поруци е-поште. Од започињања извештаја о грешци до тренутка за његово слање проћи ће неко време; будите стрпљиви."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Интерактив. извештај"</string>
@@ -1522,6 +1523,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Када је пречица укључена, притисните оба дугмета за јачину звука да бисте покренули функцију приступачности.\n\n Актуелна функција приступачности:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Можете да промените функцију у одељку Подешавања > Приступачност."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Искључи пречицу"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Користи пречицу"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Инверзија боја"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Корекција боја"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Пречица за приступачност је укључила услугу <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Пречица за приступачност је искључила услугу <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Изаберите функцију која ће се користити када додирнете дугме за приступачност:"</string>
@@ -1717,13 +1720,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Радни дан увече"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Викенд"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Догађај"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Спавање"</string>
<string name="muted_by" msgid="6147073845094180001">"Звук је искључио/ла <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Дошло је до интерног проблема у вези са уређајем и можда ће бити нестабилан док не обавите ресетовање на фабричка подешавања."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Дошло је до интерног проблема у вези са уређајем. Потражите детаље од произвођача."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD захтев је промењен у DIAL захтев."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD захтев је промењен у SS захтев."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD захтев је промењен у нови USSD захтев."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD захтев је промењен у Video DIAL захтев."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS захтев је промењен у DIAL захтев."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS захтев је промењен у Video DIAL захтев."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS захтев је промењен у USSD захтев."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS захтев је промењен у нови SS захтев."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Профил за Work"</string>
diff --git a/core/res/res/values-sv/required_apps_managed_device.xml b/core/res/res/values-sv/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-sv/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sv/required_apps_managed_profile.xml b/core/res/res/values-sv/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-sv/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sv/required_apps_managed_user.xml b/core/res/res/values-sv/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-sv/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index b894c83..2705326 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Stäng av"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Nödsituation"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Felrapport"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Avsluta session"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Skapa felrapport"</string>
<string name="bugreport_message" msgid="398447048750350456">"Nu hämtas information om aktuell status för enheten, som sedan skickas i ett e-postmeddelade. Det tar en liten stund innan felrapporten är färdig och kan skickas, så vi ber dig ha tålamod."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktiv rapport"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"När kortkommandot har aktiverats startar du en tillgänglighetsfunktion genom att trycka ned båda volymknapparna i tre sekunder.\n\n Aktuell tillgänglighetsfunktion:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Du kan ändra funktionen i Inställningar > Tillgänglighet."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Inaktivera kortkommandot"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Använd kortkommandot"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inverterade färger"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Färgkorrigering"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktiverades av Aktivera tillgänglighet snabbt"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> inaktiverades av Aktivera tillgänglighet snabbt"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Välj en funktion som ska användas när du trycker på tillgänglighetsknappen:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Vardagskväll"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"I helgen"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Händelse"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"När jag sover"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> har stängt av ljudet"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Ett internt problem har uppstått i enheten, och det kan hända att problemet kvarstår tills du återställer standardinställningarna."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Ett internt problem har uppstått i enheten. Kontakta tillverkaren om du vill veta mer."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD-begäran har ändrats till en DIAL-begäran."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD-begäran har ändrats till en SS-begäran."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD-begäran har ändrats till en ny USSD-begäran."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD-begäran har ändrats till en Video DIAL-begäran."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS-begäran har ändrats till en DIAL-begäran."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS-begäran har ändrats till en Video DIAL-begäran."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS-begäran har ändrats till en USSD-begäran."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS-begäran har ändrats till en ny SS-begäran."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Jobbprofil"</string>
diff --git a/core/res/res/values-sw/required_apps_managed_device.xml b/core/res/res/values-sw/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-sw/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sw/required_apps_managed_profile.xml b/core/res/res/values-sw/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-sw/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sw/required_apps_managed_user.xml b/core/res/res/values-sw/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-sw/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index bdf304a..a8ab410 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -210,6 +210,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Zima"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Dharura"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Ripoti ya hitilafu"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Maliza kipindi"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Chukua ripoti ya hitilafu"</string>
<string name="bugreport_message" msgid="398447048750350456">"Hii itakusanya maelezo kuhusu hali ya kifaa chako kwa sasa, na itume kama barua pepe. Itachukua muda mfupi tangu ripoti ya hitilafu ianze kuzalishwa hadi iwe tayari kutumwa; vumilia."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Ripoti ya kushirikiana"</string>
@@ -1496,6 +1497,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Unapowasha kipengele cha njia ya mkato, hatua ya kubonyeza vitufe vyote viwili vya sauti kwa dakika 3 itafungua kipengele cha zana za walio na matatizo ya kuona au kusikia.\n\n Kipengele kilichopo cha zana za walio na matatizo ya kuona au kusikia:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Unaweza kubadilisha kipengele hiki katika Mipangilio > Zana za walio na matatizo ya kuona au kusikia."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Zima kipengele cha Njia ya Mkato"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Tumia Njia ya Mkato"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Ugeuzaji wa Rangi"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Usahihishaji wa rangi"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Njia ya mkato ya zana za walio na matatizo ya kuona au kusikia imewasha <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Njia ya mkato ya zana za walio na matatizo ya kuona au kusikia imezima <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Chagua kipengele utakachotumia, ukigonga Kitufe cha zana za walio na matatizo ya kuona au kusikia."</string>
@@ -1682,13 +1685,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Usiku wa wiki"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Wikendi"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Tukio"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Kulala"</string>
<string name="muted_by" msgid="6147073845094180001">"Sauti imezimwa na <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Kuna hitilafu ya ndani ya kifaa chako, na huenda kisiwe thabiti mpaka urejeshe mipangilio ya kiwandani."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Kuna hitilafu ya ndani ya kifaa chako. Wasiliana na mtengenezaji wa kifaa chako kwa maelezo."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Ombi la USSD limerekebishwa na kuwa ombi la DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Ombi la USSD limerekebishwa na kuwa ombi la SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Ombi la USSD limerekebishwa na kuwa ombi jipya la USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Ombi la USSD limebadilishwa kuwa ombi la DIAL la Video."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Ombi la SS limerekebishwa na kuwa ombi la DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Ombi la SS limebadilishwa kuwa ombi la DIAL la Video."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Ombi la SS limerekebishwa na kuwa ombi la USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Ombi la SS limerekebishwa na kuwa ombi jipya la SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Wasifu wa kazini"</string>
diff --git a/core/res/res/values-ta/required_apps_managed_device.xml b/core/res/res/values-ta/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-ta/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ta/required_apps_managed_profile.xml b/core/res/res/values-ta/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-ta/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ta/required_apps_managed_user.xml b/core/res/res/values-ta/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-ta/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 9b77ec0..c517223 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"முடக்கு"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"அவசர அழைப்பு"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"பிழை அறிக்கை"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"அமர்வை முடிக்கிறது"</string>
<string name="bugreport_title" msgid="2667494803742548533">"பிழை அறிக்கையை எடு"</string>
<string name="bugreport_message" msgid="398447048750350456">"உங்கள் நடப்புச் சாதன நிலையை மின்னஞ்சல் செய்தியாக அனுப்ப, அது குறித்த தகவலை இது சேகரிக்கும். பிழை அறிக்கையைத் தொடங்குவதில் இருந்து, அது அனுப்புவதற்குத் தயாராகும் வரை, இதற்குச் சிறிது நேரம் ஆகும்; பொறுமையாகக் காத்திருக்கவும்."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ஊடாடத்தக்க அறிக்கை"</string>
@@ -1051,8 +1052,7 @@
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"சிஸ்டம் அமைப்பு > பயன்பாடுகள் > பதிவிறக்கம் என்பதில் இதை மீண்டும் இயக்கவும்."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"தற்போதைய திரை அளவு அமைப்பை <xliff:g id="APP_NAME">%1$s</xliff:g> ஆதரிக்காததால், அது வழக்கத்திற்கு மாறாகச் செயல்படக்கூடும்."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"எப்போதும் காட்டு"</string>
- <!-- no translation found for unsupported_compile_sdk_message (4253168368781441759) -->
- <skip />
+ <string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> பயன்பாடானது, இந்தச் சாதனத்தின் Android OSக்கு இணக்கமற்ற பதிப்பிற்காக உருவாக்கப்பட்டதால், இதில் சரியாகச் செயல்படாது. இந்தப் பயன்பாட்டின் புதுப்பிக்கப்பட்ட பதிப்பானது தற்போது கிடைக்கக்கூடும்."</string>
<string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"எப்போதும் காட்டு"</string>
<string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"புதுப்பிப்பு உள்ளதா எனப் பார்"</string>
<string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> பயன்பாடு (செயல்முறை <xliff:g id="PROCESS">%2$s</xliff:g>), தனது சுய-செயலாக்க StrictMode கொள்கையை மீறியது."</string>
@@ -1500,6 +1500,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"குறுக்குவழி இயக்கத்தில் இருந்தால், இரண்டு ஒலியளவு பொத்தான்களையும் 3 வினாடிகள் அழுத்தி, அணுகல்தன்மை அம்சத்தை இயக்கலாம்.\n\n தற்போதைய அணுகல்தன்மை அம்சம்:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n அமைப்புகள் > அணுகல்தன்மை என்பதற்குச் சென்று, அம்சத்தை மாற்றலாம்."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"குறுக்குவழியை முடக்கு"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"குறுக்குவழியைப் பயன்படுத்து"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"வண்ணத்தை நேர் எதிராக மாற்றுதல்"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"வண்ணத் திருத்தம்"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"அணுகல்தன்மைக் குறுக்குவழியானது <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ஐ இயக்கியது"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"அணுகல்தன்மைக் குறுக்குவழியானது <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ஐ முடக்கியது"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"அணுகல்தன்மைப் பொத்தானைத் தட்டி, பயன்படுத்துவதற்கான அம்சத்தைத் தேர்ந்தெடுக்கவும்:"</string>
@@ -1639,8 +1641,7 @@
<string name="package_installed_device_owner" msgid="6875717669960212648">"உங்கள் நிர்வாகி நிறுவியுள்ளார்"</string>
<string name="package_updated_device_owner" msgid="1847154566357862089">"உங்கள் நிர்வாகி புதுப்பித்துள்ளார்"</string>
<string name="package_deleted_device_owner" msgid="2307122077550236438">"உங்கள் நிர்வாகி நீக்கியுள்ளார்"</string>
- <!-- no translation found for battery_saver_description (5394663545060026162) -->
- <skip />
+ <string name="battery_saver_description" msgid="5394663545060026162">"சாதனத்தின் பேட்டரி ஆயுளை அதிகரிப்பதற்காக, பேட்டரி சேமிப்பான் உங்கள் சாதனத்தின் செயல்திறனைச் சற்றே குறைத்து, அதிர்வு, இருப்பிடச் சேவைகள் மற்றும் பெரும்பாலான பின்புலத் தரவு போன்றவற்றைக் கட்டுப்படுத்துகிறது. ஒத்திசைவைச் சார்ந்திருக்கும் மின்னஞ்சல், செய்தியிடல் மற்றும் பிற ஆப்ஸ் ஆகியவற்றை நீங்கள் திறக்காதவரை, அவை ஒத்திசைக்கப்படாமல் இருக்கும்.\n\nஉங்கள் ஃபோன் சார்ஜ் செய்யப்படும்போது, பேட்டரி சேமிப்பான் தானாகவே ஆஃப் செய்யப்படும்."</string>
<string name="data_saver_description" msgid="6015391409098303235">"டேட்டா பயன்பாட்டைக் குறைப்பதற்கு உதவ, பின்புலத்தில் டேட்டாவை அனுப்புவது அல்லது பெறுவதிலிருந்து சில பயன்பாடுகளை டேட்டா சேமிப்பான் தடுக்கும். தற்போது பயன்படுத்தும் பயன்பாடானது எப்போதாவது டேட்டாவை அணுகலாம். எடுத்துக்காட்டாக, படங்களை நீங்கள் தட்டும் வரை அவை காட்டப்படாது."</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"டேட்டா சேமிப்பானை இயக்கவா?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"இயக்கு"</string>
@@ -1687,13 +1688,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"வார இரவு"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"வார இறுதி"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"நிகழ்வு"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"உறக்கத்தில்"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ஒலியடக்கினார்"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"சாதனத்தில் அகச் சிக்கல் இருக்கிறது, அதனை ஆரம்பநிலைக்கு மீட்டமைக்கும் வரை நிலையற்று இயங்கலாம்."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"சாதனத்தில் அகச் சிக்கல் இருக்கிறது. விவரங்களுக்கு சாதன தயாரிப்பாளரைத் தொடர்புகொள்ளவும்."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD கோரிக்கையானது DIAL கோரிக்கைக்கு மாற்றப்பட்டது."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD கோரிக்கையானது SS கோரிக்கைக்கு மாற்றப்பட்டது."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD கோரிக்கையானது புதிய USSD கோரிக்கைக்கு மாற்றப்பட்டது."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD கோரிக்கையானது வீடியோ DIAL கோரிக்கைக்கு மாற்றப்பட்டது."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS கோரிக்கையானது DIAL கோரிக்கைக்கு மாற்றப்பட்டது."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS கோரிக்கையானது வீடியோ DIAL கோரிக்கைக்கு மாற்றப்பட்டது."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS கோரிக்கையானது USSD கோரிக்கைக்கு மாற்றப்பட்டது."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS கோரிக்கையானது புதிய SS கோரிக்கைக்கு மாற்றப்பட்டது."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"பணி சுயவிவரம்"</string>
diff --git a/core/res/res/values-te/required_apps_managed_device.xml b/core/res/res/values-te/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-te/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-te/required_apps_managed_profile.xml b/core/res/res/values-te/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-te/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-te/required_apps_managed_user.xml b/core/res/res/values-te/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-te/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 8658ae9..fd103bc 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"పవర్ ఆఫ్ చేయి"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"అత్యవసరం"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"బగ్ నివేదిక"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"సెషన్ను ముగించు"</string>
<string name="bugreport_title" msgid="2667494803742548533">"బగ్ నివేదికను సిద్ధం చేయి"</string>
<string name="bugreport_message" msgid="398447048750350456">"ఇది ఇ-మెయిల్ సందేశం రూపంలో పంపడానికి మీ ప్రస్తుత పరికర స్థితి గురించి సమాచారాన్ని సేకరిస్తుంది. బగ్ నివేదికను ప్రారంభించడం మొదలుకొని పంపడానికి సిద్ధం చేసే వరకు ఇందుకు కొంత సమయం పడుతుంది; దయచేసి ఓపిక పట్టండి."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ప్రభావశీల నివేదిక"</string>
@@ -1051,8 +1052,7 @@
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"సిస్టమ్ సెట్టింగ్లు > అనువర్తనాలు > డౌన్లోడ్ చేసినవిలో దీన్ని పునఃప్రారంభించండి."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> ప్రస్తుత ప్రదర్శన పరిమాణ సెట్టింగ్కు మద్దతు ఇవ్వదు, దీని వలన ఊహించని సమస్యలు తలెత్తవచ్చు."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"ఎల్లప్పుడూ చూపు"</string>
- <!-- no translation found for unsupported_compile_sdk_message (4253168368781441759) -->
- <skip />
+ <string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"Android OS యొక్క అననుకూల వెర్షన్ కోసం <xliff:g id="APP_NAME">%1$s</xliff:g> రూపొందించబడింది మరియు ఊహించని సమస్యలు తలెత్తవచ్చు. యాప్ యొక్క అప్డేట్ చేసిన వెర్షన్ అందుబాటులో ఉండవచ్చు."</string>
<string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"ఎల్లప్పుడూ చూపు"</string>
<string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"అప్డేట్ కోసం తనిఖీ చేయి"</string>
<string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> యాప్ (<xliff:g id="PROCESS">%2$s</xliff:g> ప్రాసెస్) అది స్వయంగా అమలు చేసే ఖచ్చితమైన మోడ్ విధానాన్ని ఉల్లంఘించింది."</string>
@@ -1500,6 +1500,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"షార్ట్కట్ ఆన్లో ఉన్నప్పుడు, రెండు వాల్యూమ్ బటన్లను 3 సెకన్ల పాటు నొక్కితే యాక్సెస్ సామర్థ్య ఫీచర్ ప్రారంభం అవుతుంది.\n\n ప్రస్తుత యాక్సెస్ సామర్థ్య ఫీచర్:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n సెట్టింగ్లు > యాక్సెస్ సామర్థ్యంలో మీరు ఫీచర్ను మార్చవచ్చు."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"సత్వరమార్గాన్ని ఆఫ్ చేయి"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"సత్వరమార్గాన్ని ఉపయోగించు"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"వర్ణ విలోమం"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"రంగు సవరణ"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"యాక్సెస్ సామర్థ్య షార్ట్కట్ ద్వారా <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆన్ చేయబడింది"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"యాక్సెస్ సామర్థ్య షార్ట్కట్ ద్వారా <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆఫ్ చేయబడింది"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"యాక్సెస్ సామర్థ్య బటన్ను మీరు నొక్కినప్పుడు ఉపయోగించాల్సిన ఒక ఫీచర్ను ఎంచుకోండి:"</string>
@@ -1639,8 +1641,7 @@
<string name="package_installed_device_owner" msgid="6875717669960212648">"మీ నిర్వాహకులు ఇన్స్టాల్ చేసారు"</string>
<string name="package_updated_device_owner" msgid="1847154566357862089">"మీ నిర్వాహకులు నవీకరించారు"</string>
<string name="package_deleted_device_owner" msgid="2307122077550236438">"మీ నిర్వాహకులు తొలగించారు"</string>
- <!-- no translation found for battery_saver_description (5394663545060026162) -->
- <skip />
+ <string name="battery_saver_description" msgid="5394663545060026162">"బ్యాటరీ జీవితకాలాన్ని మెరుగుపరచడంలో సహాయపడటానికి, బ్యాటరీ సేవర్ మీ పరికరం పనితీరును తగ్గిస్తుంది మరియు వైబ్రేషన్ను, స్థాన సేవలను మరియు అత్యధిక నేపథ్య డేటాను పరిమితం చేస్తుంది. ఇమెయిల్, మెసేజింగ్ మరియు సమకాలీకరణపై ఆధారపడే ఇతర యాప్లు మీరు వాటిని తెరిస్తే మినహా అప్డేట్ చేయబడవు.\n\nమీ పరికరం ఛార్జ్ అవుతున్నప్పుడు బ్యాటరీ సేవర్ స్వయంచాలకంగా ఆఫ్ అవుతుంది."</string>
<string name="data_saver_description" msgid="6015391409098303235">"డేటా వినియోగాన్ని తగ్గించడంలో సహాయకరంగా ఉండటానికి, డేటా సేవర్ కొన్ని యాప్లను నేపథ్యంలో డేటాను పంపకుండా లేదా స్వీకరించకుండా నిరోధిస్తుంది. మీరు ప్రస్తుతం ఉపయోగిస్తున్న యాప్ డేటాను యాక్సెస్ చేయగలదు కానీ అలా అరుదుగా చేయవచ్చు. అంటే, ఉదాహరణకు, మీరు ఆ చిత్రాలను నొక్కే వరకు అవి ప్రదర్శించబడవు."</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"డేటా సేవర్ను ఆన్ చేయాలా?"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"ఆన్ చేయి"</string>
@@ -1687,13 +1688,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"వారపు రోజుల్లో రాత్రి"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"వారాంతం"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"ఈవెంట్"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"నిద్రావస్థ"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ద్వారా మ్యూట్ చేయబడింది"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"మీ పరికరంతో అంతర్గత సమస్య ఏర్పడింది మరియు మీరు ఫ్యాక్టరీ డేటా రీసెట్ చేసే వరకు అస్థిరంగా ఉంటుంది."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"మీ పరికరంతో అంతర్గత సమస్య ఏర్పడింది. వివరాల కోసం మీ తయారీదారుని సంప్రదించండి."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD అభ్యర్థన డయల్ అభ్యర్థనగా సవరించబడింది."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD అభ్యర్థన SS అభ్యర్థనగా సవరించబడింది."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD అభ్యర్థన కొత్త USSD అభ్యర్థనగా సవరించబడింది."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD అభ్యర్థన వీడియో డయల్ అభ్యర్థనగా సవరించబడింది."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS అభ్యర్థన డయల్ అభ్యర్థనగా సవరించబడింది."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS అభ్యర్థన వీడియో డయల్ అభ్యర్థనగా సవరించబడింది."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS అభ్యర్థన USSD అభ్యర్థనగా సవరించబడింది."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS అభ్యర్థన కొత్త SS అభ్యర్థనగా సవరించబడింది."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"కార్యాలయ ప్రొఫైల్"</string>
diff --git a/core/res/res/values-th/required_apps_managed_device.xml b/core/res/res/values-th/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-th/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-th/required_apps_managed_profile.xml b/core/res/res/values-th/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-th/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-th/required_apps_managed_user.xml b/core/res/res/values-th/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-th/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 026611f..2515241 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"ปิดเครื่อง"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"เหตุฉุกเฉิน"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"รายงานข้อบกพร่อง"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"จบเซสชัน"</string>
<string name="bugreport_title" msgid="2667494803742548533">"ใช้รายงานข้อบกพร่อง"</string>
<string name="bugreport_message" msgid="398447048750350456">"การดำเนินการนี้จะรวบรวมข้อมูลเกี่ยวกับสถานะปัจจุบันของอุปกรณ์ของคุณ โดยจะส่งไปในรูปแบบข้อความอีเมล อาจใช้เวลาสักครู่ตั้งแต่เริ่มการสร้างรายงานข้อบกพร่องจนกระทั่งเสร็จสมบูรณ์ โปรดอดทนรอ"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"รายงานแบบอินเทอร์แอกทีฟ"</string>
@@ -1051,7 +1052,7 @@
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"เปิดใช้งานอีกครั้งในการตั้งค่าระบบ > แอปพลิเคชัน > ดาวน์โหลด"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> ไม่สนับสนุนการตั้งค่าขนาดการแสดงผลปัจจุบันและอาจแสดงผลผิดปกติ"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"แสดงเสมอ"</string>
- <string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> สร้างมาสำหรับระบบปฏิบัติการ Android เวอร์ชันที่ใช้ร่วมกันไม่ได้ และอาจแสดงผลผิดปกติ ทั้งนี้ แอปเวอร์ชันอัปเดตอาจพร้อมใช้งานแล้ว"</string>
+ <string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> ไม่ได้สร้างมาเพื่อใช้งานกับระบบปฏิบัติการ Android เวอร์ชันนี้ จึงอาจแสดงผลผิดปกติ ทั้งนี้ แอปเวอร์ชันอัปเดตอาจพร้อมใช้งานแล้ว"</string>
<string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"แสดงเสมอ"</string>
<string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"ตรวจสอบอัปเดต"</string>
<string name="smv_application" msgid="3307209192155442829">"แอปพลิเคชัน <xliff:g id="APPLICATION">%1$s</xliff:g> (กระบวนการ <xliff:g id="PROCESS">%2$s</xliff:g>) ละเมิดนโยบาย StrictMode ที่บังคับใช้ด้วยตัวเอง"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"เมื่อทางลัดเปิดอยู่ การกดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มเป็นเวลา 3 วินาทีจะเริ่มฟีเจอร์การเข้าถึงพิเศษ\n\n ฟีเจอร์การเข้าถึงพิเศษปัจจุบัน:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n คุณสามารถเปลี่ยนฟีเจอร์ในการตั้งค่า > การเข้าถึงพิเศษ"</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"ปิดทางลัด"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"ใช้ทางลัด"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"การกลับสี"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"การปรับแก้สี"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ทางลัดการเข้าถึงเปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ทางลัดการเข้าถึงปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"เลือกฟีเจอร์ที่จะใช้เมื่อคุณแตะปุ่ม \"การเข้าถึงพิเศษ\":"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"คืนวันธรรมดา"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"สุดสัปดาห์"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"กิจกรรม"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"นอนหลับ"</string>
<string name="muted_by" msgid="6147073845094180001">"ปิดเสียงโดย <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"อุปกรณ์ของคุณเกิดปัญหาภายในเครื่อง อุปกรณ์อาจทำงานไม่เสถียรจนกว่าคุณจะรีเซ็ตข้อมูลเป็นค่าเริ่มต้น"</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"อุปกรณ์ของคุณเกิดปัญหาภายในเครื่อง โปรดติดต่อผู้ผลิตเพื่อขอรายละเอียดเพิ่มเติม"</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"คำขอ USSD ได้รับการแก้ไขให้เป็นคำขอ DIAL"</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"คำขอ USSD ได้รับการแก้ไขให้เป็นคำขอ SS"</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"คำขอ USSD ได้รับการแก้ไขให้เป็นคำขอ USSD ใหม่"</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"คำขอ USSD ได้รับการแก้ไขให้เป็นคำขอ Video DIAL"</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"คำขอ SS ได้รับการแก้ไขให้เป็นคำขอ DIAL"</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"คำขอ SS ได้รับการแก้ไขให้เป็นคำขอ Video DIAL"</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"คำขอ SS ได้รับการแก้ไขให้เป็นคำขอ USSD"</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"คำขอ SS ได้รับการแก้ไขให้เป็นคำขอ SS ใหม่"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"โปรไฟล์งาน"</string>
diff --git a/core/res/res/values-tl/required_apps_managed_device.xml b/core/res/res/values-tl/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-tl/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-tl/required_apps_managed_profile.xml b/core/res/res/values-tl/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-tl/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-tl/required_apps_managed_user.xml b/core/res/res/values-tl/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-tl/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 7f4063a..d30267b 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"I-off"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Ulat sa bug"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Tapusin ang session"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Kunin ang ulat sa bug"</string>
<string name="bugreport_message" msgid="398447048750350456">"Mangongolekta ito ng impormasyon tungkol sa kasalukuyang katayuan ng iyong device, na ipapadala bilang mensaheng e-mail. Gugugol ito ng kaunting oras mula sa pagsisimula ng ulat sa bug hanggang sa handa na itong maipadala; mangyaring magpasensya."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interactive na ulat"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Kapag naka-on ang shortcut, magsisimula ang isang feature ng pagiging naa-access kapag pinindot ang parehong button ng volume sa loob ng 3 segundo.\n\n Kasalukuyang feature ng pagiging naa-access:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Maaari mong baguhin ang feature sa Mga Setting > Pagiging Naa-access."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"I-off ang Shortcut"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Gamitin ang Shortcut"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Pag-invert ng Kulay"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Pagwawasto ng Kulay"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Na-on ng Shortcut sa Accessibility ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Na-off ng Shortcut sa Accessibility ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Pumili ng feature na gagamitin kapag na-tap mo ang button ng Pagiging Naa-access:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Weeknight"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Weekend"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Kaganapan"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Pag-sleep"</string>
<string name="muted_by" msgid="6147073845094180001">"Na-mute ng <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"May internal na problema sa iyong device, at maaaring hindi ito maging stable hanggang sa i-reset mo ang factory data."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"May internal na problema sa iyong device. Makipag-ugnayan sa iyong manufacturer upang malaman ang mga detalye."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Ginawang DIAL request ang USSD request."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Ginawang SS request ang USSD request."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Ginawang bagong USSD request ang USSD request."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Ginawang kahilingan sa Video DIAL ang kahilingan sa USSD."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Ginawang DIAL request ang SS request."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Ginawang kahilingan sa Video DIAL ang kahilingan sa SS."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Ginawang USSD request ang SS request."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Ginawang bagong SS request ang SS request."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Profile sa trabaho"</string>
diff --git a/core/res/res/values-tr/required_apps_managed_device.xml b/core/res/res/values-tr/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-tr/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-tr/required_apps_managed_profile.xml b/core/res/res/values-tr/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-tr/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-tr/required_apps_managed_user.xml b/core/res/res/values-tr/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-tr/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 47792b4..e6646c4 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Kapat"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Acil durum"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Hata raporu"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Oturumu sonlandır"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Hata raporu al"</string>
<string name="bugreport_message" msgid="398447048750350456">"Bu rapor, e-posta iletisi olarak göndermek üzere cihazınızın şu anki durumuyla ilgili bilgi toplar. Hata raporu başlatıldıktan sonra hazır olması biraz zaman alabilir, lütfen sabırlı olun."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Etkileşimli rapor"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Kısayol açık olduğunda, ses düğmelerinin ikisini birden 3 saniyeliğine basılı tutmanız bir erişilebilirlik özelliğini başlatır.\n\n Geçerli erişilebilirlik özelliği:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Özelliği, Ayarlar > Erişilebilirlik seçeneğinden değiştirebilirsiniz."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Kısayolu Kapat"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Kısayolu Kullan"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Rengi Ters Çevirme"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Renk Düzeltme"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Erişilebilirlik Kısayolu <xliff:g id="SERVICE_NAME">%1$s</xliff:g> hizmetini açtı"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Erişilebilirlik Kısayolu <xliff:g id="SERVICE_NAME">%1$s</xliff:g> hizmetini kapattı"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Erişilebilirlik düğmesine dokunduğunuzda kullanmak üzere bir özellik seçin:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Hafta içi gece"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Hafta sonu"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Etkinlik"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Uyku"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> tarafından kapatıldı"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Cihazınızla ilgili dahili bir sorun oluştu ve fabrika verilerine sıfırlama işlemi gerçekleştirilene kadar kararsız çalışabilir."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Cihazınızla ilgili dahili bir sorun oluştu. Ayrıntılı bilgi için üreticinizle iletişim kurun."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD isteği DIAL isteği olarak değiştirildi."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD isteği SS isteği olarak değiştirildi."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD isteği yeni USSD isteği olarak değiştirildi."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD isteği Video DIAL isteği olarak değiştirildi."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS isteği DIAL isteği olarak değiştirildi."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS isteği, Video DIAL isteği olarak değiştirildi."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS isteği USSD isteği olarak değiştirildi."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS isteği yeni SS isteği olarak değiştirildi."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"İş profili"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index ad6c6bc..bfbe902 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -216,8 +216,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Вимкнути"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Екстрений виклик"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Звіт про помилки"</string>
- <!-- no translation found for global_action_logout (935179188218826050) -->
- <skip />
+ <string name="global_action_logout" msgid="935179188218826050">"Завершити сеанс"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Звіт про помилку"</string>
<string name="bugreport_message" msgid="398447048750350456">"Інформація про поточний стан вашого пристрою буде зібрана й надіслана електронною поштою. Підготовка звіту триватиме певний час."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Інтерактивний звіт"</string>
@@ -1548,10 +1547,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Коли ярлик увімкнено, після натискання обох клавіш гучності й утримування їх протягом 3 секунд увімкнеться функція спеціальних можливостей.\n\n Поточна функція спеціальних можливостей:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Цю функцію можна змінити в меню \"Налаштування\" > \"Спеціальні можливості\"."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Вимкнути ярлик"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Використовувати ярлик"</string>
- <!-- no translation found for color_inversion_feature_name (4231186527799958644) -->
- <skip />
- <!-- no translation found for color_correction_feature_name (6779391426096954933) -->
- <skip />
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Інверсія кольорів"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Корекція кольорів"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Ярлик спеціальних можливостей увімкнув <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Ярлик спеціальних можливостей вимкнув <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Виберіть функцію для кнопки спеціальних можливостей:"</string>
@@ -1756,15 +1753,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Увечері в будні"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"На вихідних"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Подія"</string>
- <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) -->
- <skip />
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Під час сну"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> вимикає звук"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Через внутрішню помилку ваш пристрій може працювати нестабільно. Відновіть заводські налаштування."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"На пристрої сталася внутрішня помилка. Зв’яжіться з виробником пристрою, щоб дізнатися більше."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Запит USSD перетворено на запит DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Запит USSD перетворено на запит SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Запит USSD перетворено на новий запит USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Запит USSD перетворено на запит Video DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Запит SS перетворено на запит DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Запит SS перетворено на запит Video DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Запит SS перетворено на запит USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Запит SS перетворено на новий запит SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Робочий профіль"</string>
diff --git a/core/res/res/values-ur/required_apps_managed_device.xml b/core/res/res/values-ur/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-ur/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ur/required_apps_managed_profile.xml b/core/res/res/values-ur/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-ur/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ur/required_apps_managed_user.xml b/core/res/res/values-ur/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-ur/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 6774754..2771432 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"پاور آف"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"ایمرجنسی"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"بگ کی اطلاع"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"سیشن ختم کریں"</string>
<string name="bugreport_title" msgid="2667494803742548533">"بگ کی اطلاع لیں"</string>
<string name="bugreport_message" msgid="398447048750350456">"ایک ای میل پیغام کے بطور بھیجنے کیلئے، یہ آپ کے موجودہ آلہ کی حالت کے بارے میں معلومات جمع کرے گا۔ بگ کی اطلاع شروع کرنے سے لے کر بھیجنے کیلئے تیار ہونے تک اس میں تھوڑا وقت لگے گا؛ براہ کرم تحمل سے کام لیں۔"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"متعامل رپورٹ"</string>
@@ -1051,8 +1052,7 @@
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"سسٹم ترتیبات > ایپس > ڈاؤن لوڈ کردہ میں اسے دوبارہ فعال کریں۔"</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> میں موجودہ ڈسپلے سائز ترتیبات کی معاونت نہیں ہے اور ہو سکتا ہے غیر متوقع طریقے سے کام کرے۔"</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"ہمیشہ دکھائیں"</string>
- <!-- no translation found for unsupported_compile_sdk_message (4253168368781441759) -->
- <skip />
+ <string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> کو Android OS کے غیر مطابقت پذیر ورژن کیلئے بنایا گیا اور یہ غیر متوقع برتاؤ کر سکتی ہے۔ ممکن ہے ایپ کا اپ ڈیٹ کردہ ورژن دستیاب ہو۔"</string>
<string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"ہمیشہ دکھائیں"</string>
<string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"اپ ڈیٹ چیک کریں"</string>
<string name="smv_application" msgid="3307209192155442829">"ایپ <xliff:g id="APPLICATION">%1$s</xliff:g> (کارروائی <xliff:g id="PROCESS">%2$s</xliff:g>) نے خود نافذ کی گئی StrictMode پالیسی کی خلاف ورزی کی ہے۔"</string>
@@ -1500,6 +1500,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"شارٹ کٹ آن ہونے پر، 3 سیکنڈ تک دونوں والیوم بٹنز کو دبانے سے ایک ایکسیسبیلٹی خصوصیت شروع ہو جائے گی۔\n\n موجودہ ایکسیسبیلٹی خصوصیت:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n آپ خصوصیت کو ترتیبات > ایکسیسبیلٹی میں جا کر تبدیل کر سکتے ہیں۔"</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"شارٹ کٹ آف کریں"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"شارٹ کٹ استعمال کریں"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"رنگ میں تبدیلی"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"رنگ کی تصحیح"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"ایکسیسبیلٹی شارٹ کٹ نے <xliff:g id="SERVICE_NAME">%1$s</xliff:g> کو آن کر دیا"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"ایکسیسبیلٹی شارٹ کٹ نے <xliff:g id="SERVICE_NAME">%1$s</xliff:g> کو آف کر دیا"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"ایکسیسبیلٹی بٹن پر تھپتھپانے وقت استعمال کرنے کیلئے ایک خصوصیت چنیں:"</string>
@@ -1639,8 +1641,7 @@
<string name="package_installed_device_owner" msgid="6875717669960212648">"آپ کے منتظم کے ذریعے انسٹال کیا گیا"</string>
<string name="package_updated_device_owner" msgid="1847154566357862089">"آپ کے منتظم کے ذریعے اپ ڈیٹ کیا گیا"</string>
<string name="package_deleted_device_owner" msgid="2307122077550236438">"آپ کے منتظم کے ذریعے حذف کیا گیا"</string>
- <!-- no translation found for battery_saver_description (5394663545060026162) -->
- <skip />
+ <string name="battery_saver_description" msgid="5394663545060026162">"بیٹری کی میعاد بہتر بنانے میں مدد کرنے کیلئے، بیٹری سیور آپ کے آلہ کی کارکردگی کم کر دیتی ہے اور وائبریشن، مقام کی سروسز اور پس منظر کا بیشتر ڈیٹا محدود کر دیتی ہے۔ ای میل، پیغام رسانی اور مطابقت پذیری پر منحصر دیگر ایپس ممکن ہے اس وقت تک اپ ڈیٹ نہ ہوں جب تک آپ انہیں نہ کھولیں۔\n\nآلہ چارج ہوتے وقت بیٹری سیور خود بخود آف ہو جاتی ہے۔"</string>
<string name="data_saver_description" msgid="6015391409098303235">"ڈیٹا کے استعمال کو کم کرنے میں مدد کیلئے، ڈیٹا سیور پس منظر میں کچھ ایپس کو ڈیٹا بھیجنے یا موصول کرنے سے روکتا ہے۔ آپ جو ایپ فی الحال استعمال کر رہے ہیں وہ ڈیٹا پر رسائی کر سکتی ہے مگر ہو سکتا ہے ایسا زیادہ نہ ہو۔ اس کا مطلب مثال کے طور پر یہ ہو سکتا ہے کہ تصاویر تھپتھپانے تک ظاہر نہ ہوں۔"</string>
<string name="data_saver_enable_title" msgid="4674073932722787417">"ڈیٹا سیور آن کریں؟"</string>
<string name="data_saver_enable_button" msgid="7147735965247211818">"آن کریں"</string>
@@ -1687,13 +1688,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"ویک نائٹ"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"ویک اینڈ"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"ایونٹ"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"سونا"</string>
<string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> کے ذریعے خاموش کردہ"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"آپ کے آلہ میں ایک داخلی مسئلہ ہے اور جب تک آپ فیکٹری ڈیٹا کو دوبارہ ترتیب نہیں دے دیتے ہیں، ہوسکتا ہے کہ یہ غیر مستحکم رہے۔"</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"آپ کے آلہ میں ایک داخلی مسئلہ ہے۔ تفصیلات کیلئے اپنے مینوفیکچرر سے رابطہ کریں۔"</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD درخواست میں ترمیم کر کے DIAL درخواست بنا دی گئی ہے۔"</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD درخواست میں ترمیم کر کے SS درخواست بنا دی گئی ہے۔"</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD درخواست میں ترمیم کر کے نئی USSD درخواست بنا دی گئی ہے۔"</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD درخواست کو ویڈیو DIAL درخواست میں تبدیل کر دیا گیا ہے۔"</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS درخواست میں ترمیم کر کے DIAL درخواست بنا دی گئی ہے۔"</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS درخواست کو ویڈیو DIAL درخواست میں تبدیل کر دیا گیا ہے۔"</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS درخواست میں ترمیم کر کے USSD درخواست بنا دی گئی ہے۔"</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS درخواست میں ترمیم کر کے نئی SS درخواست بنا دی گئی ہے۔"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"دفتری پروفائل"</string>
diff --git a/core/res/res/values-uz/required_apps_managed_device.xml b/core/res/res/values-uz/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-uz/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-uz/required_apps_managed_profile.xml b/core/res/res/values-uz/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-uz/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-uz/required_apps_managed_user.xml b/core/res/res/values-uz/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-uz/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 52f46df..c488473 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"O‘chirish"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Favqulodda chaqiruv"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Nosozlik haqida ma’lumot berish"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Seansni yakunlash"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Xatoliklar hisoboti"</string>
<string name="bugreport_message" msgid="398447048750350456">"Qurilmangiz holati haqidagi ma’lumotlar to‘planib, e-pochta orqali yuboriladi. Hisobotni tayyorlash biroz vaqt olishi mumkin."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktiv hisobot"</string>
@@ -1416,7 +1417,7 @@
<string name="fingerprints" msgid="4516019619850763049">"Barmoq izlari:"</string>
<string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 barmoq izi:"</string>
<string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 barmoq izi:"</string>
- <string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Barchasini ko‘rish"</string>
+ <string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Hammasi"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Harakat turini tanlang"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"Ulashish"</string>
<string name="sending" msgid="3245653681008218030">"Jo‘natilmoqda…"</string>
@@ -1499,6 +1500,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Maxsus imkoniyatlar funksiyasidan foydalanish uchun u yoniqligida ikkala ovoz balandligini boshqarish tugmasini 3 soniya bosib turing.\n\n Joriy maxsus imkoniyatlar funksiyasi:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Bu funksiyani Sozlamalar > Maxsus imkoniyatlar orqali o‘zgartirish mumkin."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Tezkor ishga tushirishni o‘chirib qo‘yish"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Tezkor ishga tushirishdan foydalanish"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Ranglarni akslantirish"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Rangni tuzatish"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> xizmati yoqildi"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> xizmati o‘chirib qo‘yildi"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Maxsus imkoniyatlar tugmasi bosilganda ishga tushadigan funksiyani tanlang:"</string>
@@ -1685,13 +1688,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Ish kunlari kechqurun"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Dam olish kunlari"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Tadbir"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Uyquda"</string>
<string name="muted_by" msgid="6147073845094180001">"“<xliff:g id="THIRD_PARTY">%1$s</xliff:g>” tomonidan ovozsiz qilingan"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Qurilmangiz bilan bog‘liq ichki muammo mavjud. U zavod sozlamalari tiklanmaguncha barqaror ishlamasligi mumkin."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Qurilmangiz bilan bog‘liq ichki muammo mavjud. Tafsilotlar uchun qurilmangiz ishlab chiqaruvchisiga murojaat qiling."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD so‘rovi DIAL so‘roviga o‘zgartirildi."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD so‘rovi SS so‘roviga o‘zgartirildi."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD so‘rovi yangi USSD so‘roviga o‘zgartirildi."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD so‘rovi Video DIAL so‘roviga o‘zgartirildi."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS so‘rovi DIAL so‘roviga o‘zgartirildi."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS so‘rovi Video DIAL so‘roviga o‘zgartirildi."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS so‘rovi USSD so‘roviga o‘zgartirildi."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS so‘rovi yangi SS so‘roviga o‘zgartirildi."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Ishchi profil"</string>
diff --git a/core/res/res/values-vi/required_apps_managed_device.xml b/core/res/res/values-vi/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-vi/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-vi/required_apps_managed_profile.xml b/core/res/res/values-vi/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-vi/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-vi/required_apps_managed_user.xml b/core/res/res/values-vi/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-vi/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 0ffbe00..5f72834 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Tắt nguồn"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Khẩn cấp"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Báo cáo lỗi"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Kết thúc phiên"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Nhận báo cáo lỗi"</string>
<string name="bugreport_message" msgid="398447048750350456">"Báo cáo này sẽ thu thập thông tin về tình trạng thiết bị hiện tại của bạn, để gửi dưới dạng thông báo qua email. Sẽ mất một chút thời gian kể từ khi bắt đầu báo cáo lỗi cho tới khi báo cáo sẵn sàng để gửi; xin vui lòng kiên nhẫn."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Báo cáo tương tác"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Khi phím tắt được bật, nhấn cả hai nút âm lượng trong 3 giây sẽ bắt đầu một tính năng trợ năng.\n\n Tính năng trợ năng hiện tại:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Bạn có thể thay đổi tính năng trong Cài đặt > Trợ năng."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Tắt phím tắt"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Sử dụng phím tắt"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Đảo màu"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Sửa màu"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Đã bật phím tắt trợ năng <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Đã tắt phím tắt trợ năng <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Chọn tính năng sẽ sử dụng khi bạn nhấn nút Trợ năng:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Đêm trong tuần"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Cuối tuần"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Sự kiện"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Khi đang ngủ"</string>
<string name="muted_by" msgid="6147073845094180001">"Do <xliff:g id="THIRD_PARTY">%1$s</xliff:g> tắt tiếng"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Đã xảy ra sự cố nội bộ với thiết bị của bạn và thiết bị có thể sẽ không ổn định cho tới khi bạn thiết lập lại dữ liệu ban đầu."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Đã xảy ra sự cố nội bộ với thiết bị. Hãy liên hệ với nhà sản xuất của bạn để biết chi tiết."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Yêu cầu USSD được sửa đổi thành yêu cầu DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Yêu cầu USSD được sửa đổi thành yêu cầu SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Yêu cầu USSD được sửa đổi thành yêu cầu USSD mới."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Yêu cầu USSD được sửa đổi thành yêu cầu DIAL video."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Yêu cầu SS được sửa đổi thành yêu cầu DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Yêu cầu SS được sửa đổi thành yêu cầu DIAL video."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Yêu cầu SS được sửa đổi thành yêu cầu USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Yêu cầu SS được sửa đổi thành yêu cầu SS mới."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Hồ sơ công việc"</string>
diff --git a/core/res/res/values-zh-rCN/required_apps_managed_device.xml b/core/res/res/values-zh-rCN/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-zh-rCN/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-zh-rCN/required_apps_managed_profile.xml b/core/res/res/values-zh-rCN/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-zh-rCN/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-zh-rCN/required_apps_managed_user.xml b/core/res/res/values-zh-rCN/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-zh-rCN/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 1765a7e..028c082 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"关机"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"紧急呼救"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"错误报告"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"结束会话"</string>
<string name="bugreport_title" msgid="2667494803742548533">"提交错误报告"</string>
<string name="bugreport_message" msgid="398447048750350456">"这会收集有关当前设备状态的信息,并以电子邮件的形式进行发送。从开始生成错误报告到准备好发送需要一点时间,请耐心等待。"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"互动式报告"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"开启快捷方式后,同时按下两个音量按钮 3 秒钟即可启动所设定的无障碍功能。\n\n当前设定的无障碍功能:\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n如需更改设定的功能,请依次转到“设置”>“无障碍”。"</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"关闭快捷方式"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"使用快捷方式"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"颜色反转"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"色彩校正"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"无障碍快捷方式已开启<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"无障碍快捷方式已关闭<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"选择按下“无障碍”按钮时要使用的功能:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"周一至周五夜间"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"周末"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"活动"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"睡眠"</string>
<string name="muted_by" msgid="6147073845094180001">"已被<xliff:g id="THIRD_PARTY">%1$s</xliff:g>设为静音"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"您的设备内部出现了问题。如果不将设备恢复出厂设置,设备运行可能会不稳定。"</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"您的设备内部出现了问题。请联系您的设备制造商了解详情。"</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD 请求已修改为 DIAL 请求。"</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD 请求已修改为 SS 请求。"</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD 请求已修改为新的 USSD 请求。"</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD 请求已修改为视频 DIAL 请求。"</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS 请求已修改为 DIAL 请求。"</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS 请求已修改为视频 DIAL 请求。"</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS 请求已修改为 USSD 请求。"</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS 请求已修改为新的 SS 请求。"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"工作资料"</string>
diff --git a/core/res/res/values-zh-rHK/required_apps_managed_device.xml b/core/res/res/values-zh-rHK/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-zh-rHK/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-zh-rHK/required_apps_managed_profile.xml b/core/res/res/values-zh-rHK/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-zh-rHK/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-zh-rHK/required_apps_managed_user.xml b/core/res/res/values-zh-rHK/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-zh-rHK/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 47bf16b..2da0d4a 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"關閉"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"緊急"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"錯誤報告"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"結束工作階段"</string>
<string name="bugreport_title" msgid="2667494803742548533">"取得錯誤報告"</string>
<string name="bugreport_message" msgid="398447048750350456">"這會收集您目前裝置狀態的相關資訊,並以電郵傳送給您。從開始建立錯誤報告到準備傳送需要一段時間,請耐心等候。"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"互動報告"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"快速鍵開啟後,同時按住音量按鈕 3 秒,無障礙功能便會啟用。\n\n目前的無障礙功能:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n如要變更功能,請前往「設定」>「無障礙功能」。"</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"關閉快速鍵"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"使用快速鍵"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"色彩反轉"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"色彩校正"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"無障礙功能快速鍵已啟用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"無障礙功能快速鍵已停用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"請選擇輕按「無障礙功能」按鈕時使用的功能:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"平日夜間"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"週末"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"活動"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"睡眠"</string>
<string name="muted_by" msgid="6147073845094180001">"靜音設定者:<xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"您裝置的系統發生問題,回復原廠設定後即可解決該問題。"</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"您裝置的系統發生問題,請聯絡您的製造商瞭解詳情。"</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD 要求已修改為 DIAL 要求。"</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD 要求已修改為 SS 要求。"</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD 要求已修改為新的 USSD 要求。"</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD 要求已修改為影片 DIAL 要求。"</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS 要求已修改為 DIAL 要求。"</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS 要求已修改為影片 DIAL 要求。"</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS 要求已修改為 USSD 要求。"</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS 要求已修改為新的 SS 要求。"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"工作設定檔"</string>
diff --git a/core/res/res/values-zh-rTW/required_apps_managed_device.xml b/core/res/res/values-zh-rTW/required_apps_managed_device.xml
new file mode 100644
index 0000000..9044fcc
--- /dev/null
+++ b/core/res/res/values-zh-rTW/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_device">
+ <item msgid="1104492179978792509">"com.android.settings"</item>
+ <item msgid="7004798084799227194">"com.android.contacts"</item>
+ <item msgid="5782220690863647256">"com.android.dialer"</item>
+ <item msgid="5746338511138092673">"com.android.stk"</item>
+ <item msgid="1712599182168590664">"com.android.providers.downloads"</item>
+ <item msgid="2858239953396384085">"com.android.providers.downloads.ui"</item>
+ <item msgid="3892021562839042708">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-zh-rTW/required_apps_managed_profile.xml b/core/res/res/values-zh-rTW/required_apps_managed_profile.xml
new file mode 100644
index 0000000..4296b0d
--- /dev/null
+++ b/core/res/res/values-zh-rTW/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_profile">
+ <item msgid="1457364287544474838">"com.android.contacts"</item>
+ <item msgid="4633145750237794002">"com.android.settings"</item>
+ <item msgid="6518205098643077579">"com.android.providers.downloads"</item>
+ <item msgid="9003577256117829525">"com.android.providers.downloads.ui"</item>
+ <item msgid="6106837921940099371">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-zh-rTW/required_apps_managed_user.xml b/core/res/res/values-zh-rTW/required_apps_managed_user.xml
new file mode 100644
index 0000000..1a7ade9
--- /dev/null
+++ b/core/res/res/values-zh-rTW/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="required_apps_managed_user">
+ <item msgid="4823915868435007499">"com.android.settings"</item>
+ <item msgid="2250259015310893915">"com.android.contacts"</item>
+ <item msgid="7166574999426592423">"com.android.dialer"</item>
+ <item msgid="7306937186458176744">"com.android.stk"</item>
+ <item msgid="7415441588151512455">"com.android.providers.downloads"</item>
+ <item msgid="2277950048461066377">"com.android.providers.downloads.ui"</item>
+ <item msgid="8640522622655589373">"com.android.documentsui"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index f050dfe..4f2492f 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"關機"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"緊急電話"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"錯誤報告"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"結束"</string>
<string name="bugreport_title" msgid="2667494803742548533">"取得錯誤報告"</string>
<string name="bugreport_message" msgid="398447048750350456">"這會收集你目前裝置狀態的相關資訊,以便透過電子郵件傳送。從錯誤報告開始建立到準備傳送的這段過程可能需要一點時間,敬請耐心等候。"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"互動式報告"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"啟用捷徑功能後,只要同時按下兩個音量鍵 3 秒,就能啟動協助工具功能。\n\n 目前設定的協助工具功能為:\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n 如要變更設定的功能,請依序輕觸 [設定] > [協助工具]。"</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"停用捷徑"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"使用捷徑"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"色彩反轉"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"色彩校正"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"協助工具捷徑啟用了「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"協助工具捷徑停用了「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"輕觸 [協助工具] 按鈕後,選擇你想使用的功能:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"週間晚上"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"週末"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"活動"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"睡眠"</string>
<string name="muted_by" msgid="6147073845094180001">"由 <xliff:g id="THIRD_PARTY">%1$s</xliff:g> 設為靜音"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"你的裝置發生內部問題,必須將裝置恢復原廠設定才能解除不穩定狀態。"</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"你的裝置發生內部問題,詳情請洽裝置製造商。"</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"USSD 要求已改為 DIAL 要求。"</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"USSD 要求已改為 SS 要求。"</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"USSD 要求已改為新的 USSD 要求。"</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"USSD 要求已改為影片 DIAL 要求。"</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"SS 要求已改為 DIAL 要求。"</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"SS 要求已改為影片 DIAL 要求。"</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"SS 要求已改為 USSD 要求。"</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"SS 要求已改為新的 SS 要求。"</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Work 設定檔"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 3311d79a..2977a85 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -212,6 +212,7 @@
<string name="global_action_power_off" msgid="4471879440839879722">"Vala amandla"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"Isimo esiphuthumayo"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Umbiko wephutha"</string>
+ <string name="global_action_logout" msgid="935179188218826050">"Phothula isikhathi"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Thatha umbiko wesiphazamiso"</string>
<string name="bugreport_message" msgid="398447048750350456">"Lokhu kuzoqoqa ulwazi mayelana nesimo samanje sedivayisi yakho, ukuthumela imilayezo ye-imeyili. Kuzothatha isikhathi esincane kusuka ekuqaleni umbiko wesiphazamiso uze ulungele ukuthunyelwa; sicela ubekezele."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Umbiko obandakanyayo"</string>
@@ -1498,6 +1499,8 @@
<string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Uma kuvulwe isinqamuleli, ukucindezela zombili izinkinobho zevolumu amasekhondi angu-3 kuzoqala isici sokufinyelela.\n\n Isici samanje sokufinyelela:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Ungashintsha isici kuzilungiselelo > Ukufinyelela."</string>
<string name="disable_accessibility_shortcut" msgid="627625354248453445">"Vala isinqamuleli"</string>
<string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Sebenzisa isinqamuleli"</string>
+ <string name="color_inversion_feature_name" msgid="4231186527799958644">"Ukuguqulwa kombala"</string>
+ <string name="color_correction_feature_name" msgid="6779391426096954933">"Ukulungiswa kombala"</string>
<string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Isinqamuleli sokufinyelela sivule i-<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Isinqamuleli sokufinyelela sivale i-<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Khetha isici ozosisebenzisa uma uthepha inkinobho yokufinyelela:"</string>
@@ -1684,13 +1687,16 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Ubusuku beviki"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Ngempelasonto"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"Umcimbi"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Ulele"</string>
<string name="muted_by" msgid="6147073845094180001">"Ithuliswe ngu-<xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"Kukhona inkinga yangaphakathi ngedivayisi yakho, futhi ingase ibe engazinzile kuze kube yilapho usetha kabusha yonke idatha."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"Kukhona inkinga yangaphakathi ngedivayisi yakho. Xhumana nomkhiqizi wakho ukuze uthole imininingwane."</string>
<string name="stk_cc_ussd_to_dial" msgid="5202342984749947872">"Isicelo se-USSD siguqulelwe kusicelo se-DIAL."</string>
<string name="stk_cc_ussd_to_ss" msgid="2345360594181405482">"Isicelo se-USSD siguqulelwe kusicelo se-SS."</string>
<string name="stk_cc_ussd_to_ussd" msgid="7466087659967191653">"Isicelo se-USSD siguqulelwe kusicelo esisha se-USSD."</string>
+ <string name="stk_cc_ussd_to_dial_video" msgid="585340552561515305">"Isicelo se-USSD siguqulelwe kusicelo se-DIAL."</string>
<string name="stk_cc_ss_to_dial" msgid="2151304435775557162">"Isicelo se-SS siguqulelwe kusicelo se-DIAL."</string>
+ <string name="stk_cc_ss_to_dial_video" msgid="4306210904450719045">"Isicelo se-SS siguqulelwe kusicelo se-DIAL."</string>
<string name="stk_cc_ss_to_ussd" msgid="3951862188105305589">"Isicelo se-SS siguqulelwe kusicelo se-USSD."</string>
<string name="stk_cc_ss_to_ss" msgid="5470768854991452695">"Isicelo se-SS siguqulelwe kusicelo esisha se-SS."</string>
<string name="notification_work_profile_content_description" msgid="4600554564103770764">"Iphrofayela yomsebenzi"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 51ffa60..64febf1 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4442,7 +4442,7 @@
<attr name="textColor" />
<!-- Size of the text. Recommended dimension type for text is "sp" for scaled-pixels (example: 15sp). -->
<attr name="textSize" />
- <!-- Style (bold, italic, bolditalic) for the text. -->
+ <!-- Style (normal, bold, italic, bold|italic) for the text. -->
<attr name="textStyle" />
<!-- Typeface (normal, sans, serif, monospace) for the text. -->
<attr name="typeface" />
@@ -4527,7 +4527,7 @@
<attr name="textScaleX" format="float" />
<!-- Typeface (normal, sans, serif, monospace) for the text. -->
<attr name="typeface" />
- <!-- Style (bold, italic, bolditalic) for the text. -->
+ <!-- Style (normal, bold, italic, bold|italic) for the text. -->
<attr name="textStyle" />
<!-- Font family (named by string or as a font resource reference) for the text. -->
<attr name="fontFamily" />
@@ -7729,6 +7729,10 @@
wallpaper. -->
<attr name="showMetadataInPreview" format="boolean" />
+ <!-- Wallpapers optimized and capable of drawing in ambient mode will return true.
+ @hide -->
+ <attr name="supportsAmbientMode" format="boolean" />
+
</declare-styleable>
<!-- Use <code>dream</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index e87b9d8..efbe9c2 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1375,6 +1375,13 @@
{@link android.os.Build.VERSION#CODENAME Build.VERSION.CODENAME}. -->
<attr name="compileSdkVersionCodename" format="string" />
+ <!-- The (optional) fully-qualified name for a subclass of
+ {@link android.app.AppComponentFactory} that the system uses to instantiate
+ every other manifest defined class. Most applications
+ don't need this attribute. If it's not specified, the system
+ instantiates items without it.-->
+ <attr name="appComponentFactory" format="string" />
+
<!-- The <code>manifest</code> tag is the root of an
<code>AndroidManifest.xml</code> file,
describing the contents of an Android package (.apk) file. One
@@ -1546,6 +1553,8 @@
<!-- Declares the kind of classloader this application's classes must be loaded with -->
<attr name="classLoader" />
+ <attr name="appComponentFactory" />
+
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
used to control access from other packages to specific components or
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0b38d1b..dc791cf 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -963,12 +963,6 @@
-->
<integer name="config_longPressOnBackBehavior">0</integer>
- <!-- Control the behavior when the user panic presses the back button.
- 0 - Nothing
- 1 - Go to home
- -->
- <integer name="config_backPanicBehavior">0</integer>
-
<!-- Control the behavior when the user short presses the power button.
0 - Nothing
1 - Go to sleep (doze)
@@ -1301,6 +1295,22 @@
<integer-array name="config_autoBrightnessLcdBacklightValues">
</integer-array>
+ <!-- Array of desired screen brightness in nits corresponding to the lux values
+ in the config_autoBrightnessLevels array. As with config_screenBrightnessMinimumNits and
+ config_screenBrightnessMaximumNits, the display brightness is defined as the measured
+ brightness of an all-white image.
+
+ If this is defined then:
+ - config_autoBrightnessLcdBacklightValues should not be defined
+ - config_screenBrightnessMinimumNits must be defined
+ - config_screenBrightnessMaximumNits must be defined
+
+ This array should have size one greater than the size of the config_autoBrightnessLevels
+ array. The brightness values must be non-negative and non-decreasing. This must be
+ overridden in platform specific overlays -->
+ <array name="config_autoBrightnessDisplayValuesNits">
+ </array>
+
<!-- Array of output values for button backlight corresponding to the LUX values
in the config_autoBrightnessLevels array. This array should have size one greater
than the size of the config_autoBrightnessLevels array.
@@ -1337,6 +1347,29 @@
<item>200</item>
</integer-array>
+ <!-- The minimum brightness of the display in nits. On OLED displays this should be measured
+ with an all white image while the display is fully on and the backlight is set to
+ config_screenBrightnessSettingMinimum or config_screenBrightnessSettingDark, whichever
+ is darker.
+
+ If this and config_screenBrightnessMinimumNits are set, then the display's brightness
+ range is assumed to be linear between
+ (config_screenBrightnessSettingMinimum, config_screenBrightnessMinimumNits) and
+ (config_screenBrightnessSettingMaximum, config_screenBrightnessMaximumNits). -->
+ <item name="config_screenBrightnessMinimumNits" format="float" type="dimen">-1.0</item>
+
+ <!-- The maximum brightness of the display in nits. On OLED displays this should be measured
+ with an all white image while the display is fully on and the "backlight" is set to
+ config_screenBrightnessSettingMaximum. Note that this value should *not* reflect the
+ maximum brightness value for any high brightness modes but only the maximum brightness
+ value obtainable in a sustainable manner.
+
+ If this and config_screenBrightnessMinimumNits are set to something non-negative, then the
+ display's brightness range is assumed to be linear between
+ (config_screenBrightnessSettingMinimum, config_screenBrightnessMaximumNits) and
+ (config_screenBrightnessSettingMaximum, config_screenBrightnessMaximumNits). -->
+ <item name="config_screenBrightnessMaximumNits" format="float" type="dimen">-1.0</item>
+
<!-- Array of ambient lux threshold values. This is used for determining hysteresis constraint
values by calculating the index to use for lookup and then setting the constraint value
to the corresponding value of the array. The new brightening hysteresis constraint value
@@ -1387,6 +1420,9 @@
<!-- True if WallpaperService is enabled -->
<bool name="config_enableWallpaperService">true</bool>
+ <!-- Class name of WallpaperManagerService. -->
+ <string name="config_wallpaperManagerServiceName">com.android.server.wallpaper.WallpaperManagerService</string>
+
<!-- Enables the TimeZoneRuleManager service. This is the master switch for the updateable time
zone update mechanism. -->
<bool name="config_enableUpdateableTimeZoneRules">false</bool>
@@ -2061,6 +2097,10 @@
movement threshold where scrolling should begin. -->
<dimen name="config_viewConfigurationTouchSlop">8dp</dimen>
+ <!-- Base "hover slop" value used by ViewConfiguration as a
+ movement threshold under which hover is considered "stationary". -->
+ <dimen name="config_viewConfigurationHoverSlop">4dp</dimen>
+
<!-- Minimum velocity to initiate a fling, as measured in dips per second. -->
<dimen name="config_viewMinFlingVelocity">50dp</dimen>
diff --git a/core/res/res/values/disallowed_apps_managed_device.xml b/core/res/res/values/disallowed_apps_managed_device.xml
new file mode 100644
index 0000000..8940b15
--- /dev/null
+++ b/core/res/res/values/disallowed_apps_managed_device.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources>
+ <!-- A list of apps to be removed from the managed device. -->
+ <string-array name="disallowed_apps_managed_device">
+ </string-array>
+</resources>
diff --git a/core/res/res/values/disallowed_apps_managed_profile.xml b/core/res/res/values/disallowed_apps_managed_profile.xml
new file mode 100644
index 0000000..e3a513f
--- /dev/null
+++ b/core/res/res/values/disallowed_apps_managed_profile.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources>
+ <!-- A list of apps to be removed from the managed profile. -->
+ <string-array name="disallowed_apps_managed_profile">
+ </string-array>
+</resources>
diff --git a/core/res/res/values/disallowed_apps_managed_user.xml b/core/res/res/values/disallowed_apps_managed_user.xml
new file mode 100644
index 0000000..b7b645d
--- /dev/null
+++ b/core/res/res/values/disallowed_apps_managed_user.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources>
+ <!-- A list of apps to be removed from the managed user. -->
+ <string-array name="disallowed_apps_managed_user">
+ </string-array>
+</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 373a656..8540ff1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2859,6 +2859,7 @@
<!-- @hide @SystemApi -->
<public name="isVrOnly"/>
<public name="widgetFeatures" />
+ <public name="appComponentFactory" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/required_apps_managed_device.xml b/core/res/res/values/required_apps_managed_device.xml
new file mode 100644
index 0000000..0ac706f
--- /dev/null
+++ b/core/res/res/values/required_apps_managed_device.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources>
+ <!-- A list of apps to be retained on the managed device.
+ Takes precedence over the disallowed apps lists. -->
+ <string-array name="required_apps_managed_device">
+ <item>com.android.settings</item>
+ <item>com.android.contacts</item>
+ <item>com.android.dialer</item>
+ <item>com.android.stk</item> <!-- Required by com.android.phone by certain carriers -->
+ <item>com.android.providers.downloads</item>
+ <item>com.android.providers.downloads.ui</item>
+ <item>com.android.documentsui</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values/required_apps_managed_profile.xml b/core/res/res/values/required_apps_managed_profile.xml
new file mode 100644
index 0000000..a0b8492
--- /dev/null
+++ b/core/res/res/values/required_apps_managed_profile.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources>
+ <!-- A list of apps to be retained in the managed profile.
+ Takes precedence over the disallowed apps lists. -->
+ <string-array name="required_apps_managed_profile">
+ <item>com.android.contacts</item>
+ <item>com.android.settings</item>
+ <item>com.android.providers.downloads</item>
+ <item>com.android.providers.downloads.ui</item>
+ <item>com.android.documentsui</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values/required_apps_managed_user.xml b/core/res/res/values/required_apps_managed_user.xml
new file mode 100644
index 0000000..e8fdb21
--- /dev/null
+++ b/core/res/res/values/required_apps_managed_user.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources>
+ <!-- A list of apps to be retained on the managed user.
+ Takes precedence over the disallowed apps lists. -->
+ <string-array name="required_apps_managed_user">
+ <item>com.android.settings</item>
+ <item>com.android.contacts</item>
+ <item>com.android.dialer</item>
+ <item>com.android.stk</item> <!-- Required by com.android.phone by certain carriers -->
+ <item>com.android.providers.downloads</item>
+ <item>com.android.providers.downloads.ui</item>
+ <item>com.android.documentsui</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 5ccaf5c..5783435 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3014,6 +3014,13 @@
<!-- Notification action name for opening the wifi picker, showing the user all the nearby networks. -->
<string name="wifi_available_action_all_networks">All Networks</string>
+ <!--Notification title for Wi-Fi Wake onboarding. This is displayed the first time a user disables Wi-Fi with the feature enabled. -->
+ <string name="wifi_wakeup_onboarding_title">Wi\u2011Fi will turn on automatically</string>
+ <!--Notification subtext for Wi-Fi Wake onboarding.-->
+ <string name="wifi_wakeup_onboarding_subtext">When you\'re near a high quality saved network</string>
+ <!--Notification action to disable Wi-Fi Wake during onboarding.-->
+ <string name="wifi_wakeup_onboarding_action_disable">Don\'t turn back on</string>
+
<!-- A notification is shown when a wifi captive portal network is detected. This is the notification's title. -->
<string name="wifi_available_sign_in">Sign in to Wi-Fi network</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f659360..7f71446 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -287,6 +287,7 @@
<java-symbol type="bool" name="split_action_bar_is_narrow" />
<java-symbol type="bool" name="config_useVolumeKeySounds" />
<java-symbol type="bool" name="config_enableWallpaperService" />
+ <java-symbol type="string" name="config_wallpaperManagerServiceName" />
<java-symbol type="bool" name="config_enableUpdateableTimeZoneRules" />
<java-symbol type="bool" name="config_timeZoneRulesUpdateTrackingEnabled" />
<java-symbol type="string" name="config_timeZoneRulesUpdaterPackage" />
@@ -424,7 +425,6 @@
<java-symbol type="integer" name="config_veryLongPressOnPowerBehavior" />
<java-symbol type="integer" name="config_veryLongPressTimeout" />
<java-symbol type="integer" name="config_longPressOnBackBehavior" />
- <java-symbol type="integer" name="config_backPanicBehavior" />
<java-symbol type="integer" name="config_lowMemoryKillerMinFreeKbytesAdjust" />
<java-symbol type="integer" name="config_lowMemoryKillerMinFreeKbytesAbsolute" />
<java-symbol type="integer" name="config_max_pan_devices" />
@@ -470,6 +470,7 @@
<java-symbol type="dimen" name="config_minScrollbarTouchTarget" />
<java-symbol type="dimen" name="config_prefDialogWidth" />
<java-symbol type="dimen" name="config_viewConfigurationTouchSlop" />
+ <java-symbol type="dimen" name="config_viewConfigurationHoverSlop" />
<java-symbol type="dimen" name="config_viewMinFlingVelocity" />
<java-symbol type="dimen" name="config_viewMaxFlingVelocity" />
<java-symbol type="dimen" name="config_scrollbarSize" />
@@ -1213,6 +1214,18 @@
<java-symbol type="array" name="config_telephonyHardware" />
<java-symbol type="array" name="config_keySystemUuidMapping" />
<java-symbol type="array" name="config_gpsParameters" />
+ <java-symbol type="array" name="required_apps_managed_user" />
+ <java-symbol type="array" name="required_apps_managed_profile" />
+ <java-symbol type="array" name="required_apps_managed_device" />
+ <java-symbol type="array" name="disallowed_apps_managed_user" />
+ <java-symbol type="array" name="disallowed_apps_managed_profile" />
+ <java-symbol type="array" name="disallowed_apps_managed_device" />
+ <java-symbol type="array" name="vendor_required_apps_managed_user" />
+ <java-symbol type="array" name="vendor_required_apps_managed_profile" />
+ <java-symbol type="array" name="vendor_required_apps_managed_device" />
+ <java-symbol type="array" name="vendor_disallowed_apps_managed_user" />
+ <java-symbol type="array" name="vendor_disallowed_apps_managed_profile" />
+ <java-symbol type="array" name="vendor_disallowed_apps_managed_device" />
<java-symbol type="drawable" name="default_wallpaper" />
<java-symbol type="drawable" name="default_lock_wallpaper" />
@@ -1295,6 +1308,7 @@
<java-symbol type="drawable" name="platlogo" />
<java-symbol type="drawable" name="stat_notify_sync_error" />
<java-symbol type="drawable" name="stat_notify_wifi_in_range" />
+ <java-symbol type="drawable" name="ic_wifi_settings" />
<java-symbol type="drawable" name="ic_wifi_signal_0" />
<java-symbol type="drawable" name="ic_wifi_signal_1" />
<java-symbol type="drawable" name="ic_wifi_signal_2" />
@@ -1892,6 +1906,9 @@
<java-symbol type="string" name="wifi_available_content_failed_to_connect" />
<java-symbol type="string" name="wifi_available_action_connect" />
<java-symbol type="string" name="wifi_available_action_all_networks" />
+ <java-symbol type="string" name="wifi_wakeup_onboarding_title" />
+ <java-symbol type="string" name="wifi_wakeup_onboarding_subtext" />
+ <java-symbol type="string" name="wifi_wakeup_onboarding_action_disable" />
<java-symbol type="string" name="accessibility_binding_label" />
<java-symbol type="string" name="adb_active_notification_message" />
<java-symbol type="string" name="adb_active_notification_title" />
@@ -3173,4 +3190,8 @@
<java-symbol type="string" name="global_action_logout" />
<java-symbol type="drawable" name="ic_logout" />
+
+ <java-symbol type="dimen" name="config_screenBrightnessMinimumNits" />
+ <java-symbol type="dimen" name="config_screenBrightnessMaximumNits" />
+ <java-symbol type="array" name="config_autoBrightnessDisplayValuesNits" />
</resources>
diff --git a/core/res/res/values/vendor_disallowed_apps_managed_device.xml b/core/res/res/values/vendor_disallowed_apps_managed_device.xml
new file mode 100644
index 0000000..c826d27
--- /dev/null
+++ b/core/res/res/values/vendor_disallowed_apps_managed_device.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources>
+ <!-- A list of apps to be removed from the managed device by a particular vendor. -->
+ <string-array name="vendor_disallowed_apps_managed_device">
+ </string-array>
+</resources>
diff --git a/core/res/res/values/vendor_disallowed_apps_managed_profile.xml b/core/res/res/values/vendor_disallowed_apps_managed_profile.xml
new file mode 100644
index 0000000..5fcb2778
--- /dev/null
+++ b/core/res/res/values/vendor_disallowed_apps_managed_profile.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources>
+ <!-- A list of apps to be removed from the managed profile by a particular vendor. -->
+ <string-array name="vendor_disallowed_apps_managed_profile">
+ </string-array>
+</resources>
diff --git a/core/res/res/values/vendor_disallowed_apps_managed_user.xml b/core/res/res/values/vendor_disallowed_apps_managed_user.xml
new file mode 100644
index 0000000..3355d77
--- /dev/null
+++ b/core/res/res/values/vendor_disallowed_apps_managed_user.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources>
+ <!-- A list of apps to be removed from the managed user by a particular vendor. -->
+ <string-array name="vendor_disallowed_apps_managed_user">
+ </string-array>
+</resources>
diff --git a/core/res/res/values/vendor_required_apps_managed_device.xml b/core/res/res/values/vendor_required_apps_managed_device.xml
new file mode 100644
index 0000000..e684e22
--- /dev/null
+++ b/core/res/res/values/vendor_required_apps_managed_device.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources>
+ <!-- A list of apps to be retained on the managed device by a particular vendor.
+ Takes precedence over the disallowed apps lists. -->
+ <string-array name="vendor_required_apps_managed_device">
+ </string-array>
+</resources>
diff --git a/core/res/res/values/vendor_required_apps_managed_profile.xml b/core/res/res/values/vendor_required_apps_managed_profile.xml
new file mode 100644
index 0000000..4a3edf8
--- /dev/null
+++ b/core/res/res/values/vendor_required_apps_managed_profile.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources>
+ <!-- A list of apps to be retained in the managed profile by a particular vendor.
+ Takes precedence over the disallowed apps lists. -->
+ <string-array name="vendor_required_apps_managed_profile">
+ </string-array>
+</resources>
diff --git a/core/res/res/values/vendor_required_apps_managed_user.xml b/core/res/res/values/vendor_required_apps_managed_user.xml
new file mode 100644
index 0000000..71dbd62
--- /dev/null
+++ b/core/res/res/values/vendor_required_apps_managed_user.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<resources>
+ <!-- A list of apps to be retained on the managed user by a particular vendor.
+ Takes precedence over the disallowed apps lists. -->
+ <string-array name="vendor_required_apps_managed_user">
+ </string-array>
+</resources>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 3f2a46a..e094772 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1051,7 +1051,7 @@
</intent-filter>
</activity>
- <activity android:name=".menus.MenuLayoutPortrait" android:label="MenuLayoutPortrait"
+ <activity android:name="android.view.menu.MenuLayoutPortrait" android:label="MenuLayoutPortrait"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttf b/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttf
deleted file mode 100644
index cf769ed..0000000
--- a/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttf
+++ /dev/null
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttx b/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttx
deleted file mode 100644
index 04d85922..0000000
--- a/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttx
+++ /dev/null
@@ -1,234 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
--->
-<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.9">
-
- <GlyphOrder>
- <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
- <GlyphID id="0" name=".notdef"/>
- <GlyphID id="1" name="space"/>
- <GlyphID id="2" name="R"/>
- <GlyphID id="3" name="a"/>
- <GlyphID id="4" name="y"/>
- </GlyphOrder>
-
- <head>
- <!-- Most of this table will be recalculated by the compiler -->
- <tableVersion value="1.0"/>
- <fontRevision value="1.0"/>
- <checkSumAdjustment value="0x26d0d624"/>
- <magicNumber value="0x5f0f3cf5"/>
- <flags value="00000000 00000011"/>
- <unitsPerEm value="1000"/>
- <created value="Tue Oct 3 23:00:00 2017"/>
- <modified value="Tue Oct 3 23:33:15 2017"/>
- <xMin value="-1500"/>
- <yMin value="0"/>
- <xMax value="5000"/>
- <yMax value="1000"/>
- <macStyle value="00000000 00000000"/>
- <lowestRecPPEM value="7"/>
- <fontDirectionHint value="2"/>
- <indexToLocFormat value="0"/>
- <glyphDataFormat value="0"/>
- </head>
-
- <hhea>
- <tableVersion value="0x00010000"/>
- <ascent value="1000"/>
- <descent value="-200"/>
- <lineGap value="0"/>
- <advanceWidthMax value="1000"/>
- <minLeftSideBearing value="-1500"/>
- <minRightSideBearing value="-4000"/>
- <xMaxExtent value="5000"/>
- <caretSlopeRise value="1"/>
- <caretSlopeRun value="0"/>
- <caretOffset value="0"/>
- <reserved0 value="0"/>
- <reserved1 value="0"/>
- <reserved2 value="0"/>
- <reserved3 value="0"/>
- <metricDataFormat value="0"/>
- <numberOfHMetrics value="1"/>
- </hhea>
-
- <maxp>
- <!-- Most of this table will be recalculated by the compiler -->
- <tableVersion value="0x10000"/>
- <numGlyphs value="5"/>
- <maxPoints value="4"/>
- <maxContours value="1"/>
- <maxCompositePoints value="0"/>
- <maxCompositeContours value="0"/>
- <maxZones value="0"/>
- <maxTwilightPoints value="0"/>
- <maxStorage value="0"/>
- <maxFunctionDefs value="0"/>
- <maxInstructionDefs value="0"/>
- <maxStackElements value="0"/>
- <maxSizeOfInstructions value="0"/>
- <maxComponentElements value="0"/>
- <maxComponentDepth value="0"/>
- </maxp>
-
- <OS_2>
- <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
- will be recalculated by the compiler -->
- <version value="3"/>
- <xAvgCharWidth value="594"/>
- <usWeightClass value="400"/>
- <usWidthClass value="5"/>
- <fsType value="00000000 00001000"/>
- <ySubscriptXSize value="650"/>
- <ySubscriptYSize value="600"/>
- <ySubscriptXOffset value="0"/>
- <ySubscriptYOffset value="75"/>
- <ySuperscriptXSize value="650"/>
- <ySuperscriptYSize value="600"/>
- <ySuperscriptXOffset value="0"/>
- <ySuperscriptYOffset value="350"/>
- <yStrikeoutSize value="50"/>
- <yStrikeoutPosition value="300"/>
- <sFamilyClass value="0"/>
- <panose>
- <bFamilyType value="0"/>
- <bSerifStyle value="0"/>
- <bWeight value="5"/>
- <bProportion value="0"/>
- <bContrast value="0"/>
- <bStrokeVariation value="0"/>
- <bArmStyle value="0"/>
- <bLetterForm value="0"/>
- <bMidline value="0"/>
- <bXHeight value="0"/>
- </panose>
- <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
- <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
- <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
- <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
- <achVendID value="UKWN"/>
- <fsSelection value="00000000 01000000"/>
- <usFirstCharIndex value="32"/>
- <usLastCharIndex value="121"/>
- <sTypoAscender value="800"/>
- <sTypoDescender value="-200"/>
- <sTypoLineGap value="200"/>
- <usWinAscent value="1000"/>
- <usWinDescent value="200"/>
- <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
- <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
- <sxHeight value="500"/>
- <sCapHeight value="700"/>
- <usDefaultChar value="0"/>
- <usBreakChar value="32"/>
- <usMaxContext value="0"/>
- </OS_2>
-
- <hmtx>
- <mtx name=".notdef" width="1000" lsb="0"/>
- <mtx name="R" width="1000" lsb="0"/>
- <mtx name="a" width="1000" lsb="0"/>
- <mtx name="space" width="1000" lsb="0"/>
- <mtx name="y" width="1000" lsb="-1500"/>
- </hmtx>
-
- <cmap>
- <tableVersion version="0"/>
- <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="64" language="0" nGroups="4">
- <map code="0x20" name="space"/><!-- SPACE -->
- <map code="0x52" name="R"/><!-- LATIN CAPITAL LETTER R -->
- <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
- <map code="0x79" name="y"/><!-- LATIN SMALL LETTER Y -->
- </cmap_format_12>
- </cmap>
-
- <loca>
- <!-- The 'loca' table will be calculated by the compiler -->
- </loca>
-
- <glyf>
-
- <!-- The xMin, yMin, xMax and yMax values
- will be recalculated by the compiler. -->
-
- <TTGlyph name=".notdef"/><!-- contains no outline data -->
-
- <TTGlyph name="R" xMin="0" yMin="0" xMax="5000" yMax="1000">
- <contour>
- <pt x="0" y="0" on="1"/>
- <pt x="0" y="1000" on="1"/>
- <pt x="5000" y="1000" on="1"/>
- <pt x="5000" y="0" on="1"/>
- </contour>
- <instructions/>
- </TTGlyph>
-
- <TTGlyph name="a"/><!-- contains no outline data -->
-
- <TTGlyph name="space"/><!-- contains no outline data -->
-
- <TTGlyph name="y" xMin="-1500" yMin="0" xMax="1000" yMax="1000">
- <contour>
- <pt x="-1500" y="0" on="1"/>
- <pt x="-1500" y="1000" on="1"/>
- <pt x="1000" y="1000" on="1"/>
- <pt x="1000" y="0" on="1"/>
- </contour>
- <instructions/>
- </TTGlyph>
-
- </glyf>
-
- <name>
- <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
- Font for LineBreakingOverhangsTest
- </namerecord>
- <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
- Regular
- </namerecord>
- <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
- Font for LineBreakingOverhangsTest
- </namerecord>
- <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
- SampleFont-Regular
- </namerecord>
- <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
- Sample Font
- </namerecord>
- <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
- Regular
- </namerecord>
- <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
- Sample Font
- </namerecord>
- <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
- SampleFont-Regular
- </namerecord>
- </name>
-
- <post>
- <formatType value="3.0"/>
- <italicAngle value="0.0"/>
- <underlinePosition value="-75"/>
- <underlineThickness value="50"/>
- <isFixedPitch value="0"/>
- <minMemType42 value="0"/>
- <maxMemType42 value="0"/>
- <minMemType1 value="0"/>
- <maxMemType1 value="0"/>
- </post>
-
-</ttFont>
diff --git a/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java b/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java
index 3c81853..e26bdf5 100644
--- a/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java
@@ -15,6 +15,7 @@
*/
package android.animation;
+import android.support.test.filters.LargeTest;
import android.test.ActivityInstrumentationTestCase2;
import java.util.HashSet;
@@ -24,6 +25,7 @@
import com.android.frameworks.coretests.R;
+@LargeTest
public class AnimatorInflaterTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
Set<Integer> identityHashes = new HashSet<Integer>();
diff --git a/core/tests/coretests/src/android/animation/StateListAnimatorTest.java b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
index 38df78d..a9961e1 100644
--- a/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
+++ b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
@@ -17,6 +17,7 @@
package android.animation;
+import android.support.test.filters.LargeTest;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
import android.util.StateSet;
@@ -27,7 +28,7 @@
import java.util.concurrent.atomic.AtomicInteger;
-
+@LargeTest
public class StateListAnimatorTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
public StateListAnimatorTest() {
diff --git a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
index f60bf94..063bef7 100644
--- a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
+++ b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
@@ -22,6 +22,7 @@
import android.content.pm.PackageInfo;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
+import android.support.test.filters.LargeTest;
import junit.framework.TestCase;
@@ -34,6 +35,7 @@
import static android.os.storage.VolumeInfo.STATE_MOUNTED;
import static android.os.storage.VolumeInfo.STATE_UNMOUNTED;
+@LargeTest
public class ApplicationPackageManagerTest extends TestCase {
private static final String sInternalVolPath = "/data";
private static final String sAdoptedVolPath = "/mnt/expand/123";
diff --git a/core/tests/coretests/src/android/app/InstrumentationTest.java b/core/tests/coretests/src/android/app/InstrumentationTest.java
index ee3834c..9b59da4 100644
--- a/core/tests/coretests/src/android/app/InstrumentationTest.java
+++ b/core/tests/coretests/src/android/app/InstrumentationTest.java
@@ -17,8 +17,10 @@
package android.app;
import android.os.Bundle;
+import android.support.test.filters.LargeTest;
import android.test.InstrumentationTestCase;
+@LargeTest
public class InstrumentationTest extends InstrumentationTestCase {
/**
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index c14dc90..7183934 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -214,6 +214,20 @@
assertTrue(n.allPendingIntents.contains(intent));
}
+ @Test
+ public void testMessagingStyle_isGroupConversation() {
+ Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
+ .setGroupConversation(true);
+ Notification notification = new Notification.Builder(mContext, "test id")
+ .setSmallIcon(1)
+ .setContentTitle("test title")
+ .setStyle(messagingStyle)
+ .build();
+
+ assertTrue(messagingStyle.isGroupConversation());
+ assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
+ }
+
private Notification.Builder getMediaNotification() {
MediaSession session = new MediaSession(mContext, "test");
return new Notification.Builder(mContext, "color")
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
index e9e8bfc..13e70eb 100644
--- a/core/tests/coretests/src/android/app/activity/BroadcastTest.java
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -27,12 +27,13 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.UserHandle;
+import android.support.test.filters.LargeTest;
import android.test.FlakyTest;
-import android.test.suitebuilder.annotation.Suppress;
import android.util.Log;
import java.util.Arrays;
+@LargeTest
public class BroadcastTest extends ActivityTestsBase {
public static final int BROADCAST_TIMEOUT = 5 * 1000;
diff --git a/core/tests/coretests/src/android/app/activity/IntentSenderTest.java b/core/tests/coretests/src/android/app/activity/IntentSenderTest.java
index 3c30915..8c1d79b 100644
--- a/core/tests/coretests/src/android/app/activity/IntentSenderTest.java
+++ b/core/tests/coretests/src/android/app/activity/IntentSenderTest.java
@@ -20,10 +20,10 @@
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
-import android.test.suitebuilder.annotation.Suppress;
import android.os.Bundle;
-import android.test.suitebuilder.annotation.Suppress;
+import android.support.test.filters.LargeTest;
+@LargeTest
public class IntentSenderTest extends BroadcastTest {
public void testRegisteredReceivePermissionGranted() throws Exception {
diff --git a/core/tests/coretests/src/android/app/backup/BackupDataTest.java b/core/tests/coretests/src/android/app/backup/BackupDataTest.java
index 0c204e0..5b8e481 100644
--- a/core/tests/coretests/src/android/app/backup/BackupDataTest.java
+++ b/core/tests/coretests/src/android/app/backup/BackupDataTest.java
@@ -23,6 +23,7 @@
import android.os.Bundle;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
+import android.support.test.filters.LargeTest;
import android.test.AndroidTestCase;
import android.test.InstrumentationTestCase;
import android.util.Base64;
@@ -41,6 +42,7 @@
import java.lang.Exception;
import java.nio.ByteBuffer;
+@LargeTest
public class BackupDataTest extends AndroidTestCase {
private static final String KEY1 = "key1";
private static final String KEY2 = "key2a";
diff --git a/core/tests/coretests/src/android/app/backup/FullBackupTest.java b/core/tests/coretests/src/android/app/backup/FullBackupTest.java
index 3869cd2..bc6fc15 100644
--- a/core/tests/coretests/src/android/app/backup/FullBackupTest.java
+++ b/core/tests/coretests/src/android/app/backup/FullBackupTest.java
@@ -20,6 +20,7 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.support.test.filters.LargeTest;
import android.test.AndroidTestCase;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -36,6 +37,7 @@
import java.util.Map;
import java.util.Set;
+@LargeTest
public class FullBackupTest extends AndroidTestCase {
private XmlPullParserFactory mFactory;
private XmlPullParser mXpp;
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index aefc47e..c19a343 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -42,7 +42,8 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
-@Presubmit
+// TODO: b/70616950
+//@Presubmit
public class ObjectPoolTests {
// 1. Check if two obtained objects from pool are not the same.
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 4b1f2da..e4de526 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -513,6 +513,12 @@
}
@Override
+ public void dumpMemInfoProto(ParcelFileDescriptor parcelFileDescriptor,
+ Debug.MemoryInfo memoryInfo, boolean b, boolean b1, boolean b2,
+ boolean b3, String[] strings) throws RemoteException {
+ }
+
+ @Override
public void dumpGfxInfo(ParcelFileDescriptor parcelFileDescriptor, String[] strings)
throws RemoteException {
}
diff --git a/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java b/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
index 70a0877..e20645c 100644
--- a/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
+++ b/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
@@ -21,12 +21,14 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
+import android.support.test.filters.LargeTest;
import org.junit.Test;
/**
* Tests for {@link DistroFormatVersion}.
*/
+@LargeTest
public class DistroFormatVersionTest {
@Test
diff --git a/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java b/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
index eecae46..b69054c 100644
--- a/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
+++ b/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
@@ -21,12 +21,14 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
+import android.support.test.filters.LargeTest;
import org.junit.Test;
/**
* Tests for {@link DistroRulesVersion}.
*/
+@LargeTest
public class DistroRulesVersionTest {
@Test
diff --git a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
index 99abe24..dd46240 100644
--- a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
+++ b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
@@ -23,12 +23,14 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
+import android.support.test.filters.LargeTest;
import org.junit.Test;
/**
* Tests for {@link RulesState}.
*/
+@LargeTest
public class RulesStateTest {
@Test
diff --git a/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java b/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
index 91f8ebc..e4aac50 100644
--- a/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
+++ b/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
+import android.support.test.filters.LargeTest;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
@@ -33,6 +34,7 @@
/**
* Tests for {@link RulesUpdaterContract}.
*/
+@LargeTest
public class RulesUpdaterContractTest {
@Test
diff --git a/core/tests/coretests/src/android/content/RestrictionsManagerTest.java b/core/tests/coretests/src/android/content/RestrictionsManagerTest.java
index d92eece..10d74f7 100644
--- a/core/tests/coretests/src/android/content/RestrictionsManagerTest.java
+++ b/core/tests/coretests/src/android/content/RestrictionsManagerTest.java
@@ -17,6 +17,7 @@
import android.os.Bundle;
import android.os.Parcelable;
+import android.support.test.filters.LargeTest;
import android.test.AndroidTestCase;
import java.util.Arrays;
@@ -24,6 +25,7 @@
import java.util.List;
import java.util.Set;
+@LargeTest
public class RestrictionsManagerTest extends AndroidTestCase {
private RestrictionsManager mRm;
diff --git a/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java b/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java
index 948e722..659f9ea 100644
--- a/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java
+++ b/core/tests/coretests/src/android/content/pm/MacAuthenticatedInputStreamTest.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.support.test.filters.LargeTest;
import android.test.AndroidTestCase;
import java.io.ByteArrayInputStream;
@@ -27,6 +28,7 @@
import libcore.io.Streams;
+@LargeTest
public class MacAuthenticatedInputStreamTest extends AndroidTestCase {
private static final SecretKey HMAC_KEY_1 = new SecretKeySpec("test_key_1".getBytes(), "HMAC");
diff --git a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
index a9d19b4..952bb55 100644
--- a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
+++ b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
@@ -2,6 +2,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.test.filters.LargeTest;
import junit.framework.TestCase;
@@ -9,6 +10,7 @@
import java.util.Collections;
import java.util.List;
+@LargeTest
public class ParceledListSliceTest extends TestCase {
public void testSmallList() throws Exception {
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index 271c639..d3d1f22a 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -21,6 +21,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.support.test.filters.LargeTest;
import android.test.AndroidTestCase;
import android.util.AttributeSet;
import android.util.SparseArray;
@@ -43,6 +44,7 @@
/**
* Tests for {@link android.content.pm.RegisteredServicesCache}
*/
+@LargeTest
public class RegisteredServicesCacheTest extends AndroidTestCase {
private static final int U0 = 0;
private static final int U1 = 1;
diff --git a/core/tests/coretests/src/android/content/pm/SignatureTest.java b/core/tests/coretests/src/android/content/pm/SignatureTest.java
index 89d5997..a3fa1a9 100644
--- a/core/tests/coretests/src/android/content/pm/SignatureTest.java
+++ b/core/tests/coretests/src/android/content/pm/SignatureTest.java
@@ -16,8 +16,11 @@
package android.content.pm;
+import android.support.test.filters.LargeTest;
+
import junit.framework.TestCase;
+@LargeTest
public class SignatureTest extends TestCase {
/** Cert A with valid syntax */
diff --git a/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java b/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java
index d963812..68942cb 100644
--- a/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java
+++ b/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java
@@ -19,6 +19,7 @@
import android.content.pm.VerificationParams;
import android.net.Uri;
import android.os.Parcel;
+import android.support.test.filters.LargeTest;
import android.test.AndroidTestCase;
/**
@@ -27,6 +28,7 @@
* To test run:
* ./development/testrunner/runtest.py frameworks-core -c android.content.pm.VerificationParamsTest
*/
+@LargeTest
public class VerificationParamsTest extends AndroidTestCase {
private final static String VERIFICATION_URI_STRING = "http://verification.uri/path";
diff --git a/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java b/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java
index cb13eb7..88d7a59 100644
--- a/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java
+++ b/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java
@@ -17,9 +17,11 @@
package android.content.pm;
import android.os.Parcel;
+import android.support.test.filters.LargeTest;
import java.util.Random;
+@LargeTest
public class VerifierDeviceIdentityTest extends android.test.AndroidTestCase {
private static final long TEST_1 = 0x7A5F00FF5A55AAA5L;
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java
new file mode 100644
index 0000000..230655d
--- /dev/null
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.database.sqlite;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.database.DatabaseUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+/**
+ * Tests for {@link SQLiteCompatibilityWalFlags}
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SQLiteCompatibilityWalFlagsTest {
+
+ private SQLiteDatabase mDatabase;
+
+ @After
+ public void tearDown() {
+ SQLiteCompatibilityWalFlags.reset();
+ if (mDatabase != null) {
+ mDatabase.close();
+ SQLiteDatabase.deleteDatabase(new File(mDatabase.getPath()));
+ }
+ }
+
+ @Test
+ public void testParseConfig() {
+ SQLiteCompatibilityWalFlags.init("");
+ assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet());
+
+ SQLiteCompatibilityWalFlags.init(null);
+ assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet());
+
+ SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=false,wal_syncmode=OFF");
+ assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet());
+ assertFalse(SQLiteCompatibilityWalFlags.isCompatibilityWalSupported());
+ assertEquals("OFF", SQLiteCompatibilityWalFlags.getWALSyncMode());
+
+ SQLiteCompatibilityWalFlags.init("wal_syncmode=VALUE");
+ assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet());
+ assertEquals(SQLiteGlobal.isCompatibilityWalSupported(),
+ SQLiteCompatibilityWalFlags.isCompatibilityWalSupported());
+ assertEquals("VALUE", SQLiteCompatibilityWalFlags.getWALSyncMode());
+
+ SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=true");
+ assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet());
+ assertEquals(SQLiteGlobal.getWALSyncMode(),
+ SQLiteCompatibilityWalFlags.getWALSyncMode());
+ assertTrue(SQLiteCompatibilityWalFlags.isCompatibilityWalSupported());
+
+ SQLiteCompatibilityWalFlags.reset();
+ SQLiteCompatibilityWalFlags.init("Invalid value");
+ assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet());
+ }
+
+ @Test
+ public void testApplyFlags() {
+ Context ctx = InstrumentationRegistry.getContext();
+
+ SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=true,wal_syncmode=NORMAL");
+ mDatabase = SQLiteDatabase
+ .openOrCreateDatabase(ctx.getDatabasePath("SQLiteCompatibilityWalFlagsTest"), null);
+ String journalMode = DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null);
+ assertEquals("WAL", journalMode.toUpperCase());
+ String syncMode = DatabaseUtils.stringForQuery(mDatabase, "PRAGMA synchronous", null);
+ assertEquals("Normal mode (1) is expected", "1", syncMode);
+ }
+
+
+}
diff --git a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
index 7550cb5..b28a4b5 100644
--- a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
@@ -15,6 +15,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.support.test.filters.LargeTest;
import android.test.AndroidTestCase;
import android.util.Log;
import android.util.PathParser;
@@ -23,6 +24,7 @@
import java.util.Arrays;
import org.junit.Test;
+@LargeTest
public class AdaptiveIconDrawableTest extends AndroidTestCase {
public static final String TAG = AdaptiveIconDrawableTest.class.getSimpleName();
diff --git a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
new file mode 100644
index 0000000..bad0d25
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.hardware.display;
+
+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 android.util.Pair;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BrightnessConfigurationTest {
+ private static final float[] LUX_LEVELS = {
+ 0f,
+ 10f,
+ 100f,
+ };
+
+ private static final float[] NITS_LEVELS = {
+ 0.5f,
+ 90f,
+ 100f,
+ };
+
+ @Test
+ public void testSetCurveIsUnmodified() {
+ BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
+ builder.setCurve(LUX_LEVELS, NITS_LEVELS);
+ BrightnessConfiguration config = builder.build();
+ Pair<float[], float[]> curve = config.getCurve();
+ assertArrayEquals(LUX_LEVELS, curve.first, "lux");
+ assertArrayEquals(NITS_LEVELS, curve.second, "nits");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCurveMustHaveZeroLuxPoint() {
+ BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
+ float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length);
+ lux[0] = 1f;
+ builder.setCurve(lux, NITS_LEVELS);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testCurveMustBeSet() {
+ BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
+ builder.build();
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testCurveMustNotHaveNullArrays() {
+ BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
+ builder.setCurve(null, null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCurveMustNotHaveEmptyArrays() {
+ BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
+ builder.setCurve(new float[0], new float[0]);
+ }
+
+ @Test
+ public void testCurveMustNotHaveArraysOfDifferentLengths() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
+ float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length + 1);
+ lux[lux.length - 1] = lux[lux.length - 2] + 1;
+ boolean exceptionThrown = false;
+ builder.setCurve(lux, NITS_LEVELS);
+ });
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
+ float[] nits = Arrays.copyOf(NITS_LEVELS, NITS_LEVELS.length + 1);
+ nits[nits.length - 1] = nits[nits.length - 2] + 1;
+ builder.setCurve(LUX_LEVELS, nits);
+ });
+ }
+
+ @Test
+ public void testCurvesMustNotContainNaN() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length);
+ lux[lux.length - 1] = Float.NaN;
+ BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
+ builder.setCurve(lux, NITS_LEVELS);
+ });
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ float[] nits = Arrays.copyOf(NITS_LEVELS, NITS_LEVELS.length);
+ nits[nits.length - 1] = Float.NaN;
+ BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
+ builder.setCurve(LUX_LEVELS, nits);
+ });
+ }
+
+
+ @Test
+ public void testParceledConfigIsEquivalent() {
+ BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
+ builder.setCurve(LUX_LEVELS, NITS_LEVELS);
+ BrightnessConfiguration config = builder.build();
+ Parcel p = Parcel.obtain();
+ p.writeParcelable(config, 0 /*flags*/);
+ p.setDataPosition(0);
+ BrightnessConfiguration newConfig =
+ p.readParcelable(BrightnessConfiguration.class.getClassLoader());
+ assertEquals(config, newConfig);
+ }
+
+ @Test
+ public void testEquals() {
+ BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
+ builder.setCurve(LUX_LEVELS, NITS_LEVELS);
+ BrightnessConfiguration baseConfig = builder.build();
+
+ builder = new BrightnessConfiguration.Builder();
+ builder.setCurve(LUX_LEVELS, NITS_LEVELS);
+ BrightnessConfiguration identicalConfig = builder.build();
+ assertEquals(baseConfig, identicalConfig);
+ assertEquals("hashCodes must be equal for identical configs",
+ baseConfig.hashCode(), identicalConfig.hashCode());
+
+ float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length);
+ lux[lux.length - 1] = lux[lux.length - 1] * 2;
+ builder = new BrightnessConfiguration.Builder();
+ builder.setCurve(lux, NITS_LEVELS);
+ BrightnessConfiguration luxDifferConfig = builder.build();
+ assertNotEquals(baseConfig, luxDifferConfig);
+
+ float[] nits = Arrays.copyOf(NITS_LEVELS, NITS_LEVELS.length);
+ nits[nits.length - 1] = nits[nits.length - 1] * 2;
+ builder = new BrightnessConfiguration.Builder();
+ builder.setCurve(LUX_LEVELS, nits);
+ BrightnessConfiguration nitsDifferConfig = builder.build();
+ assertNotEquals(baseConfig, nitsDifferConfig);
+ }
+
+ private static void assertArrayEquals(float[] expected, float[] actual, String name) {
+ assertEquals("Expected " + name + " arrays to be the same length!",
+ expected.length, actual.length);
+ for (int i = 0; i < expected.length; i++) {
+ assertEquals("Expected " + name + " arrays to be equivalent when value " + i
+ + "differs", expected[i], actual[i], 0.01 /*tolerance*/);
+ }
+ }
+
+ 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/hardware/display/VirtualDisplayTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
index a2e9ae8..f30b1a2 100644
--- a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
+++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
@@ -30,6 +30,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
+import android.support.test.filters.LargeTest;
import android.test.AndroidTestCase;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -49,6 +50,7 @@
* Contains additional tests that cannot be included in CTS because they require
* system permissions. See also the CTS version of VirtualDisplayTest.
*/
+@LargeTest
public class VirtualDisplayTest extends AndroidTestCase {
private static final String TAG = "VirtualDisplayTest";
diff --git a/core/tests/coretests/src/android/metrics/LogMakerTest.java b/core/tests/coretests/src/android/metrics/LogMakerTest.java
index ada59cd..3be776d 100644
--- a/core/tests/coretests/src/android/metrics/LogMakerTest.java
+++ b/core/tests/coretests/src/android/metrics/LogMakerTest.java
@@ -15,9 +15,13 @@
*/
package android.metrics;
+import android.support.test.filters.LargeTest;
+
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
import junit.framework.TestCase;
+@LargeTest
public class LogMakerTest extends TestCase {
public void testSerialize() {
diff --git a/core/tests/coretests/src/android/metrics/MetricsReaderTest.java b/core/tests/coretests/src/android/metrics/MetricsReaderTest.java
index d10b351..784a12f 100644
--- a/core/tests/coretests/src/android/metrics/MetricsReaderTest.java
+++ b/core/tests/coretests/src/android/metrics/MetricsReaderTest.java
@@ -16,6 +16,7 @@
package android.metrics;
import android.metrics.MetricsReader.Event;
+import android.support.test.filters.LargeTest;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -23,6 +24,7 @@
import java.util.Collection;
+@LargeTest
public class MetricsReaderTest extends TestCase {
private static final int FULL_N = 10;
private static final int CHECKPOINTED_N = 4;
diff --git a/core/tests/coretests/src/android/os/WorkSourceTest.java b/core/tests/coretests/src/android/os/WorkSourceTest.java
new file mode 100644
index 0000000..7350db7
--- /dev/null
+++ b/core/tests/coretests/src/android/os/WorkSourceTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.os;
+
+import android.os.WorkSource.WorkChain;
+
+import junit.framework.TestCase;
+
+import java.util.List;
+
+/**
+ * Provides unit tests for hidden / unstable WorkSource APIs that are not CTS testable.
+ *
+ * These tests will be moved to CTS when finalized.
+ */
+public class WorkSourceTest extends TestCase {
+ public void testWorkChain_add() {
+ WorkChain wc1 = new WorkChain();
+ wc1.addNode(56, null);
+
+ assertEquals(56, wc1.getUids()[0]);
+ assertEquals(null, wc1.getTags()[0]);
+ assertEquals(1, wc1.getSize());
+
+ wc1.addNode(57, "foo");
+ assertEquals(56, wc1.getUids()[0]);
+ assertEquals(null, wc1.getTags()[0]);
+ assertEquals(57, wc1.getUids()[1]);
+ assertEquals("foo", wc1.getTags()[1]);
+
+ assertEquals(2, wc1.getSize());
+ }
+
+ public void testWorkChain_equalsHashCode() {
+ WorkChain wc1 = new WorkChain();
+ WorkChain wc2 = new WorkChain();
+
+ assertEquals(wc1, wc2);
+ assertEquals(wc1.hashCode(), wc2.hashCode());
+
+ wc1.addNode(1, null);
+ wc2.addNode(1, null);
+ assertEquals(wc1, wc2);
+ assertEquals(wc1.hashCode(), wc2.hashCode());
+
+ wc1.addNode(2, "tag");
+ wc2.addNode(2, "tag");
+ assertEquals(wc1, wc2);
+ assertEquals(wc1.hashCode(), wc2.hashCode());
+
+ wc1 = new WorkChain();
+ wc2 = new WorkChain();
+ wc1.addNode(5, null);
+ wc2.addNode(6, null);
+ assertFalse(wc1.equals(wc2));
+ assertFalse(wc1.hashCode() == wc2.hashCode());
+
+ wc1 = new WorkChain();
+ wc2 = new WorkChain();
+ wc1.addNode(5, "tag1");
+ wc2.addNode(5, "tag2");
+ assertFalse(wc1.equals(wc2));
+ assertFalse(wc1.hashCode() == wc2.hashCode());
+ }
+
+ public void testWorkChain_constructor() {
+ WorkChain wc1 = new WorkChain();
+ wc1.addNode(1, "foo")
+ .addNode(2, null)
+ .addNode(3, "baz");
+
+ WorkChain wc2 = new WorkChain(wc1);
+ assertEquals(wc1, wc2);
+
+ wc1.addNode(4, "baz");
+ assertFalse(wc1.equals(wc2));
+ }
+
+ public void testDiff_workChains() {
+ WorkSource ws1 = new WorkSource();
+ ws1.add(50);
+ ws1.createWorkChain().addNode(52, "foo");
+ WorkSource ws2 = new WorkSource();
+ ws2.add(50);
+ ws2.createWorkChain().addNode(60, "bar");
+
+ // Diffs don't take WorkChains into account for the sake of backward compatibility.
+ assertFalse(ws1.diff(ws2));
+ assertFalse(ws2.diff(ws1));
+ }
+
+ public void testEquals_workChains() {
+ WorkSource ws1 = new WorkSource();
+ ws1.add(50);
+ ws1.createWorkChain().addNode(52, "foo");
+
+ WorkSource ws2 = new WorkSource();
+ ws2.add(50);
+ ws2.createWorkChain().addNode(52, "foo");
+
+ assertEquals(ws1, ws2);
+
+ // Unequal number of WorkChains.
+ ws2.createWorkChain().addNode(53, "baz");
+ assertFalse(ws1.equals(ws2));
+
+ // Different WorkChain contents.
+ WorkSource ws3 = new WorkSource();
+ ws3.add(50);
+ ws3.createWorkChain().addNode(60, "bar");
+
+ assertFalse(ws1.equals(ws3));
+ assertFalse(ws3.equals(ws1));
+ }
+
+ public void testWorkSourceParcelling() {
+ WorkSource ws = new WorkSource();
+
+ WorkChain wc = ws.createWorkChain();
+ wc.addNode(56, "foo");
+ wc.addNode(75, "baz");
+ WorkChain wc2 = ws.createWorkChain();
+ wc2.addNode(20, "foo2");
+ wc2.addNode(30, "baz2");
+
+ Parcel p = Parcel.obtain();
+ ws.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ WorkSource unparcelled = WorkSource.CREATOR.createFromParcel(p);
+
+ assertEquals(unparcelled, ws);
+ }
+
+ public void testSet_workChains() {
+ WorkSource ws1 = new WorkSource();
+ ws1.add(50);
+
+ WorkSource ws2 = new WorkSource();
+ ws2.add(60);
+ WorkChain wc = ws2.createWorkChain();
+ wc.addNode(75, "tag");
+
+ ws1.set(ws2);
+
+ // Assert that the WorkChains are copied across correctly to the new WorkSource object.
+ List<WorkChain> workChains = ws1.getWorkChains();
+ assertEquals(1, workChains.size());
+
+ assertEquals(1, workChains.get(0).getSize());
+ assertEquals(75, workChains.get(0).getUids()[0]);
+ assertEquals("tag", workChains.get(0).getTags()[0]);
+
+ // Also assert that a deep copy of workchains is made, so the addition of a new WorkChain
+ // or the modification of an existing WorkChain has no effect.
+ ws2.createWorkChain();
+ assertEquals(1, ws1.getWorkChains().size());
+
+ wc.addNode(50, "tag2");
+ assertEquals(1, ws1.getWorkChains().size());
+ assertEquals(1, ws1.getWorkChains().get(0).getSize());
+ }
+
+ public void testSet_nullWorkChain() {
+ WorkSource ws = new WorkSource();
+ ws.add(60);
+ WorkChain wc = ws.createWorkChain();
+ wc.addNode(75, "tag");
+
+ ws.set(null);
+ assertEquals(0, ws.getWorkChains().size());
+ }
+
+ public void testAdd_workChains() {
+ WorkSource ws = new WorkSource();
+ ws.createWorkChain().addNode(70, "foo");
+
+ WorkSource ws2 = new WorkSource();
+ ws2.createWorkChain().addNode(60, "tag");
+
+ ws.add(ws2);
+
+ // Check that the new WorkChain is added to the end of the list.
+ List<WorkChain> workChains = ws.getWorkChains();
+ assertEquals(2, workChains.size());
+ assertEquals(1, workChains.get(1).getSize());
+ assertEquals(60, ws.getWorkChains().get(1).getUids()[0]);
+ assertEquals("tag", ws.getWorkChains().get(1).getTags()[0]);
+
+ // Adding the same WorkChain twice should be a no-op.
+ ws.add(ws2);
+ assertEquals(2, workChains.size());
+ }
+}
diff --git a/core/tests/coretests/src/android/preference/ListPreferenceTest.java b/core/tests/coretests/src/android/preference/ListPreferenceTest.java
index 41f8e03..72f62f1 100644
--- a/core/tests/coretests/src/android/preference/ListPreferenceTest.java
+++ b/core/tests/coretests/src/android/preference/ListPreferenceTest.java
@@ -17,8 +17,10 @@
package android.preference;
import android.preference.ListPreference;
+import android.support.test.filters.LargeTest;
import android.test.AndroidTestCase;
+@LargeTest
public class ListPreferenceTest extends AndroidTestCase {
public void testListPreferenceSummaryFromEntries() {
String[] entries = { "one", "two", "three" };
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 0982a4b..78d7785 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -106,6 +106,7 @@
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.BATTERY_DISCHARGE_DURATION_THRESHOLD,
@@ -335,6 +336,7 @@
Settings.Global.SMS_SHORT_CODES_UPDATE_CONTENT_URL,
Settings.Global.SMS_SHORT_CODES_UPDATE_METADATA_URL,
Settings.Global.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS,
+ Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
Settings.Global.STORAGE_BENCHMARK_INTERVAL,
Settings.Global.STORAGE_SETTINGS_CLOBBER_THRESHOLD,
Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
@@ -367,6 +369,7 @@
Settings.Global.ENABLE_GPU_DEBUG_LAYERS,
Settings.Global.GPU_DEBUG_APP,
Settings.Global.GPU_DEBUG_LAYERS,
+ Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
Settings.Global.WARNING_TEMPERATURE,
Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
@@ -419,13 +422,6 @@
private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
newHashSet(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- // TODO(b/67867469): Move autofill settings below to
- // BACKUP_BLACKLISTED_SYSTEM_SETTINGS once feature is moved out of experimental
- Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
- Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
- Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE,
- Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH,
- Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH,
Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS,
Settings.Secure.ALWAYS_ON_VPN_APP,
Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
@@ -435,6 +431,11 @@
Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
Settings.Secure.ASSIST_STRUCTURE_ENABLED,
+ Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
+ Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
+ Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE,
+ Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH,
+ Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH,
Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED,
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
diff --git a/core/tests/coretests/src/android/security/recoverablekeystore/RecoverableKeyGeneratorTest.java b/core/tests/coretests/src/android/security/recoverablekeystore/RecoverableKeyGeneratorTest.java
deleted file mode 100644
index d85d3b8..0000000
--- a/core/tests/coretests/src/android/security/recoverablekeystore/RecoverableKeyGeneratorTest.java
+++ /dev/null
@@ -1,154 +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.security.recoverablekeystore;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.security.keystore.AndroidKeyStoreSecretKey;
-import android.security.keystore.KeyGenParameterSpec;
-import android.security.keystore.KeyProperties;
-import android.security.keystore.KeyProtection;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.security.KeyStore;
-
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class RecoverableKeyGeneratorTest {
- private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
- private static final String KEY_ALGORITHM = "AES";
- private static final String TEST_ALIAS = "karlin";
- private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyGeneratorTestWrappingKey";
-
- @Mock RecoverableKeyStorage mRecoverableKeyStorage;
-
- @Captor ArgumentCaptor<KeyProtection> mKeyProtectionArgumentCaptor;
-
- private AndroidKeyStoreSecretKey mPlatformKey;
- private SecretKey mKeyHandle;
- private RecoverableKeyGenerator mRecoverableKeyGenerator;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mPlatformKey = generateAndroidKeyStoreKey();
- mKeyHandle = generateKey();
- mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(
- mPlatformKey, mRecoverableKeyStorage);
-
- when(mRecoverableKeyStorage.loadFromAndroidKeyStore(any())).thenReturn(mKeyHandle);
- }
-
- @After
- public void tearDown() throws Exception {
- KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
- keyStore.load(/*param=*/ null);
- keyStore.deleteEntry(WRAPPING_KEY_ALIAS);
- }
-
- @Test
- public void generateAndStoreKey_setsKeyInKeyStore() throws Exception {
- mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS);
-
- verify(mRecoverableKeyStorage, times(1))
- .importIntoAndroidKeyStore(eq(TEST_ALIAS), any(), any());
- }
-
- @Test
- public void generateAndStoreKey_storesKeyEnabledForEncryptDecrypt() throws Exception {
- mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS);
-
- KeyProtection keyProtection = getKeyProtectionUsed();
- assertEquals(KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
- keyProtection.getPurposes());
- }
-
- @Test
- public void generateAndStoreKey_storesKeyEnabledForGCM() throws Exception {
- mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS);
-
- KeyProtection keyProtection = getKeyProtectionUsed();
- assertArrayEquals(new String[] { KeyProperties.BLOCK_MODE_GCM },
- keyProtection.getBlockModes());
- }
-
- @Test
- public void generateAndStoreKey_storesKeyEnabledForNoPadding() throws Exception {
- mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS);
-
- KeyProtection keyProtection = getKeyProtectionUsed();
- assertArrayEquals(new String[] { KeyProperties.ENCRYPTION_PADDING_NONE },
- keyProtection.getEncryptionPaddings());
- }
-
- @Test
- public void generateAndStoreKey_storesWrappedKey() throws Exception {
- mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS);
-
- verify(mRecoverableKeyStorage, times(1)).persistToDisk(eq(TEST_ALIAS), any());
- }
-
- @Test
- public void generateAndStoreKey_returnsKeyHandle() throws Exception {
- SecretKey secretKey = mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS);
-
- assertEquals(mKeyHandle, secretKey);
- }
-
- private KeyProtection getKeyProtectionUsed() throws Exception {
- verify(mRecoverableKeyStorage, times(1)).importIntoAndroidKeyStore(
- any(), any(), mKeyProtectionArgumentCaptor.capture());
- return mKeyProtectionArgumentCaptor.getValue();
- }
-
- private SecretKey generateKey() throws Exception {
- KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
- keyGenerator.init(/*keySize=*/ 256);
- return keyGenerator.generateKey();
- }
-
- private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
- KeyGenerator keyGenerator = KeyGenerator.getInstance(
- KEY_ALGORITHM,
- ANDROID_KEY_STORE_PROVIDER);
- keyGenerator.init(new KeyGenParameterSpec.Builder(
- WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
- .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
- .build());
- return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
- }
-}
diff --git a/core/tests/coretests/src/android/security/recoverablekeystore/WrappedKeyTest.java b/core/tests/coretests/src/android/security/recoverablekeystore/WrappedKeyTest.java
deleted file mode 100644
index 233c821..0000000
--- a/core/tests/coretests/src/android/security/recoverablekeystore/WrappedKeyTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.recoverablekeystore;
-
-import static org.junit.Assert.assertEquals;
-
-import android.security.keystore.AndroidKeyStoreSecretKey;
-import android.security.keystore.KeyGenParameterSpec;
-import android.security.keystore.KeyProperties;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.security.KeyStore;
-
-import javax.crypto.Cipher;
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.GCMParameterSpec;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class WrappedKeyTest {
- private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
- private static final String KEY_ALGORITHM = "AES";
- private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
- private static final String WRAPPING_KEY_ALIAS = "WrappedKeyTestWrappingKeyAlias";
- private static final int GCM_TAG_LENGTH_BYTES = 16;
- private static final int BITS_PER_BYTE = 8;
- private static final int GCM_TAG_LENGTH_BITS = GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE;
-
- @After
- public void tearDown() throws Exception {
- KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
- keyStore.load(/*param=*/ null);
- keyStore.deleteEntry(WRAPPING_KEY_ALIAS);
- }
-
- @Test
- public void fromSecretKey_createsWrappedKeyThatCanBeUnwrapped() throws Exception {
- SecretKey wrappingKey = generateAndroidKeyStoreKey();
- SecretKey rawKey = generateKey();
-
- WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey);
-
- Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
- cipher.init(
- Cipher.UNWRAP_MODE,
- wrappingKey,
- new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
- SecretKey unwrappedKey = (SecretKey) cipher.unwrap(
- wrappedKey.getKeyMaterial(), KEY_ALGORITHM, Cipher.SECRET_KEY);
- assertEquals(rawKey, unwrappedKey);
- }
-
- private SecretKey generateKey() throws Exception {
- KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
- keyGenerator.init(/*keySize=*/ 256);
- return keyGenerator.generateKey();
- }
-
- private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
- KeyGenerator keyGenerator = KeyGenerator.getInstance(
- KEY_ALGORITHM,
- ANDROID_KEY_STORE_PROVIDER);
- keyGenerator.init(new KeyGenParameterSpec.Builder(
- WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
- .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
- .build());
- return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
- }
-}
diff --git a/core/tests/coretests/src/android/text/BidiFormatterTest.java b/core/tests/coretests/src/android/text/BidiFormatterTest.java
index 14705ad..1b936c7 100644
--- a/core/tests/coretests/src/android/text/BidiFormatterTest.java
+++ b/core/tests/coretests/src/android/text/BidiFormatterTest.java
@@ -18,12 +18,14 @@
import static org.junit.Assert.assertEquals;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BidiFormatterTest {
diff --git a/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java b/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java
index 08e1b5b..c69f4f5 100644
--- a/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java
+++ b/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -33,6 +34,7 @@
* Requires disabling access checks in the vm since this calls package-private APIs.
*
*/
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DynamicLayoutBlocksTest {
diff --git a/core/tests/coretests/src/android/text/DynamicLayoutTest.java b/core/tests/coretests/src/android/text/DynamicLayoutTest.java
index 639cefb..ea954f6 100644
--- a/core/tests/coretests/src/android/text/DynamicLayoutTest.java
+++ b/core/tests/coretests/src/android/text/DynamicLayoutTest.java
@@ -26,13 +26,16 @@
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.style.ReplacementSpan;
+import android.util.ArraySet;
import org.junit.Test;
import org.junit.runner.RunWith;
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DynamicLayoutTest {
@@ -188,6 +191,27 @@
}
@Test
+ public void testReflow_afterSpannableEdit() {
+ final String text = "a\nb:\uD83C\uDF1A c \n\uD83C\uDF1A";
+ final int length = text.length();
+ final SpannableStringBuilder spannable = new SpannableStringBuilder(text);
+ spannable.setSpan(new MockReplacementSpan(), 4, 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ spannable.setSpan(new MockReplacementSpan(), 10, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ final DynamicLayout layout = new DynamicLayout(spannable, new TextPaint(), WIDTH,
+ ALIGN_NORMAL, 1.0f /*spacingMultiplier*/, 0f /*spacingAdd*/, false /*includepad*/);
+
+ spannable.delete(8, 9);
+ spannable.replace(7, 8, "ch");
+
+ layout.reflow(spannable, 0, length, length);
+ final ArraySet<Integer> blocks = layout.getBlocksAlwaysNeedToBeRedrawn();
+ for (Integer value : blocks) {
+ assertTrue("Block index should not be negative", value >= 0);
+ }
+ }
+
+ @Test
public void testFallbackLineSpacing() {
// All glyphs in the fonts are 1em wide.
final String[] testFontFiles = {
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 9cf8e1e..2448b0b 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -29,6 +29,7 @@
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.Layout.Alignment;
@@ -42,6 +43,7 @@
import java.util.List;
import java.util.Locale;
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class LayoutTest {
diff --git a/core/tests/coretests/src/android/text/LineBreakingOverhangsTest.java b/core/tests/coretests/src/android/text/LineBreakingOverhangsTest.java
deleted file mode 100644
index 4f18b0b..0000000
--- a/core/tests/coretests/src/android/text/LineBreakingOverhangsTest.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2012 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 static org.junit.Assert.assertEquals;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.Typeface;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class LineBreakingOverhangsTest {
- private static final int EM = 100; // Make 1em == 100px.
- private static final TextPaint sTextPaint = new TextPaint();
-
- static {
- // The test font has following coverage and overhangs.
- // All the characters have a width of 1em.
- // space: no overhangs
- // R: 4em overhang on the right
- // a: no overhang
- // y: 1.5em overhang on the left
- sTextPaint.setTypeface(Typeface.createFromAsset(
- InstrumentationRegistry.getTargetContext().getAssets(),
- "fonts/LineBreakingOverhangsTestFont.ttf"));
- sTextPaint.setTextSize(EM);
- }
-
- private static void layout(@NonNull CharSequence source, @NonNull int[] breaks, double width,
- @Nullable int[] leftPadding, @Nullable int[] rightPadding) {
- layout(source, breaks, width, leftPadding, rightPadding, null /* indents */);
- }
-
- private static void layout(@NonNull CharSequence source, @NonNull int[] breaks, double width,
- @Nullable int[] leftPadding, @Nullable int[] rightPadding, @Nullable int[] indents) {
- final StaticLayout staticLayout = StaticLayout.Builder
- .obtain(source, 0, source.length(), sTextPaint, (int) width)
- .setAvailablePaddings(leftPadding, rightPadding)
- .setIndents(indents, indents)
- .build();
-
- final int lineCount = breaks.length + 1;
- assertEquals("Number of lines", lineCount, staticLayout.getLineCount());
-
- for (int line = 0; line < lineCount; line++) {
- final int lineStart = staticLayout.getLineStart(line);
- final int lineEnd = staticLayout.getLineEnd(line);
-
- if (line == 0) {
- assertEquals("Line start for first line", 0, lineStart);
- } else {
- assertEquals("Line start for line " + line, breaks[line - 1], lineStart);
- }
-
- if (line == lineCount - 1) {
- assertEquals("Line end for last line", source.length(), lineEnd);
- } else {
- assertEquals("Line end for line " + line, breaks[line], lineEnd);
- }
- }
- }
-
- private static final int[] NO_BREAK = new int[] {};
-
- private static final int[] NO_PADDING = null;
- // Maximum needed for left side of 'y'.
- private static final int[] FULL_LEFT_PADDING = new int[] {(int) (1.5 * EM)};
- // Maximum padding needed for right side of 'R'.
- private static final int[] FULL_RIGHT_PADDING = new int[] {4 * EM};
-
- private static final int[] ONE_EM_PADDING = new int[] {1 * EM};
- private static final int[] HALF_EM_PADDING = new int[] {(int) (0.5 * EM)};
- private static final int[] QUARTER_EM_PADDING = new int[] {(int) (0.25 * EM)};
-
- @Test
- public void testRightOverhang() {
- // The advance of "aaa R" is 5em, but the right-side overhang of 'R' would need 4em more, so
- // we break the line if there's not enough overhang.
-
- // Enough right padding, so the whole line fits in 5em.
- layout("aaa R", NO_BREAK, 5 * EM, NO_PADDING, FULL_RIGHT_PADDING);
-
- // No right padding, so we'd need 9em to fit the advance and the right padding of 'R'.
- layout("aaa R", new int[] {4}, 8.9 * EM, NO_PADDING, NO_PADDING);
- layout("aaa R", NO_BREAK, 9 * EM, NO_PADDING, NO_PADDING);
-
- // 1em of right padding means we can fit the string in 8em.
- layout("aaa R", new int[] {4}, 7.9 * EM, NO_PADDING, ONE_EM_PADDING);
- layout("aaa R", NO_BREAK, 8 * EM, NO_PADDING, ONE_EM_PADDING);
- }
-
- @Test
- public void testLeftOverhang() {
- // The advance of "y a" is 3em, but the left-side overhang of 'y' would need 1.5em more, so
- // we break the line if there's not enough overhang.
-
- // Enough left padding, so the whole line fits in 3em.
- layout("y a", NO_BREAK, 3 * EM, FULL_LEFT_PADDING, NO_PADDING);
-
- // No right padding, so we'd need 4.5em to fit the advance and the left padding of 'y'.
- layout("y a", new int[] {2}, 4.4 * EM, NO_PADDING, NO_PADDING);
- layout("y a", NO_BREAK, 4.5 * EM, NO_PADDING, NO_PADDING);
-
- // 1em of left padding means we can fit the string in 3.5em.
- layout("y a", new int[] {2}, 3.4 * EM, ONE_EM_PADDING, NO_PADDING);
- layout("y a", NO_BREAK, 3.5 * EM, ONE_EM_PADDING, NO_PADDING);
- }
-
- @Test
- public void testBothSidesOverhang() {
- // The advance of "y a R" is 5em, but the left-side overhang of 'y' would need 1.5em more,
- // and the right side overhang or 'R' would need 4em more, so we break the line if there's
- // not enough overhang.
-
- // Enough padding, so the whole line fits in 5em.
- layout("y a R", NO_BREAK, 5 * EM, FULL_LEFT_PADDING, FULL_RIGHT_PADDING);
-
- // No padding, so we'd need 10.5em to fit the advance and the paddings.
- layout("y a R", new int[] {4}, 10.4 * EM, NO_PADDING, NO_PADDING);
- layout("y a R", NO_BREAK, 10.5 * EM, NO_PADDING, NO_PADDING);
-
- // 1em of padding on each side means we can fit the string in 8.5em.
- layout("y a R", new int[] {4}, 8.4 * EM, ONE_EM_PADDING, ONE_EM_PADDING);
- layout("y a R", NO_BREAK, 8.5 * EM, ONE_EM_PADDING, ONE_EM_PADDING);
- }
-
- @Test
- public void testIndentsDontAffectPaddings() {
- // This is identical to the previous test, except that it applies wide indents of 4em on
- // each side and thus needs an extra 8em of width. This test makes sure that indents and
- // paddings are independent.
- final int[] indents = new int[] {4 * EM};
- final int indentAdj = 8 * EM;
-
- // Enough padding, so the whole line fits in 5em.
- layout("y a R", NO_BREAK, 5 * EM + indentAdj, FULL_LEFT_PADDING, FULL_RIGHT_PADDING,
- indents);
-
- // No padding, so we'd need 10.5em to fit the advance and the paddings.
- layout("y a R", new int[] {4}, 10.4 * EM + indentAdj, NO_PADDING, NO_PADDING, indents);
- layout("y a R", NO_BREAK, 10.5 * EM + indentAdj, NO_PADDING, NO_PADDING, indents);
-
- // 1em of padding on each side means we can fit the string in 8.5em.
- layout("y a R", new int[] {4}, 8.4 * EM + indentAdj, ONE_EM_PADDING, ONE_EM_PADDING,
- indents);
- layout("y a R", NO_BREAK, 8.5 * EM + indentAdj, ONE_EM_PADDING, ONE_EM_PADDING, indents);
- }
-}
diff --git a/core/tests/coretests/src/android/text/OWNERS b/core/tests/coretests/src/android/text/OWNERS
new file mode 100644
index 0000000..0f85e1f
--- /dev/null
+++ b/core/tests/coretests/src/android/text/OWNERS
@@ -0,0 +1,4 @@
+siyamed@google.com
+nona@google.com
+clarabayarri@google.com
+toki@google.com
diff --git a/core/tests/coretests/src/android/text/PackedIntVectorTest.java b/core/tests/coretests/src/android/text/PackedIntVectorTest.java
index 9df0f89..d9dc6fc 100644
--- a/core/tests/coretests/src/android/text/PackedIntVectorTest.java
+++ b/core/tests/coretests/src/android/text/PackedIntVectorTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -27,6 +28,7 @@
/**
* PackedIntVectorTest tests the features of android.util.PackedIntVector.
*/
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class PackedIntVectorTest {
diff --git a/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java b/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java
index 04a486e..d0f2d46 100644
--- a/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java
+++ b/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java
@@ -20,13 +20,20 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import android.text.style.BulletSpan;
import android.text.style.QuoteSpan;
import android.text.style.SubscriptSpan;
import android.text.style.UnderlineSpan;
import org.junit.Test;
+import org.junit.runner.RunWith;
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
public class SpannableStringBuilderTest extends SpannableTest {
protected Spannable newSpannableWithText(String text) {
diff --git a/core/tests/coretests/src/android/text/SpannableTest.java b/core/tests/coretests/src/android/text/SpannableTest.java
index 5ed6250..307ac62 100644
--- a/core/tests/coretests/src/android/text/SpannableTest.java
+++ b/core/tests/coretests/src/android/text/SpannableTest.java
@@ -16,14 +16,16 @@
package android.text;
-import android.support.test.filters.MediumTest;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.test.MoreAsserts;
import org.junit.Test;
import org.junit.runner.RunWith;
-@MediumTest
+@Presubmit
+@SmallTest
@RunWith(AndroidJUnit4.class)
public abstract class SpannableTest {
diff --git a/core/tests/coretests/src/android/text/SpannedTest.java b/core/tests/coretests/src/android/text/SpannedTest.java
index 60cddb08..5737571 100644
--- a/core/tests/coretests/src/android/text/SpannedTest.java
+++ b/core/tests/coretests/src/android/text/SpannedTest.java
@@ -20,6 +20,7 @@
import android.graphics.Typeface;
import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.style.CharacterStyle;
@@ -33,6 +34,7 @@
/**
* SpannedTest tests some features of Spanned
*/
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SpannedTest {
diff --git a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
index 8092203..bf0d427 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
@@ -28,6 +29,7 @@
/**
* Quick check of native bidi implementation.
*/
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class StaticLayoutBidiTest {
diff --git a/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java b/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java
index e0b4776..5cf5426 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java
@@ -18,16 +18,19 @@
import static org.junit.Assert.fail;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.Layout.Directions;
import android.text.StaticLayoutTest.LayoutBuilder;
-import java.util.Arrays;
-import java.util.Formatter;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
+import java.util.Formatter;
+
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class StaticLayoutDirectionsTest {
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java
index 078ed76..d817278 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java
@@ -24,6 +24,7 @@
import android.graphics.Canvas;
import android.graphics.Paint.FontMetricsInt;
import android.os.LocaleList;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.Layout.Alignment;
@@ -43,6 +44,7 @@
/**
* Tests StaticLayout vertical metrics behavior.
*/
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class StaticLayoutTest {
@@ -695,9 +697,13 @@
public void testGetOffset_UNICODE_Hebrew() {
String testString = "\u05DE\u05E1\u05E2\u05D3\u05D4"; // Hebrew Characters
for (CharSequence seq: buildTestCharSequences(testString, Normalizer.Form.values())) {
- StaticLayout layout = new StaticLayout(seq, mDefaultPaint,
- DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN,
- TextDirectionHeuristics.RTL, SPACE_MULTI, SPACE_ADD, true);
+ StaticLayout.Builder b = StaticLayout.Builder.obtain(
+ seq, 0, seq.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH)
+ .setAlignment(DEFAULT_ALIGN)
+ .setTextDirection(TextDirectionHeuristics.RTL)
+ .setLineSpacing(SPACE_ADD, SPACE_MULTI)
+ .setIncludePad(true);
+ StaticLayout layout = b.build();
String testLabel = buildTestMessage(seq);
diff --git a/core/tests/coretests/src/android/text/TextLayoutTest.java b/core/tests/coretests/src/android/text/TextLayoutTest.java
index 8963189..24020ce 100644
--- a/core/tests/coretests/src/android/text/TextLayoutTest.java
+++ b/core/tests/coretests/src/android/text/TextLayoutTest.java
@@ -16,6 +16,7 @@
package android.text;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -23,6 +24,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TextLayoutTest {
diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java
index f8d6884..d9c09c8 100644
--- a/core/tests/coretests/src/android/text/TextLineTest.java
+++ b/core/tests/coretests/src/android/text/TextLineTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.filters.Suppress;
import android.support.test.runner.AndroidJUnit4;
@@ -26,6 +27,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TextLineTest {
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index 79619d9a..e70b841 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -25,6 +25,7 @@
import static org.junit.Assert.fail;
import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.LargeTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -46,6 +47,7 @@
/**
* TextUtilsTest tests {@link TextUtils}.
*/
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TextUtilsTest {
diff --git a/core/tests/coretests/src/android/text/VariationParserTest.java b/core/tests/coretests/src/android/text/VariationParserTest.java
index ec2c96c..fbd2412 100644
--- a/core/tests/coretests/src/android/text/VariationParserTest.java
+++ b/core/tests/coretests/src/android/text/VariationParserTest.java
@@ -20,12 +20,14 @@
import static org.junit.Assert.fail;
import android.graphics.fonts.FontVariationAxis;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class VariationParserTest {
diff --git a/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java
index 0f08d18..9000ed0 100644
--- a/core/tests/coretests/src/android/text/format/DateFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -27,6 +28,7 @@
import java.util.Locale;
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DateFormatTest {
diff --git a/core/tests/coretests/src/android/text/format/DateUtilsTest.java b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
index f21c929..f8e3b4d 100644
--- a/core/tests/coretests/src/android/text/format/DateUtilsTest.java
+++ b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
@@ -21,6 +21,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.LocaleList;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -35,6 +36,7 @@
import java.util.Locale;
import java.util.TimeZone;
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DateUtilsTest {
diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java
index dee51dc..9c544f4 100644
--- a/core/tests/coretests/src/android/text/format/FormatterTest.java
+++ b/core/tests/coretests/src/android/text/format/FormatterTest.java
@@ -23,6 +23,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.icu.util.MeasureUnit;
+import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -36,7 +37,7 @@
import java.util.Locale;
import java.util.Set;
-
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class FormatterTest {
@@ -189,7 +190,7 @@
// Make sure it works on different locales.
setLocale(new Locale("ru", "RU"));
- assertEquals("1 мин", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+ assertEquals("1 мин.", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 1 * SECOND));
}
diff --git a/core/tests/coretests/src/android/text/format/TimeTest.java b/core/tests/coretests/src/android/text/format/TimeTest.java
index 8983d15..d563f2e 100644
--- a/core/tests/coretests/src/android/text/format/TimeTest.java
+++ b/core/tests/coretests/src/android/text/format/TimeTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.filters.Suppress;
import android.support.test.runner.AndroidJUnit4;
@@ -29,6 +30,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TimeTest {
diff --git a/core/tests/coretests/src/android/text/method/BackspaceTest.java b/core/tests/coretests/src/android/text/method/BackspaceTest.java
index 8b5cc60..df4609f 100644
--- a/core/tests/coretests/src/android/text/method/BackspaceTest.java
+++ b/core/tests/coretests/src/android/text/method/BackspaceTest.java
@@ -16,6 +16,7 @@
package android.text.method;
+import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -24,6 +25,7 @@
import android.view.KeyEvent;
import android.widget.EditText;
import android.widget.TextView.BufferType;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -34,6 +36,8 @@
* Only contains edge cases. For normal cases, see {@see android.text.method.cts.BackspaceTest}.
* TODO: introduce test cases for surrogate pairs and replacement span.
*/
+
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BackspaceTest {
diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
index c3a5f80..15dbbc8 100644
--- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
+++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
@@ -16,6 +16,7 @@
package android.text.method;
+import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -35,6 +36,7 @@
* Only contains edge cases. For normal cases, see {@see android.text.method.cts.ForwardDeleteTest}.
* TODO: introduce test cases for surrogate pairs and replacement span.
*/
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ForwardDeleteTest {
diff --git a/core/tests/coretests/src/android/text/method/WordIteratorTest.java b/core/tests/coretests/src/android/text/method/WordIteratorTest.java
index d1f4f7e..6845863 100644
--- a/core/tests/coretests/src/android/text/method/WordIteratorTest.java
+++ b/core/tests/coretests/src/android/text/method/WordIteratorTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -31,6 +32,7 @@
import java.util.Locale;
// TODO(Bug: 24062099): Add more tests for non-ascii text.
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class WordIteratorTest {
diff --git a/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java b/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java
index e5c4b82..3d7e5ba 100644
--- a/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java
+++ b/core/tests/coretests/src/android/text/style/UnderlineSpanTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertEquals;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.SpannableString;
@@ -29,7 +30,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class UnderlineSpanTest {
diff --git a/core/tests/coretests/src/android/transition/TransitionTest.java b/core/tests/coretests/src/android/transition/TransitionTest.java
index ab4320c..7e629f9 100644
--- a/core/tests/coretests/src/android/transition/TransitionTest.java
+++ b/core/tests/coretests/src/android/transition/TransitionTest.java
@@ -19,6 +19,7 @@
import android.animation.AnimatorSetActivity;
import android.app.Activity;
import android.graphics.Rect;
+import android.support.test.filters.LargeTest;
import android.test.ActivityInstrumentationTestCase2;
import android.transition.Transition.EpicenterCallback;
import android.util.ArrayMap;
@@ -30,6 +31,7 @@
import java.lang.reflect.Field;
+@LargeTest
public class TransitionTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
Activity mActivity;
public TransitionTest() {
diff --git a/core/tests/coretests/src/android/util/ArrayMapTest.java b/core/tests/coretests/src/android/util/ArrayMapTest.java
index 32aa29f..f0cc7f7 100644
--- a/core/tests/coretests/src/android/util/ArrayMapTest.java
+++ b/core/tests/coretests/src/android/util/ArrayMapTest.java
@@ -14,6 +14,7 @@
package android.util;
+import android.support.test.filters.LargeTest;
import android.util.ArrayMap;
import junit.framework.TestCase;
@@ -24,6 +25,7 @@
/**
* Unit tests for ArrayMap that don't belong in CTS.
*/
+@LargeTest
public class ArrayMapTest extends TestCase {
private static final String TAG = "ArrayMapTest";
ArrayMap<String, String> map = new ArrayMap<>();
diff --git a/core/tests/coretests/src/android/util/Base64Test.java b/core/tests/coretests/src/android/util/Base64Test.java
index 53368d4..3aee583 100644
--- a/core/tests/coretests/src/android/util/Base64Test.java
+++ b/core/tests/coretests/src/android/util/Base64Test.java
@@ -16,15 +16,17 @@
package android.util;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-import junit.framework.TestCase;
+import android.support.test.filters.LargeTest;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
import java.util.Random;
+import junit.framework.TestCase;
+@LargeTest
public class Base64Test extends TestCase {
private static final String TAG = "Base64Test";
diff --git a/core/tests/coretests/src/android/util/LocalLogTest.java b/core/tests/coretests/src/android/util/LocalLogTest.java
index a63c8a0..5144c85 100644
--- a/core/tests/coretests/src/android/util/LocalLogTest.java
+++ b/core/tests/coretests/src/android/util/LocalLogTest.java
@@ -16,6 +16,8 @@
package android.util;
+import android.support.test.filters.LargeTest;
+
import junit.framework.TestCase;
import java.io.PrintWriter;
@@ -24,7 +26,7 @@
import java.util.Collections;
import java.util.List;
-
+@LargeTest
public class LocalLogTest extends TestCase {
public void testA() {
diff --git a/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java b/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java
index cb468bc..3f03db9 100644
--- a/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java
+++ b/core/tests/coretests/src/android/util/LongSparseLongArrayTest.java
@@ -16,6 +16,8 @@
package android.util;
+import android.support.test.filters.LargeTest;
+
import junit.framework.TestCase;
import java.util.HashMap;
@@ -26,6 +28,7 @@
/**
* Tests for {@link LongSparseLongArray}.
*/
+@LargeTest
public class LongSparseLongArrayTest extends TestCase {
private static final String TAG = "LongSparseLongArrayTest";
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index 6dd787d..0d8c679 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -25,6 +25,7 @@
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.Region;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
@@ -34,7 +35,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
@@ -45,19 +45,14 @@
/** This is not a consistent cutout. Useful for verifying insets in one go though. */
final DisplayCutout mCutoutNumbers = new DisplayCutout(
new Rect(1, 2, 3, 4),
- new Rect(5, 6, 7, 8),
- Arrays.asList(
- new Point(9, 10),
- new Point(11, 12),
- new Point(13, 14),
- new Point(15, 16)));
+ new Region(5, 6, 7, 8));
final DisplayCutout mCutoutTop = createCutoutTop();
@Test
public void hasCutout() throws Exception {
- assertFalse(NO_CUTOUT.hasCutout());
- assertTrue(mCutoutTop.hasCutout());
+ assertTrue(NO_CUTOUT.isEmpty());
+ assertFalse(mCutoutTop.isEmpty());
}
@Test
@@ -67,30 +62,12 @@
assertEquals(3, mCutoutNumbers.getSafeInsetRight());
assertEquals(4, mCutoutNumbers.getSafeInsetBottom());
- Rect safeInsets = new Rect();
- mCutoutNumbers.getSafeInsets(safeInsets);
-
- assertEquals(new Rect(1, 2, 3, 4), safeInsets);
+ assertEquals(new Rect(1, 2, 3, 4), mCutoutNumbers.getSafeInsets());
}
@Test
public void getBoundingRect() throws Exception {
- Rect boundingRect = new Rect();
- mCutoutTop.getBoundingRect(boundingRect);
-
- assertEquals(new Rect(50, 0, 75, 100), boundingRect);
- }
-
- @Test
- public void getBoundingPolygon() throws Exception {
- ArrayList<Point> boundingPolygon = new ArrayList<>();
- mCutoutTop.getBoundingPolygon(boundingPolygon);
-
- assertEquals(Arrays.asList(
- new Point(75, 0),
- new Point(50, 0),
- new Point(75, 100),
- new Point(50, 100)), boundingPolygon);
+ assertEquals(new Rect(50, 0, 75, 100), mCutoutTop.getBoundingRect());
}
@Test
@@ -167,104 +144,61 @@
assertEquals(cutout.getSafeInsetRight(), 0);
assertEquals(cutout.getSafeInsetBottom(), 0);
- assertFalse(cutout.hasCutout());
+ assertTrue(cutout.isEmpty());
}
@Test
public void inset_bounds() throws Exception {
DisplayCutout cutout = mCutoutTop.inset(1, 2, 3, 4);
- Rect boundingRect = new Rect();
- cutout.getBoundingRect(boundingRect);
-
- assertEquals(new Rect(49, -2, 74, 98), boundingRect);
-
- ArrayList<Point> boundingPolygon = new ArrayList<>();
- cutout.getBoundingPolygon(boundingPolygon);
-
- assertEquals(Arrays.asList(
- new Point(74, -2),
- new Point(49, -2),
- new Point(74, 98),
- new Point(49, 98)), boundingPolygon);
+ assertEquals(new Rect(49, -2, 74, 98), cutout.getBoundingRect());
}
@Test
public void calculateRelativeTo_top() throws Exception {
DisplayCutout cutout = mCutoutTop.calculateRelativeTo(new Rect(0, 0, 200, 400));
- Rect insets = new Rect();
- cutout.getSafeInsets(insets);
-
- assertEquals(new Rect(0, 100, 0, 0), insets);
+ assertEquals(new Rect(0, 100, 0, 0), cutout.getSafeInsets());
}
@Test
public void calculateRelativeTo_left() throws Exception {
DisplayCutout cutout = mCutoutTop.calculateRelativeTo(new Rect(0, 0, 400, 200));
- Rect insets = new Rect();
- cutout.getSafeInsets(insets);
-
- assertEquals(new Rect(75, 0, 0, 0), insets);
+ assertEquals(new Rect(75, 0, 0, 0), cutout.getSafeInsets());
}
@Test
public void calculateRelativeTo_bottom() throws Exception {
DisplayCutout cutout = mCutoutTop.calculateRelativeTo(new Rect(0, -300, 200, 100));
- Rect insets = new Rect();
- cutout.getSafeInsets(insets);
-
- assertEquals(new Rect(0, 0, 0, 100), insets);
+ assertEquals(new Rect(0, 0, 0, 100), cutout.getSafeInsets());
}
@Test
public void calculateRelativeTo_right() throws Exception {
DisplayCutout cutout = mCutoutTop.calculateRelativeTo(new Rect(-400, -200, 100, 100));
- Rect insets = new Rect();
- cutout.getSafeInsets(insets);
-
- assertEquals(new Rect(0, 0, 50, 0), insets);
+ assertEquals(new Rect(0, 0, 50, 0), cutout.getSafeInsets());
}
@Test
public void calculateRelativeTo_bounds() throws Exception {
DisplayCutout cutout = mCutoutTop.calculateRelativeTo(new Rect(-1000, -2000, 100, 200));
-
- Rect boundingRect = new Rect();
- cutout.getBoundingRect(boundingRect);
- assertEquals(new Rect(1050, 2000, 1075, 2100), boundingRect);
-
- ArrayList<Point> boundingPolygon = new ArrayList<>();
- cutout.getBoundingPolygon(boundingPolygon);
-
- assertEquals(Arrays.asList(
- new Point(1075, 2000),
- new Point(1050, 2000),
- new Point(1075, 2100),
- new Point(1050, 2100)), boundingPolygon);
+ assertEquals(new Rect(1050, 2000, 1075, 2100), cutout.getBoundingRect());
}
@Test
public void fromBoundingPolygon() throws Exception {
assertEquals(
- new DisplayCutout(
- new Rect(0, 0, 0, 0), // fromBoundingPolygon won't calculate safe insets.
- new Rect(50, 0, 75, 100),
- Arrays.asList(
- new Point(75, 0),
- new Point(50, 0),
- new Point(75, 100),
- new Point(50, 100))),
+ new Rect(50, 0, 75, 100),
DisplayCutout.fromBoundingPolygon(
Arrays.asList(
new Point(75, 0),
new Point(50, 0),
new Point(75, 100),
- new Point(50, 100))));
+ new Point(50, 100))).getBounds().getBounds());
}
@Test
@@ -324,24 +258,12 @@
}
private static DisplayCutout createCutoutTop() {
- return new DisplayCutout(
- new Rect(0, 100, 0, 0),
- new Rect(50, 0, 75, 100),
- Arrays.asList(
- new Point(75, 0),
- new Point(50, 0),
- new Point(75, 100),
- new Point(50, 100)));
+ return createCutoutWithInsets(0, 100, 0, 0);
}
private static DisplayCutout createCutoutWithInsets(int left, int top, int right, int bottom) {
return new DisplayCutout(
new Rect(left, top, right, bottom),
- new Rect(50, 0, 75, 100),
- Arrays.asList(
- new Point(75, 0),
- new Point(50, 0),
- new Point(75, 100),
- new Point(50, 100)));
+ new Region(50, 0, 75, 100));
}
}
diff --git a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
index 23d0251..fba8eae 100644
--- a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
+++ b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
@@ -17,6 +17,7 @@
package android.view;
import android.content.Context;
+import android.support.test.filters.LargeTest;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.ActivityInstrumentationTestCase2;
@@ -36,6 +37,7 @@
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.Espresso.onView;
+@LargeTest
public class ScaleGestureDetectorTest extends ActivityInstrumentationTestCase2<ScaleGesture> {
private ScaleGesture mScaleGestureActivity;
diff --git a/core/tests/coretests/src/android/view/ViewAttachTest.java b/core/tests/coretests/src/android/view/ViewAttachTest.java
index 44fcd13..aa8f8d8 100644
--- a/core/tests/coretests/src/android/view/ViewAttachTest.java
+++ b/core/tests/coretests/src/android/view/ViewAttachTest.java
@@ -20,6 +20,7 @@
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.os.SystemClock;
+import android.support.test.filters.LargeTest;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
import android.view.View;
@@ -28,6 +29,7 @@
import com.android.frameworks.coretests.R;
+@LargeTest
public class ViewAttachTest extends
ActivityInstrumentationTestCase2<ViewAttachTestActivity> {
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index 2f32d13..4de8155 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -28,6 +28,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.View;
@@ -42,7 +43,7 @@
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
-
+@LargeTest
@RunWith(AndroidJUnit4.class)
public class AccessibilityCacheTest {
private static final int WINDOW_ID_1 = 0xBEEF;
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
index c1b2309..318d122 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
@@ -22,6 +22,7 @@
import android.os.Bundle;
import android.os.RemoteException;
+import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
import libcore.util.EmptyArray;
@@ -36,6 +37,7 @@
/**
* Tests for AccessibilityInteractionClient
*/
+@LargeTest
@RunWith(AndroidJUnit4.class)
public class AccessibilityInteractionClientTest {
private static final int MOCK_CONNECTION_ID = 0xabcd;
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 79e6316..b135025 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.fail;
+import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.ArraySet;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -29,6 +30,7 @@
import java.util.ArrayList;
+@LargeTest
@RunWith(AndroidJUnit4.class)
public class AccessibilityNodeInfoTest {
diff --git a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
index e0664d9..0fd0136 100644
--- a/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/CursorAnchorInfoTest.java
@@ -16,21 +16,33 @@
package android.view.inputmethod;
-import android.graphics.Matrix;
-import android.graphics.RectF;
-import android.os.Parcel;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.text.TextUtils;
-import android.view.inputmethod.CursorAnchorInfo.Builder;
-
-import java.util.Objects;
-
import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
import static android.view.inputmethod.CursorAnchorInfo.FLAG_IS_RTL;
-public class CursorAnchorInfoTest extends InstrumentationTestCase {
+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.graphics.Matrix;
+import android.graphics.RectF;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
+import android.view.inputmethod.CursorAnchorInfo.Builder;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Objects;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class CursorAnchorInfoTest {
+ private static final float EPSILON = 0.0000001f;
+
private static final RectF[] MANY_BOUNDS = new RectF[] {
new RectF(101.0f, 201.0f, 301.0f, 401.0f),
new RectF(102.0f, 202.0f, 302.0f, 402.0f),
@@ -74,7 +86,7 @@
FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL,
};
- @SmallTest
+ @Test
public void testBuilder() throws Exception {
final int SELECTION_START = 30;
final int SELECTION_END = 40;
@@ -109,10 +121,10 @@
assertEquals(COMPOSING_TEXT_START, info.getComposingTextStart());
assertTrue(TextUtils.equals(COMPOSING_TEXT, info.getComposingText()));
assertEquals(INSERTION_MARKER_FLAGS, info.getInsertionMarkerFlags());
- assertEquals(INSERTION_MARKER_HORIZONTAL, info.getInsertionMarkerHorizontal());
- assertEquals(INSERTION_MARKER_TOP, info.getInsertionMarkerTop());
- assertEquals(INSERTION_MARKER_BASELINE, info.getInsertionMarkerBaseline());
- assertEquals(INSERTION_MARKER_BOTOM, info.getInsertionMarkerBottom());
+ assertEquals(INSERTION_MARKER_HORIZONTAL, info.getInsertionMarkerHorizontal(), EPSILON);
+ assertEquals(INSERTION_MARKER_TOP, info.getInsertionMarkerTop(), EPSILON);
+ assertEquals(INSERTION_MARKER_BASELINE, info.getInsertionMarkerBaseline(), EPSILON);
+ assertEquals(INSERTION_MARKER_BOTOM, info.getInsertionMarkerBottom(), EPSILON);
assertEquals(TRANSFORM_MATRIX, info.getMatrix());
for (int i = 0; i < MANY_BOUNDS.length; i++) {
final RectF expectedBounds = MANY_BOUNDS[i];
@@ -134,10 +146,10 @@
assertEquals(COMPOSING_TEXT_START, info2.getComposingTextStart());
assertTrue(TextUtils.equals(COMPOSING_TEXT, info2.getComposingText()));
assertEquals(INSERTION_MARKER_FLAGS, info2.getInsertionMarkerFlags());
- assertEquals(INSERTION_MARKER_HORIZONTAL, info2.getInsertionMarkerHorizontal());
- assertEquals(INSERTION_MARKER_TOP, info2.getInsertionMarkerTop());
- assertEquals(INSERTION_MARKER_BASELINE, info2.getInsertionMarkerBaseline());
- assertEquals(INSERTION_MARKER_BOTOM, info2.getInsertionMarkerBottom());
+ assertEquals(INSERTION_MARKER_HORIZONTAL, info2.getInsertionMarkerHorizontal(), EPSILON);
+ assertEquals(INSERTION_MARKER_TOP, info2.getInsertionMarkerTop(), EPSILON);
+ assertEquals(INSERTION_MARKER_BASELINE, info2.getInsertionMarkerBaseline(), EPSILON);
+ assertEquals(INSERTION_MARKER_BOTOM, info2.getInsertionMarkerBottom(), EPSILON);
assertEquals(TRANSFORM_MATRIX, info2.getMatrix());
for (int i = 0; i < MANY_BOUNDS.length; i++) {
final RectF expectedBounds = MANY_BOUNDS[i];
@@ -161,10 +173,10 @@
assertEquals(COMPOSING_TEXT_START, info3.getComposingTextStart());
assertTrue(TextUtils.equals(COMPOSING_TEXT, info3.getComposingText()));
assertEquals(INSERTION_MARKER_FLAGS, info3.getInsertionMarkerFlags());
- assertEquals(INSERTION_MARKER_HORIZONTAL, info3.getInsertionMarkerHorizontal());
- assertEquals(INSERTION_MARKER_TOP, info3.getInsertionMarkerTop());
- assertEquals(INSERTION_MARKER_BASELINE, info3.getInsertionMarkerBaseline());
- assertEquals(INSERTION_MARKER_BOTOM, info3.getInsertionMarkerBottom());
+ assertEquals(INSERTION_MARKER_HORIZONTAL, info3.getInsertionMarkerHorizontal(), EPSILON);
+ assertEquals(INSERTION_MARKER_TOP, info3.getInsertionMarkerTop(), EPSILON);
+ assertEquals(INSERTION_MARKER_BASELINE, info3.getInsertionMarkerBaseline(), EPSILON);
+ assertEquals(INSERTION_MARKER_BOTOM, info3.getInsertionMarkerBottom(), EPSILON);
assertEquals(TRANSFORM_MATRIX, info3.getMatrix());
for (int i = 0; i < MANY_BOUNDS.length; i++) {
final RectF expectedBounds = MANY_BOUNDS[i];
@@ -187,10 +199,10 @@
assertEquals(-1, uninitializedInfo.getComposingTextStart());
assertNull(uninitializedInfo.getComposingText());
assertEquals(0, uninitializedInfo.getInsertionMarkerFlags());
- assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal());
- assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop());
- assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline());
- assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom());
+ assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal(), EPSILON);
+ assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop(), EPSILON);
+ assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline(), EPSILON);
+ assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom(), EPSILON);
assertEquals(Matrix.IDENTITY_MATRIX, uninitializedInfo.getMatrix());
}
@@ -199,7 +211,7 @@
assertFalse(Objects.equals(reference, actual));
}
- @SmallTest
+ @Test
public void testEquality() throws Exception {
final Matrix MATRIX1 = new Matrix();
MATRIX1.setTranslate(10.0f, 20.0f);
@@ -357,7 +369,7 @@
INSERTION_MARKER_FLAGS2).build());
}
- @SmallTest
+ @Test
public void testMatrixIsCopied() throws Exception {
final Matrix MATRIX1 = new Matrix();
MATRIX1.setTranslate(10.0f, 20.0f);
@@ -385,7 +397,7 @@
assertEquals(MATRIX2, secondInstance.getMatrix());
}
- @SmallTest
+ @Test
public void testMatrixIsRequired() throws Exception {
final int SELECTION_START = 30;
final int SELECTION_END = 40;
@@ -434,7 +446,7 @@
}
}
- @SmallTest
+ @Test
public void testBuilderAddCharacterBounds() throws Exception {
// A negative index should be rejected.
try {
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
index e3c91e6..9edbf3e 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -26,6 +26,7 @@
import android.content.pm.ServiceInfo;
import android.os.Bundle;
import android.os.Parcel;
+import android.support.test.filters.LargeTest;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
@@ -34,6 +35,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+@LargeTest
@RunWith(AndroidJUnit4.class)
public class InputMethodInfoTest {
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
index a339f61..8df1848 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
@@ -16,16 +16,24 @@
package android.view.inputmethod;
+import static org.junit.Assert.assertEquals;
+
import android.os.Parcel;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.util.ArrayList;
-public class InputMethodSubtypeArrayTest extends InstrumentationTestCase {
- @SmallTest
- public void testInstanciate() throws Exception {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputMethodSubtypeArrayTest {
+
+ @Test
+ public void testInstantiate() throws Exception {
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
subtypes.add(createDummySubtype(0, "en_US"));
subtypes.add(createDummySubtype(1, "en_US"));
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
index d203465..c76359e 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeTest.java
@@ -16,15 +16,26 @@
package android.view.inputmethod;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
import android.os.Parcel;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.util.Locale;
import java.util.Objects;
-public class InputMethodSubtypeTest extends InstrumentationTestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputMethodSubtypeTest {
public void verifyLocale(final String localeString) {
// InputMethodSubtype#getLocale() returns exactly the same string that is passed to the
@@ -48,7 +59,7 @@
cloneViaParcel(cloneViaParcel(createDummySubtype(localeString))).hashCode());
}
- @SmallTest
+ @Test
public void testLocaleObj_locale() {
final InputMethodSubtype usSubtype = createDummySubtype("en_US");
Locale localeObject = usSubtype.getLocaleObject();
@@ -59,7 +70,7 @@
assertTrue(localeObject == usSubtype.getLocaleObject());
}
- @SmallTest
+ @Test
public void testLocaleObj_languageTag() {
final InputMethodSubtype usSubtype = createDummySubtypeUsingLanguageTag("en-US");
Locale localeObject = usSubtype.getLocaleObject();
@@ -71,7 +82,7 @@
assertTrue(localeObject == usSubtype.getLocaleObject());
}
- @SmallTest
+ @Test
public void testLocaleObj_emptyLocale() {
final InputMethodSubtype emptyLocaleSubtype = createDummySubtype("");
assertNull(emptyLocaleSubtype.getLocaleObject());
@@ -80,7 +91,7 @@
assertNull(emptyLocaleSubtype.getLocaleObject());
}
- @SmallTest
+ @Test
public void testLocaleString() throws Exception {
// The locale string in InputMethodSubtype has accepted an arbitrary text actually,
// regardless of the validity of the text as a locale string.
@@ -95,7 +106,7 @@
verifyLocale("fil_PH");
}
- @SmallTest
+ @Test
public void testDeprecatedLocaleString() throws Exception {
// Make sure "iw" is not automatically replaced with "he".
final InputMethodSubtype subtypeIw = createDummySubtype("iw");
diff --git a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
index f9fa017..8c96b58 100644
--- a/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/SparseRectFArrayTest.java
@@ -16,15 +16,25 @@
package android.view.inputmethod;
+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.graphics.RectF;
import android.os.Parcel;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.util.Objects;
-public class SparseRectFArrayTest extends InstrumentationTestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SparseRectFArrayTest {
// A test data for {@link SparseRectFArray}. null represents the gap of indices.
private static final RectF[] MANY_RECTS = new RectF[] {
null,
@@ -49,7 +59,7 @@
new RectF(118.0f, 218.0f, 318.0f, 418.0f),
};
- @SmallTest
+ @Test
public void testBuilder() throws Exception {
final RectF TEMP_RECT = new RectF(10.0f, 20.0f, 30.0f, 40.0f);
final int TEMP_FLAGS = 0x1234;
@@ -128,7 +138,7 @@
assertNull(builder.build().get(0));
}
- @SmallTest
+ @Test
public void testEquality() throws Exception {
// Empty array should be equal.
assertEqualRects(new SparseRectFArrayBuilder().build(),
@@ -225,7 +235,7 @@
.build());
}
- @SmallTest
+ @Test
public void testBuilderAppend() throws Exception {
// Key should be appended in ascending order.
try {
diff --git a/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java b/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java
index 9347b27..8ed0d86 100644
--- a/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java
+++ b/core/tests/coretests/src/android/view/menu/MenuLayoutLandscapeTest.java
@@ -21,8 +21,10 @@
import com.android.internal.view.menu.MenuBuilder;
import android.content.pm.ActivityInfo;
+import android.support.test.filters.LargeTest;
import android.test.ActivityInstrumentationTestCase;
+@LargeTest
public class MenuLayoutLandscapeTest extends ActivityInstrumentationTestCase<MenuLayoutLandscape> {
private static final String LONG_TITLE = "Really really really really really really really really really really long title";
private static final String SHORT_TITLE = "Item";
diff --git a/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java b/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java
index b053699..ccf1264 100644
--- a/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java
+++ b/core/tests/coretests/src/android/view/menu/MenuLayoutPortraitTest.java
@@ -16,13 +16,12 @@
package android.view.menu;
-import android.util.KeyUtils;
-import com.android.internal.view.menu.IconMenuView;
-import com.android.internal.view.menu.MenuBuilder;
-
import android.content.pm.ActivityInfo;
+import android.support.test.filters.LargeTest;
import android.test.ActivityInstrumentationTestCase;
+import android.util.KeyUtils;
+@LargeTest
public class MenuLayoutPortraitTest extends ActivityInstrumentationTestCase<MenuLayoutPortrait> {
private static final String LONG_TITLE = "Really really really really really really really really really really long title";
private static final String SHORT_TITLE = "Item";
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 9092c85..a8de374 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -16,10 +16,9 @@
package android.view.textclassifier;
+import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import android.os.LocaleList;
@@ -34,8 +33,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.Collection;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TextClassificationManagerTest {
@@ -166,20 +163,50 @@
}
@Test
- public void testGenerateLinks() {
+ public void testGenerateLinks_phone() {
if (isTextClassifierDisabled()) return;
+ String text = "The number is +12122537077. See you tonight!";
+ assertThat(mClassifier.generateLinks(text, null),
+ isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE));
+ }
- checkGenerateLinksFindsLink(
- "The number is +12122537077. See you tonight!",
- "+12122537077",
- TextClassifier.TYPE_PHONE);
+ @Test
+ public void testGenerateLinks_exclude() {
+ if (isTextClassifierDisabled()) return;
+ String text = "The number is +12122537077. See you tonight!";
+ assertThat(mClassifier.generateLinks(text, mLinksOptions.setEntityConfig(
+ new TextClassifier.EntityConfig(TextClassifier.ENTITY_PRESET_ALL)
+ .excludeEntities(TextClassifier.TYPE_PHONE))),
+ not(isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE)));
+ }
- checkGenerateLinksFindsLink(
- "The address is 1600 Amphitheater Parkway, Mountain View, CA. See you tonight!",
- "1600 Amphitheater Parkway, Mountain View, CA",
- TextClassifier.TYPE_ADDRESS);
+ @Test
+ public void testGenerateLinks_none_config() {
+ if (isTextClassifierDisabled()) return;
+ String text = "The number is +12122537077. See you tonight!";
+ assertThat(mClassifier.generateLinks(text, mLinksOptions.setEntityConfig(
+ new TextClassifier.EntityConfig(TextClassifier.ENTITY_PRESET_NONE))),
+ not(isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE)));
+ }
- // TODO: Add more entity types when the model supports them.
+ @Test
+ public void testGenerateLinks_address() {
+ if (isTextClassifierDisabled()) return;
+ String text = "The address is 1600 Amphitheater Parkway, Mountain View, CA. See you!";
+ assertThat(mClassifier.generateLinks(text, null),
+ isTextLinksContaining(text, "1600 Amphitheater Parkway, Mountain View, CA",
+ TextClassifier.TYPE_ADDRESS));
+ }
+
+ @Test
+ public void testGenerateLinks_include() {
+ if (isTextClassifierDisabled()) return;
+ String text = "The address is 1600 Amphitheater Parkway, Mountain View, CA. See you!";
+ assertThat(mClassifier.generateLinks(text, mLinksOptions.setEntityConfig(
+ new TextClassifier.EntityConfig(TextClassifier.ENTITY_PRESET_NONE)
+ .includeEntities(TextClassifier.TYPE_ADDRESS))),
+ isTextLinksContaining(text, "1600 Amphitheater Parkway, Mountain View, CA",
+ TextClassifier.TYPE_ADDRESS));
}
@Test
@@ -193,25 +220,6 @@
return mClassifier == TextClassifier.NO_OP;
}
- private void checkGenerateLinksFindsLink(String text, String classifiedText, String type) {
- assertTrue(text.contains(classifiedText));
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
-
- Collection<TextLinks.TextLink> links = mClassifier.generateLinks(text, mLinksOptions)
- .getLinks();
- for (TextLinks.TextLink link : links) {
- if (text.subSequence(link.getStart(), link.getEnd()).equals(classifiedText)) {
- assertEquals(type, link.getEntity(0));
- assertEquals(startIndex, link.getStart());
- assertEquals(endIndex, link.getEnd());
- assertTrue(link.getConfidenceScore(type) > 0);
- return;
- }
- }
- fail(); // Subsequence was not identified.
- }
-
private static Matcher<TextSelection> isTextSelection(
final int startIndex, final int endIndex, final String type) {
return new BaseMatcher<TextSelection>() {
@@ -240,6 +248,31 @@
};
}
+ private static Matcher<TextLinks> isTextLinksContaining(
+ final String text, final String substring, final String type) {
+ return new BaseMatcher<TextLinks>() {
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("text=").appendValue(text)
+ .appendText(", substring=").appendValue(substring)
+ .appendText(", type=").appendValue(type);
+ }
+
+ @Override
+ public boolean matches(Object o) {
+ if (o instanceof TextLinks) {
+ for (TextLinks.TextLink link : ((TextLinks) o).getLinks()) {
+ if (text.subSequence(link.getStart(), link.getEnd()).equals(substring)) {
+ return type.equals(link.getEntity(0));
+ }
+ }
+ }
+ return false;
+ }
+ };
+ }
+
private static Matcher<TextClassification> isTextClassification(
final String text, final String type, final String intentUri) {
return new BaseMatcher<TextClassification>() {
diff --git a/core/tests/coretests/src/android/widget/DatePickerFocusTest.java b/core/tests/coretests/src/android/widget/DatePickerFocusTest.java
index 513e40f..be85450 100644
--- a/core/tests/coretests/src/android/widget/DatePickerFocusTest.java
+++ b/core/tests/coretests/src/android/widget/DatePickerFocusTest.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import android.app.Instrumentation;
import android.os.SystemClock;
+import android.support.test.filters.LargeTest;
import android.test.ActivityInstrumentationTestCase2;
import android.view.KeyEvent;
import android.view.View;
@@ -28,6 +29,7 @@
/**
* Test {@link DatePicker} focus changes.
*/
+@LargeTest
public class DatePickerFocusTest extends ActivityInstrumentationTestCase2<DatePickerActivity> {
private Activity mActivity;
diff --git a/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java b/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java
index 28a3b67..2add221 100644
--- a/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java
+++ b/core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java
@@ -22,6 +22,7 @@
import android.graphics.PointF;
import android.graphics.RectF;
+import android.support.test.filters.LargeTest;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,6 +32,7 @@
import java.util.Arrays;
import java.util.List;
+@LargeTest
@RunWith(JUnit4.class)
public final class SelectionActionModeHelperTest {
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 0e460b9..1a654f4 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -27,8 +27,10 @@
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static android.widget.espresso.CustomViewActions.longPressAtRelativeCoordinates;
import static android.widget.espresso.DragHandleUtils.onHandleView;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarDoesNotContainItem;
+import static android.widget.espresso.FloatingToolbarEspressoUtils
+ .assertFloatingToolbarContainsItem;
+import static android.widget.espresso.FloatingToolbarEspressoUtils
+ .assertFloatingToolbarDoesNotContainItem;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarItemIndex;
import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
@@ -68,12 +70,15 @@
import android.text.InputType;
import android.text.Selection;
import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.method.LinkMovementMethod;
import android.view.ActionMode;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
import android.widget.espresso.CustomViewActions.RelativeCoordinatesProvider;
import com.android.frameworks.coretests.R;
@@ -305,6 +310,33 @@
}
@Test
+ public void testToolbarAppearsAfterLinkClicked() throws Throwable {
+ useSystemDefaultTextClassifier();
+ TextClassificationManager textClassificationManager =
+ mActivity.getSystemService(TextClassificationManager.class);
+ TextClassifier textClassifier = textClassificationManager.getTextClassifier();
+ final TextView textView = mActivity.findViewById(R.id.textview);
+ SpannableString content = new SpannableString("Call me at +19148277737");
+ TextLinks links = textClassifier.generateLinks(content);
+ links.apply(content, null);
+
+ mActivityRule.runOnUiThread(() -> {
+ textView.setText(content);
+ textView.setMovementMethod(LinkMovementMethod.getInstance());
+ });
+ mInstrumentation.waitForIdleSync();
+
+ // 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(R.id.textview)).perform(clickOnTextAtIndex(position));
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+ }
+
+ @Test
public void testToolbarAndInsertionHandle() {
final String text = "text";
onView(withId(R.id.textview)).perform(replaceText(text));
diff --git a/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearchTest.java b/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearchTest.java
index b591e5f..43986ee 100644
--- a/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearchTest.java
+++ b/core/tests/coretests/src/android/widget/focus/HorizontalFocusSearchTest.java
@@ -18,6 +18,7 @@
import android.widget.focus.HorizontalFocusSearch;
+import android.support.test.filters.LargeTest;
import android.test.ActivityInstrumentationTestCase;
import android.test.suitebuilder.annotation.Suppress;
import android.widget.LinearLayout;
@@ -32,6 +33,7 @@
* various widths and vertical placements.
*/
// Suppress until bug http://b/issue?id=1416545 is fixed.
+@LargeTest
@Suppress
public class HorizontalFocusSearchTest extends ActivityInstrumentationTestCase<HorizontalFocusSearch> {
diff --git a/core/tests/coretests/src/android/widget/focus/VerticalFocusSearchTest.java b/core/tests/coretests/src/android/widget/focus/VerticalFocusSearchTest.java
index f05d83a..f01422e 100644
--- a/core/tests/coretests/src/android/widget/focus/VerticalFocusSearchTest.java
+++ b/core/tests/coretests/src/android/widget/focus/VerticalFocusSearchTest.java
@@ -18,6 +18,7 @@
import android.widget.focus.VerticalFocusSearch;
+import android.support.test.filters.LargeTest;
import android.test.ActivityInstrumentationTestCase;
import android.test.suitebuilder.annotation.Suppress;
import android.view.FocusFinder;
@@ -31,6 +32,7 @@
* various widths and horizontal placements.
*/
// Suppress until bug http://b/issue?id=1416545 is fixed
+@LargeTest
@Suppress
public class VerticalFocusSearchTest extends ActivityInstrumentationTestCase<VerticalFocusSearch> {
diff --git a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithFirstScreenUnSelectableTest.java b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithFirstScreenUnSelectableTest.java
index 400fd7d..9a8e634 100644
--- a/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithFirstScreenUnSelectableTest.java
+++ b/core/tests/coretests/src/android/widget/listview/arrowscroll/ListWithFirstScreenUnSelectableTest.java
@@ -16,12 +16,14 @@
package android.widget.listview.arrowscroll;
-import android.widget.listview.ListWithFirstScreenUnSelectable;
+import android.support.test.filters.LargeTest;
import android.test.ActivityInstrumentationTestCase2;
import android.view.KeyEvent;
import android.widget.ListView;
+import android.widget.listview.ListWithFirstScreenUnSelectable;
import android.widget.AdapterView;
+@LargeTest
public class ListWithFirstScreenUnSelectableTest
extends ActivityInstrumentationTestCase2<ListWithFirstScreenUnSelectable> {
private ListView mListView;
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index 9061c44..13d6749 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -16,11 +16,15 @@
package com.android.internal.inputmethod;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
@@ -28,11 +32,16 @@
import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ControllerImpl;
import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputMethodSubtypeSwitchingControllerTest {
private static final String DUMMY_PACKAGE_NAME = "dummy package name";
private static final String DUMMY_IME_LABEL = "dummy ime label";
private static final String DUMMY_SETTING_ACTIVITY_NAME = "";
@@ -190,7 +199,7 @@
controller.onUserActionLocked(subtypeListItem.mImi, subtype);
}
- @SmallTest
+ @Test
public void testControllerImpl() throws Exception {
final List<ImeSubtypeListItem> disabledItems = createDisabledImeSubtypes();
final ImeSubtypeListItem disabledIme_en_US = disabledItems.get(0);
@@ -250,7 +259,7 @@
disabledSubtypeUnawareIme, null, null);
}
- @SmallTest
+ @Test
public void testControllerImplWithUserAction() throws Exception {
final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes();
final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0);
@@ -331,7 +340,7 @@
switchingUnawarelatinIme_en_UK, switchUnawareJapaneseIme_ja_JP);
}
- @SmallTest
+ @Test
public void testImeSubtypeListItem() throws Exception {
final List<ImeSubtypeListItem> items = new ArrayList<>();
addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme",
@@ -360,7 +369,7 @@
assertFalse(item_EN_US.mIsSystemLocale);
}
- @SmallTest
+ @Test
public void testImeSubtypeListComparator() throws Exception {
{
final List<ImeSubtypeListItem> items = Arrays.asList(
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
index d89dc63..e6ac682 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
@@ -16,38 +16,52 @@
package com.android.internal.inputmethod;
+import static android.view.inputmethod.InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR;
+import static android.view.inputmethod.InputMethodManager.CONTROL_WINDOW_VIEW_HAS_FOCUS;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.in;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.os.Build;
import android.os.LocaleList;
import android.os.Parcel;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
import android.view.inputmethod.InputMethodSubtype;
+import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.isIn;
-import static org.hamcrest.Matchers.not;
-
-public class InputMethodUtilsTest extends InstrumentationTestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputMethodUtilsTest {
private static final boolean IS_AUX = true;
private static final boolean IS_DEFAULT = true;
private static final boolean IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE = true;
private static final boolean IS_ASCII_CAPABLE = true;
private static final boolean IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE = true;
- private static final boolean IS_SYSTEM_READY = true;
private static final Locale LOCALE_EN = new Locale("en");
private static final Locale LOCALE_EN_US = new Locale("en", "US");
private static final Locale LOCALE_EN_GB = new Locale("en", "GB");
@@ -77,7 +91,7 @@
private static final String EXTRA_VALUE_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE =
"EnabledWhenDefaultIsNotAsciiCapable";
- @SmallTest
+ @Test
public void testVoiceImes() throws Exception {
// locale: en_US
assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US,
@@ -101,7 +115,7 @@
"DummyNonDefaultAutoVoiceIme1");
}
- @SmallTest
+ @Test
public void testKeyboardImes() throws Exception {
// locale: en_US
assertDefaultEnabledImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US,
@@ -136,7 +150,7 @@
"com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice");
}
- @SmallTest
+ @Test
public void testParcelable() throws Exception {
final ArrayList<InputMethodInfo> originalList = getSamplePreinstalledImes("en-rUS");
final List<InputMethodInfo> clonedList = cloneViaParcel(originalList);
@@ -153,7 +167,7 @@
}
}
- @SmallTest
+ @Test
public void testGetImplicitlyApplicableSubtypesLocked() throws Exception {
final InputMethodSubtype nonAutoEnUS = createDummyInputMethodSubtype("en_US",
SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
@@ -434,8 +448,8 @@
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
getResourcesForLocales(Locale.forLanguageTag("sr-Latn-RS")), imi);
assertEquals(2, result.size());
- assertThat(nonAutoSrLatn, isIn(result));
- assertThat(nonAutoHandwritingSrLatn, isIn(result));
+ assertThat(nonAutoSrLatn, is(in(result)));
+ assertThat(nonAutoHandwritingSrLatn, is(in(result)));
}
{
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
@@ -454,8 +468,8 @@
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
getResourcesForLocales(Locale.forLanguageTag("sr-Cyrl-RS")), imi);
assertEquals(2, result.size());
- assertThat(nonAutoSrCyrl, isIn(result));
- assertThat(nonAutoHandwritingSrCyrl, isIn(result));
+ assertThat(nonAutoSrCyrl, is(in(result)));
+ assertThat(nonAutoHandwritingSrCyrl, is(in(result)));
}
// Make sure that secondary locales are taken into account to find the best matching
@@ -486,12 +500,12 @@
Locale.forLanguageTag("en-US")),
imi);
assertEquals(6, result.size());
- assertThat(nonAutoEnGB, isIn(result));
- assertThat(nonAutoFr, isIn(result));
- assertThat(nonAutoSrLatn, isIn(result));
- assertThat(nonAutoHandwritingEn, isIn(result));
- assertThat(nonAutoHandwritingFr, isIn(result));
- assertThat(nonAutoHandwritingSrLatn, isIn(result));
+ assertThat(nonAutoEnGB, is(in(result)));
+ assertThat(nonAutoFr, is(in(result)));
+ assertThat(nonAutoSrLatn, is(in(result)));
+ assertThat(nonAutoHandwritingEn, is(in(result)));
+ assertThat(nonAutoHandwritingFr, is(in(result)));
+ assertThat(nonAutoHandwritingSrLatn, is(in(result)));
}
// Make sure that 3-letter language code can be handled.
@@ -604,16 +618,16 @@
final ArrayList<InputMethodSubtype> result =
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
getResourcesForLocales(LOCALE_FR, LOCALE_EN_US, LOCALE_JA_JP), imi);
- assertThat(nonAutoFrCA, isIn(result));
- assertThat(nonAutoEnUS, isIn(result));
- assertThat(nonAutoJa, isIn(result));
- assertThat(nonAutoIn, not(isIn(result)));
- assertThat(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, not(isIn(result)));
- assertThat(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, not(isIn(result)));
+ assertThat(nonAutoFrCA, is(in(result)));
+ assertThat(nonAutoEnUS, is(in(result)));
+ assertThat(nonAutoJa, is(in(result)));
+ assertThat(nonAutoIn, not(is(in(result))));
+ assertThat(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, not(is(in(result))));
+ assertThat(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, not(is(in(result))));
}
}
- @SmallTest
+ @Test
public void testContainsSubtypeOf() throws Exception {
final InputMethodSubtype nonAutoEnUS = createDummyInputMethodSubtype("en_US",
SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
@@ -788,7 +802,7 @@
private Context createTargetContextWithLocales(final LocaleList locales) {
final Configuration resourceConfiguration = new Configuration();
resourceConfiguration.setLocales(locales);
- return getInstrumentation()
+ return InstrumentationRegistry.getInstrumentation()
.getTargetContext()
.createConfigurationContext(resourceConfiguration);
}
@@ -1043,7 +1057,7 @@
return preinstalledImes;
}
- @SmallTest
+ @Test
public void testGetSuitableLocalesForSpellChecker() throws Exception {
{
final ArrayList<Locale> locales =
@@ -1138,7 +1152,7 @@
}
}
- @SmallTest
+ @Test
public void testParseInputMethodsAndSubtypesString() {
// Trivial cases.
{
@@ -1264,7 +1278,7 @@
}
}
- @SmallTest
+ @Test
public void testbuildInputMethodsAndSubtypesString() {
{
ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
@@ -1272,7 +1286,7 @@
}
{
ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
- map.put("ime0", new ArraySet<String>());
+ map.put("ime0", new ArraySet<>());
assertEquals("ime0", InputMethodUtils.buildInputMethodsAndSubtypesString(map));
}
{
@@ -1300,8 +1314,8 @@
}
{
ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
- map.put("ime0", new ArraySet<String>());
- map.put("ime1", new ArraySet<String>());
+ map.put("ime0", new ArraySet<>());
+ map.put("ime1", new ArraySet<>());
ArraySet<String> validSequences = new ArraySet<>();
validSequences.add("ime0:ime1");
@@ -1314,7 +1328,7 @@
ArraySet<String> subtypes1 = new ArraySet<>();
subtypes1.add("subtype0");
map.put("ime0", subtypes1);
- map.put("ime1", new ArraySet<String>());
+ map.put("ime1", new ArraySet<>());
ArraySet<String> validSequences = new ArraySet<>();
validSequences.add("ime0;subtype0:ime1");
@@ -1328,7 +1342,7 @@
subtypes1.add("subtype0");
subtypes1.add("subtype1");
map.put("ime0", subtypes1);
- map.put("ime1", new ArraySet<String>());
+ map.put("ime1", new ArraySet<>());
ArraySet<String> validSequences = new ArraySet<>();
validSequences.add("ime0;subtype0;subtype1:ime1");
@@ -1380,7 +1394,7 @@
}
}
- @SmallTest
+ @Test
public void testConstructLocaleFromString() throws Exception {
assertEquals(new Locale("en"), InputMethodUtils.constructLocaleFromString("en"));
assertEquals(new Locale("en", "US"), InputMethodUtils.constructLocaleFromString("en_US"));
@@ -1400,4 +1414,29 @@
assertEquals(new Locale("a b c"), InputMethodUtils.constructLocaleFromString("a b c"));
assertEquals(new Locale("en-US"), InputMethodUtils.constructLocaleFromString("en-US"));
}
+
+ @Test
+ public void testIsSoftInputModeStateVisibleAllowed() {
+ // On pre-P devices, SOFT_INPUT_STATE_VISIBLE/SOFT_INPUT_STATE_ALWAYS_VISIBLE are always
+ // allowed, regardless of the focused view state.
+ assertTrue(InputMethodUtils.isSoftInputModeStateVisibleAllowed(
+ Build.VERSION_CODES.O_MR1, 0));
+ assertTrue(InputMethodUtils.isSoftInputModeStateVisibleAllowed(
+ Build.VERSION_CODES.O_MR1, CONTROL_WINDOW_VIEW_HAS_FOCUS));
+ assertTrue(InputMethodUtils.isSoftInputModeStateVisibleAllowed(
+ Build.VERSION_CODES.O_MR1,
+ CONTROL_WINDOW_VIEW_HAS_FOCUS | CONTROL_WINDOW_IS_TEXT_EDITOR));
+
+ // On P+ devices, SOFT_INPUT_STATE_VISIBLE/SOFT_INPUT_STATE_ALWAYS_VISIBLE are allowed only
+ // when there is a focused View and its View#onCheckIsTextEditor() returns true.
+ assertFalse(InputMethodUtils.isSoftInputModeStateVisibleAllowed(
+ Build.VERSION_CODES.P, 0));
+ assertFalse(InputMethodUtils.isSoftInputModeStateVisibleAllowed(
+ Build.VERSION_CODES.P, CONTROL_WINDOW_VIEW_HAS_FOCUS));
+ assertTrue(InputMethodUtils.isSoftInputModeStateVisibleAllowed(
+ Build.VERSION_CODES.P,
+ CONTROL_WINDOW_VIEW_HAS_FOCUS | CONTROL_WINDOW_IS_TEXT_EDITOR));
+
+ }
+
}
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
index cd339fb..549511a 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
@@ -16,24 +16,25 @@
package com.android.internal.inputmethod;
+import static org.junit.Assert.assertEquals;
+
import android.os.LocaleList;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Locale;
-public class LocaleUtilsTest extends InstrumentationTestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LocaleUtilsTest {
- private static final LocaleUtils.LocaleExtractor<Locale> sIdentityMapper =
- new LocaleUtils.LocaleExtractor<Locale>() {
- @Override
- public Locale get(Locale source) {
- return source;
- }
- };
+ private static final LocaleUtils.LocaleExtractor<Locale> sIdentityMapper = source -> source;
- @SmallTest
+ @Test
public void testFilterByLanguageEmptyLanguageList() throws Exception {
final ArrayList<Locale> availableLocales = new ArrayList<>();
availableLocales.add(Locale.forLanguageTag("en-US"));
@@ -49,7 +50,7 @@
assertEquals(0, dest.size());
}
- @SmallTest
+ @Test
public void testFilterDoesNotMatchAnything() throws Exception {
final ArrayList<Locale> availableLocales = new ArrayList<>();
availableLocales.add(Locale.forLanguageTag("en-US"));
@@ -65,7 +66,7 @@
assertEquals(0, dest.size());
}
- @SmallTest
+ @Test
public void testFilterByLanguageEmptySource() throws Exception {
final ArrayList<Locale> availableLocales = new ArrayList<>();
@@ -76,7 +77,7 @@
assertEquals(0, dest.size());
}
- @SmallTest
+ @Test
public void testFilterByLanguageNullAvailableLocales() throws Exception {
{
final LocaleList preferredLocales =
@@ -138,7 +139,7 @@
}
}
- @SmallTest
+ @Test
public void testFilterByLanguage() throws Exception {
{
final ArrayList<Locale> availableLocales = new ArrayList<>();
@@ -172,7 +173,7 @@
}
}
- @SmallTest
+ @Test
public void testFilterByLanguageTheSameLanguage() throws Exception {
{
final LocaleList preferredLocales =
@@ -223,7 +224,7 @@
}
}
- @SmallTest
+ @Test
public void testFilterByLanguageFallbackRules() throws Exception {
{
final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn-RS");
@@ -355,6 +356,7 @@
}
}
+ @Test
public void testFilterKnownLimitation() throws Exception {
// Following test cases are not for intentional behavior but checks for preventing the
// behavior from becoming worse.
diff --git a/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk b/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk
index 193e601..b255c7e 100644
--- a/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk
+++ b/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk
@@ -22,6 +22,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := junit
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
LOCAL_SDK_VERSION := current
LOCAL_PACKAGE_NAME := ExternalSharedPermsTestApp
diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk b/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk
index 9f44767..428b1f8 100644
--- a/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk
+++ b/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk
@@ -22,6 +22,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := junit
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
LOCAL_SDK_VERSION := current
LOCAL_PACKAGE_NAME := ExternalSharedPermsBTTestApp
diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk b/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk
index b4d354c..93ece86 100644
--- a/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk
+++ b/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk
@@ -22,6 +22,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := junit
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
LOCAL_SDK_VERSION := current
LOCAL_PACKAGE_NAME := ExternalSharedPermsDiffKeyTestApp
diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk b/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk
index 969e588..b348966 100644
--- a/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk
+++ b/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk
@@ -22,6 +22,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := junit
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
LOCAL_SDK_VERSION := current
LOCAL_PACKAGE_NAME := ExternalSharedPermsFLTestApp
diff --git a/core/tests/packagemanagertests/Android.mk b/core/tests/packagemanagertests/Android.mk
index c1e8c98..5bfde78 100644
--- a/core/tests/packagemanagertests/Android.mk
+++ b/core/tests/packagemanagertests/Android.mk
@@ -10,7 +10,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
- frameworks-base-testutils
+ frameworks-base-testutils \
+ mockito-target-minus-junit4
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksCorePackageManagerTests
diff --git a/core/tests/privacytests/Android.mk b/core/tests/privacytests/Android.mk
new file mode 100644
index 0000000..7b11225
--- /dev/null
+++ b/core/tests/privacytests/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+# Include all test java files.
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := junit rappor-tests android-support-test
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_PACKAGE_NAME := FrameworksPrivacyLibraryTests
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/privacytests/AndroidManifest.xml b/core/tests/privacytests/AndroidManifest.xml
new file mode 100644
index 0000000..a0e5281
--- /dev/null
+++ b/core/tests/privacytests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.privacy">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.coretests.privacy"
+ android:label="Frameworks Privacy Library Tests" />
+
+</manifest>
diff --git a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
new file mode 100644
index 0000000..9166438
--- /dev/null
+++ b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
@@ -0,0 +1,392 @@
+/*
+ * 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.privacy;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.privacy.internal.longitudinalreporting.LongitudinalReportingConfig;
+import android.privacy.internal.longitudinalreporting.LongitudinalReportingEncoder;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+
+/**
+ * Unit test for the {@link LongitudinalReportingEncoder}.
+ *
+ * As {@link LongitudinalReportingEncoder} is based on Rappor,
+ * most cases are covered by Rappor tests already.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LongitudinalReportingEncoderTest {
+
+ @Test
+ public void testLongitudinalReportingEncoder_config() throws Exception {
+ final LongitudinalReportingConfig config = new LongitudinalReportingConfig(
+ "Foo", // encoderId
+ 0.4, // probabilityF
+ 0.25, // probabilityP
+ 1); // probabilityQ
+ final LongitudinalReportingEncoder encoder =
+ LongitudinalReportingEncoder.createInsecureEncoderForTest(
+ config);
+ assertEquals("LongitudinalReporting", encoder.getConfig().getAlgorithm());
+ assertEquals(
+ "EncoderId: Foo, ProbabilityF: 0.400, ProbabilityP: 0.250, ProbabilityQ: 1.000",
+ encoder.getConfig().toString());
+ }
+
+ @Test
+ public void testLongitudinalReportingEncoder_basicIRRTest() throws Exception {
+ // Test if IRR can generate expected result when seed is fixed (insecure encoder)
+ final LongitudinalReportingConfig config = new LongitudinalReportingConfig(
+ "Foo", // encoderId
+ 0.4, // probabilityF
+ 0, // probabilityP
+ 0); // probabilityQ
+ // Use insecure encoder here to make sure seed is set.
+ final LongitudinalReportingEncoder encoder =
+ LongitudinalReportingEncoder.createInsecureEncoderForTest(
+ config);
+ assertEquals(1, encoder.encodeBoolean(true)[0]);
+ assertEquals(0, encoder.encodeBoolean(true)[0]);
+ assertEquals(1, encoder.encodeBoolean(true)[0]);
+ assertEquals(1, encoder.encodeBoolean(true)[0]);
+ assertEquals(1, encoder.encodeBoolean(true)[0]);
+ assertEquals(1, encoder.encodeBoolean(true)[0]);
+ assertEquals(0, encoder.encodeBoolean(true)[0]);
+ assertEquals(1, encoder.encodeBoolean(true)[0]);
+ assertEquals(1, encoder.encodeBoolean(true)[0]);
+ assertEquals(1, encoder.encodeBoolean(true)[0]);
+
+ assertEquals(0, encoder.encodeBoolean(false)[0]);
+ assertEquals(1, encoder.encodeBoolean(false)[0]);
+ assertEquals(1, encoder.encodeBoolean(false)[0]);
+ assertEquals(0, encoder.encodeBoolean(false)[0]);
+ assertEquals(0, encoder.encodeBoolean(false)[0]);
+ assertEquals(0, encoder.encodeBoolean(false)[0]);
+ assertEquals(1, encoder.encodeBoolean(false)[0]);
+ assertEquals(0, encoder.encodeBoolean(false)[0]);
+ assertEquals(0, encoder.encodeBoolean(false)[0]);
+ assertEquals(1, encoder.encodeBoolean(false)[0]);
+
+ // Test if IRR returns original result when f = 0
+ final LongitudinalReportingConfig config2 = new LongitudinalReportingConfig(
+ "Foo", // encoderId
+ 0, // probabilityF
+ 0, // probabilityP
+ 0); // probabilityQ
+ final LongitudinalReportingEncoder encoder2
+ = LongitudinalReportingEncoder.createEncoder(
+ config2, makeTestingUserSecret("secret2"));
+ for (int i = 0; i < 10; i++) {
+ assertEquals(1, encoder2.encodeBoolean(true)[0]);
+ }
+ for (int i = 0; i < 10; i++) {
+ assertEquals(0, encoder2.encodeBoolean(false)[0]);
+ }
+
+ // Test if IRR returns opposite result when f = 1
+ final LongitudinalReportingConfig config3 = new LongitudinalReportingConfig(
+ "Foo", // encoderId
+ 1, // probabilityF
+ 0, // probabilityP
+ 0); // probabilityQ
+ final LongitudinalReportingEncoder encoder3
+ = LongitudinalReportingEncoder.createEncoder(
+ config3, makeTestingUserSecret("secret3"));
+ for (int i = 0; i < 10; i++) {
+ assertEquals(1, encoder3.encodeBoolean(false)[0]);
+ }
+ for (int i = 0; i < 10; i++) {
+ assertEquals(0, encoder3.encodeBoolean(true)[0]);
+ }
+ }
+
+ @Test
+ public void testLongitudinalReportingEncoder_basicPRRTest() throws Exception {
+ // Should always return original value when p = 0
+ for (int i = 0; i < 10; i++) {
+ for (int j = 0; j < 10; j++) {
+ final LongitudinalReportingConfig config1 = new LongitudinalReportingConfig(
+ "Foo" + i, // encoderId
+ 0, // probabilityF
+ 0, // probabilityP
+ 0); // probabilityQ
+ final LongitudinalReportingEncoder encoder1
+ = LongitudinalReportingEncoder.createEncoder(
+ config1, makeTestingUserSecret("encoder" + j));
+ assertEquals(0, encoder1.encodeBoolean(false)[0]);
+ assertEquals(0, encoder1.encodeBoolean(false)[0]);
+ assertEquals(0, encoder1.encodeBoolean(false)[0]);
+ assertEquals(0, encoder1.encodeBoolean(false)[0]);
+ assertEquals(0, encoder1.encodeBoolean(false)[0]);
+ assertEquals(1, encoder1.encodeBoolean(true)[0]);
+ assertEquals(1, encoder1.encodeBoolean(true)[0]);
+ assertEquals(1, encoder1.encodeBoolean(true)[0]);
+ assertEquals(1, encoder1.encodeBoolean(true)[0]);
+ assertEquals(1, encoder1.encodeBoolean(true)[0]);
+ }
+ }
+
+ // Should always return false when p = 1, q = 0
+ for (int i = 0; i < 10; i++) {
+ for (int j = 0; j < 10; j++) {
+ final LongitudinalReportingConfig config2 = new LongitudinalReportingConfig(
+ "Foo" + i, // encoderId
+ 0, // probabilityF
+ 1, // probabilityP
+ 0); // probabilityQ
+ final LongitudinalReportingEncoder encoder2
+ = LongitudinalReportingEncoder.createEncoder(
+ config2, makeTestingUserSecret("encoder" + j));
+ assertEquals(0, encoder2.encodeBoolean(false)[0]);
+ assertEquals(0, encoder2.encodeBoolean(false)[0]);
+ assertEquals(0, encoder2.encodeBoolean(false)[0]);
+ assertEquals(0, encoder2.encodeBoolean(false)[0]);
+ assertEquals(0, encoder2.encodeBoolean(false)[0]);
+ assertEquals(0, encoder2.encodeBoolean(true)[0]);
+ assertEquals(0, encoder2.encodeBoolean(true)[0]);
+ assertEquals(0, encoder2.encodeBoolean(true)[0]);
+ assertEquals(0, encoder2.encodeBoolean(true)[0]);
+ assertEquals(0, encoder2.encodeBoolean(true)[0]);
+ }
+ }
+
+ // Should always return true when p = 1, q = 1
+ for (int i = 0; i < 10; i++) {
+ for (int j = 0; j < 10; j++) {
+ final LongitudinalReportingConfig config3 = new LongitudinalReportingConfig(
+ "Foo" + i, // encoderId
+ 0, // probabilityF
+ 1, // probabilityP
+ 1); // probabilityQ
+ final LongitudinalReportingEncoder encoder3
+ = LongitudinalReportingEncoder.createEncoder(
+ config3, makeTestingUserSecret("encoder" + j));
+ assertEquals(1, encoder3.encodeBoolean(false)[0]);
+ assertEquals(1, encoder3.encodeBoolean(false)[0]);
+ assertEquals(1, encoder3.encodeBoolean(false)[0]);
+ assertEquals(1, encoder3.encodeBoolean(false)[0]);
+ assertEquals(1, encoder3.encodeBoolean(false)[0]);
+ assertEquals(1, encoder3.encodeBoolean(true)[0]);
+ assertEquals(1, encoder3.encodeBoolean(true)[0]);
+ assertEquals(1, encoder3.encodeBoolean(true)[0]);
+ assertEquals(1, encoder3.encodeBoolean(true)[0]);
+ assertEquals(1, encoder3.encodeBoolean(true)[0]);
+ }
+ }
+
+ // PRR should return different value when encoder id is changed
+ boolean hasFalseResult1 = false;
+ boolean hasTrueResult1 = false;
+ for (int i = 0; i < 50; i++) {
+ boolean firstResult = false;
+ for (int j = 0; j < 10; j++) {
+ final LongitudinalReportingConfig config4 = new LongitudinalReportingConfig(
+ "Foo" + i, // encoderId
+ 0, // probabilityF
+ 1, // probabilityP
+ 0.5); // probabilityQ
+ final LongitudinalReportingEncoder encoder4
+ = LongitudinalReportingEncoder.createEncoder(
+ config4, makeTestingUserSecret("encoder4"));
+ boolean encodedFalse = encoder4.encodeBoolean(false)[0] > 0;
+ boolean encodedTrue = encoder4.encodeBoolean(true)[0] > 0;
+ // PRR should always give the same value when all parameters are the same
+ assertEquals(encodedTrue, encodedFalse);
+ if (j == 0) {
+ firstResult = encodedTrue;
+ } else {
+ assertEquals(firstResult, encodedTrue);
+ }
+ if (encodedTrue) {
+ hasTrueResult1 = true;
+ } else {
+ hasFalseResult1 = true;
+ }
+ }
+ }
+ // Ensure it has both true and false results when encoder id is different
+ assertTrue(hasTrueResult1);
+ assertTrue(hasFalseResult1);
+
+ // PRR should give different value when secret is changed
+ boolean hasFalseResult2 = false;
+ boolean hasTrueResult2 = false;
+ for (int i = 0; i < 50; i++) {
+ boolean firstResult = false;
+ for (int j = 0; j < 10; j++) {
+ final LongitudinalReportingConfig config5 = new LongitudinalReportingConfig(
+ "Foo", // encoderId
+ 0, // probabilityF
+ 1, // probabilityP
+ 0.5); // probabilityQ
+ final LongitudinalReportingEncoder encoder5
+ = LongitudinalReportingEncoder.createEncoder(
+ config5, makeTestingUserSecret("encoder" + i));
+ boolean encodedFalse = encoder5.encodeBoolean(false)[0] > 0;
+ boolean encodedTrue = encoder5.encodeBoolean(true)[0] > 0;
+ // PRR should always give the same value when parameters are the same
+ assertEquals(encodedTrue, encodedFalse);
+ if (j == 0) {
+ firstResult = encodedTrue;
+ } else {
+ assertEquals(firstResult, encodedTrue);
+ }
+ if (encodedTrue) {
+ hasTrueResult2 = true;
+ } else {
+ hasFalseResult2 = true;
+ }
+ }
+ }
+ // Ensure it has both true and false results when encoder id is different
+ assertTrue(hasTrueResult2);
+ assertTrue(hasFalseResult2);
+
+ // Confirm if PRR randomizer is working correctly
+ final int n1 = 1000;
+ final double p1 = 0.8;
+ final double expectedTrueSum1 = n1 * p1;
+ final double valueRange1 = 5 * Math.sqrt(n1 * p1 * (1 - p1));
+ int trueSum1 = 0;
+ for (int i = 0; i < n1; i++) {
+ final LongitudinalReportingConfig config6 = new LongitudinalReportingConfig(
+ "Foo", // encoderId
+ 0, // probabilityF
+ p1, // probabilityP
+ 1); // probabilityQ
+ final LongitudinalReportingEncoder encoder6
+ = LongitudinalReportingEncoder.createEncoder(
+ config6, makeTestingUserSecret("encoder" + i));
+ boolean encodedFalse = encoder6.encodeBoolean(false)[0] > 0;
+ if (encodedFalse) {
+ trueSum1 += 1;
+ }
+ }
+ // Total number of true(s) should be around the mean (1000 * 0.8)
+ assertTrue(trueSum1 < expectedTrueSum1 + valueRange1);
+ assertTrue(trueSum1 > expectedTrueSum1 - valueRange1);
+
+ // Confirm if PRR randomizer is working correctly
+ final int n2 = 1000;
+ final double p2 = 0.2;
+ final double expectedTrueSum2 = n2 * p2;
+ final double valueRange2 = 5 * Math.sqrt(n2 * p2 * (1 - p2));
+ int trueSum2 = 0;
+ for (int i = 0; i < n2; i++) {
+ final LongitudinalReportingConfig config7 = new LongitudinalReportingConfig(
+ "Foo", // encoderId
+ 0, // probabilityF
+ p2, // probabilityP
+ 1); // probabilityQ
+ final LongitudinalReportingEncoder encoder7
+ = LongitudinalReportingEncoder.createEncoder(
+ config7, makeTestingUserSecret("encoder" + i));
+ boolean encodedFalse = encoder7.encodeBoolean(false)[0] > 0;
+ if (encodedFalse) {
+ trueSum2 += 1;
+ }
+ }
+ // Total number of true(s) should be around the mean (1000 * 0.2)
+ assertTrue(trueSum2 < expectedTrueSum2 + valueRange2);
+ assertTrue(trueSum2 > expectedTrueSum2 - valueRange2);
+ }
+
+ @Test
+ public void testLongitudinalReportingEncoder_basicIRRwithPRRTest() throws Exception {
+ // Verify PRR result will run IRR
+ boolean hasFalseResult1 = false;
+ boolean hasTrueResult1 = false;
+ for (int i = 0; i < 50; i++) {
+ final LongitudinalReportingConfig config1 = new LongitudinalReportingConfig(
+ "Foo", // encoderId
+ 0.5, // probabilityF
+ 1, // probabilityP
+ 1); // probabilityQ
+ final LongitudinalReportingEncoder encoder1
+ = LongitudinalReportingEncoder.createEncoder(
+ config1, makeTestingUserSecret("encoder1"));
+ if (encoder1.encodeBoolean(false)[0] > 0) {
+ hasTrueResult1 = true;
+ } else {
+ hasFalseResult1 = true;
+ }
+ }
+ assertTrue(hasTrueResult1);
+ assertTrue(hasFalseResult1);
+
+ // When secret is different, some device should use PRR result, some should use IRR result
+ boolean hasFalseResult2 = false;
+ boolean hasTrueResult2 = false;
+ for (int i = 0; i < 50; i++) {
+ final LongitudinalReportingConfig config2 = new LongitudinalReportingConfig(
+ "Foo", // encoderId
+ 1, // probabilityF
+ 0.5, // probabilityP
+ 1); // probabilityQ
+ final LongitudinalReportingEncoder encoder2
+ = LongitudinalReportingEncoder.createEncoder(
+ config2, makeTestingUserSecret("encoder" + i));
+ if (encoder2.encodeBoolean(false)[0] > 0) {
+ hasTrueResult2 = true;
+ } else {
+ hasFalseResult2 = true;
+ }
+ }
+ assertTrue(hasTrueResult2);
+ assertTrue(hasFalseResult2);
+ }
+
+ @Test
+ public void testLongTermRandomizedResult() throws Exception {
+ // Verify getLongTermRandomizedResult can return expected result when parameters are fixed.
+ final boolean[] expectedResult =
+ new boolean[]{true, false, true, true, true,
+ false, false, false, true, false,
+ false, false, false, true, true,
+ true, true, false, true, true,
+ true, true, false, true, true};
+ for (int i = 0; i < 5; i++) {
+ for (int j = 0; j < 5; j++) {
+ boolean result = LongitudinalReportingEncoder.getLongTermRandomizedResult(0.5,
+ true, makeTestingUserSecret("secret" + i), "encoder" + j);
+ assertEquals(expectedResult[i * 5 + j], result);
+ }
+ }
+ }
+
+ private static byte[] makeTestingUserSecret(String testingSecret) throws Exception {
+ // We generate the fake user secret by concatenating three copies of the
+ // 16 byte MD5 hash of the testingSecret string encoded in UTF 8.
+ MessageDigest md5 = MessageDigest.getInstance("MD5");
+ byte[] digest = md5.digest(testingSecret.getBytes(StandardCharsets.UTF_8));
+ assertEquals(16, digest.length);
+ return ByteBuffer.allocate(48).put(digest).put(digest).put(digest).array();
+ }
+}
diff --git a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
new file mode 100644
index 0000000..dad98b8
--- /dev/null
+++ b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
@@ -0,0 +1,204 @@
+/*
+ * 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.privacy;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.privacy.internal.rappor.RapporConfig;
+import android.privacy.internal.rappor.RapporEncoder;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+
+/**
+ * Unit test for the {@link RapporEncoder}.
+ * Most of the tests are done in external/rappor/client/javatest/ already.
+ * Tests here are just make sure the {@link RapporEncoder} wrap Rappor correctly.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class RapporEncoderTest {
+
+ @Test
+ public void testRapporEncoder_config() throws Exception {
+ final RapporConfig config = new RapporConfig(
+ "Foo", // encoderId
+ 8, // numBits,
+ 13.0 / 128.0, // probabilityF
+ 0.25, // probabilityP
+ 0.75, // probabilityQ
+ 1, // numCohorts
+ 2); // numBloomHashes)
+ final RapporEncoder encoder = RapporEncoder.createEncoder(config,
+ makeTestingUserSecret("encoder1"));
+ assertEquals("Rappor", encoder.getConfig().getAlgorithm());
+ assertEquals("EncoderId: Foo, NumBits: 8, ProbabilityF: 0.102, "
+ + "ProbabilityP: 0.250, ProbabilityQ: 0.750, NumCohorts: 1, "
+ + "NumBloomHashes: 2", encoder.getConfig().toString());
+ }
+
+ @Test
+ public void testRapporEncoder_basicIRRTest() throws Exception {
+ final RapporConfig config = new RapporConfig(
+ "Foo", // encoderId
+ 12, // numBits,
+ 0, // probabilityF
+ 0, // probabilityP
+ 1, // probabilityQ
+ 1, // numCohorts (so must be cohort 0)
+ 2); // numBloomHashes
+ // Use insecure encoder here as we want to get the exact output.
+ final RapporEncoder encoder = RapporEncoder.createInsecureEncoderForTest(config);
+ assertEquals(768, toLong(encoder.encodeString("Testing")));
+ }
+
+ @Test
+ public void testRapporEncoder_IRRWithPRR() throws Exception {
+ int numBits = 8;
+ final long inputValue = 254L;
+ final long prrValue = 250L;
+ final long prrAndIrrValue = 184L;
+
+ final RapporConfig config1 = new RapporConfig(
+ "Foo", // encoderId
+ numBits, // numBits,
+ 0.25, // probabilityF
+ 0, // probabilityP
+ 1, // probabilityQ
+ 1, // numCohorts
+ 2); // numBloomHashes
+ // Use insecure encoder here as we want to get the exact output.
+ final RapporEncoder encoder1 = RapporEncoder.createInsecureEncoderForTest(config1);
+ // Verify that PRR is working as expected.
+ assertEquals(prrValue, toLong(encoder1.encodeBits(toBytes(inputValue))));
+ assertTrue(encoder1.isInsecureEncoderForTest());
+
+ // Verify that IRR is working as expected.
+ final RapporConfig config2 = new RapporConfig(
+ "Foo", // encoderId
+ numBits, // numBits,
+ 0, // probabilityF
+ 0.3, // probabilityP
+ 0.7, // probabilityQ
+ 1, // numCohorts
+ 2); // numBloomHashes
+ // Use insecure encoder here as we want to get the exact output.
+ final RapporEncoder encoder2 = RapporEncoder.createInsecureEncoderForTest(config2);
+ assertEquals(prrAndIrrValue, toLong(encoder2.encodeBits(toBytes(prrValue))));
+
+ // Test that end-to-end is the result of PRR + IRR.
+ final RapporConfig config3 = new RapporConfig(
+ "Foo", // encoderId
+ numBits, // numBits,
+ 0.25, // probabilityF
+ 0.3, // probabilityP
+ 0.7, // probabilityQ
+ 1, // numCohorts
+ 2); // numBloomHashes
+ final RapporEncoder encoder3 = RapporEncoder.createInsecureEncoderForTest(config3);
+ // Verify that PRR is working as expected.
+ assertEquals(prrAndIrrValue, toLong(encoder3.encodeBits(toBytes(inputValue))));
+ }
+
+ @Test
+ public void testRapporEncoder_ensureSecureEncoderIsSecure() throws Exception {
+ int numBits = 8;
+ final long inputValue = 254L;
+ final long prrValue = 250L;
+ final long prrAndIrrValue = 184L;
+
+ final RapporConfig config1 = new RapporConfig(
+ "Foo", // encoderId
+ numBits, // numBits,
+ 0.25, // probabilityF
+ 0, // probabilityP
+ 1, // probabilityQ
+ 1, // numCohorts
+ 2); // numBloomHashes
+ final RapporEncoder encoder1 = RapporEncoder.createEncoder(config1,
+ makeTestingUserSecret("secret1"));
+ // Verify that PRR is working as expected, not affected by random seed.
+ assertEquals(prrValue, toLong(encoder1.encodeBits(toBytes(inputValue))));
+ assertFalse(encoder1.isInsecureEncoderForTest());
+
+ boolean hasDifferentResult2 = false;
+ for (int i = 0; i < 5; i++) {
+ final RapporConfig config2 = new RapporConfig(
+ "Foo", // encoderId
+ numBits, // numBits,
+ 0, // probabilityF
+ 0.3, // probabilityP
+ 0.7, // probabilityQ
+ 1, // numCohorts
+ 2); // numBloomHashes
+ final RapporEncoder encoder2 = RapporEncoder.createEncoder(config2,
+ makeTestingUserSecret("secret1"));
+ hasDifferentResult2 |= (prrAndIrrValue != toLong(
+ encoder2.encodeBits(toBytes(prrValue))));
+ }
+ // Ensure it's not getting same result as it has random seed while encoder id and secret
+ // is the same.
+ assertTrue(hasDifferentResult2);
+
+ boolean hasDifferentResults3 = false;
+ for (int i = 0; i < 5; i++) {
+ final RapporConfig config3 = new RapporConfig(
+ "Foo", // encoderId
+ numBits, // numBits,
+ 0.25, // probabilityF
+ 0.3, // probabilityP
+ 0.7, // probabilityQ
+ 1, // numCohorts
+ 2); // numBloomHashes
+ final RapporEncoder encoder3 = RapporEncoder.createEncoder(config3,
+ makeTestingUserSecret("secret1"));
+ hasDifferentResults3 |= (prrAndIrrValue != toLong(
+ encoder3.encodeBits(toBytes(inputValue))));
+ }
+ // Ensure it's not getting same result as it has random seed while encoder id and secret
+ // is the same.
+ assertTrue(hasDifferentResults3);
+ }
+
+ private static byte[] toBytes(long value) {
+ return ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(value).array();
+ }
+
+ private static long toLong(byte[] bytes) {
+ ByteBuffer buffer = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).put(bytes);
+ buffer.rewind();
+ return buffer.getLong();
+ }
+
+ private static byte[] makeTestingUserSecret(String testingSecret) throws Exception {
+ // We generate the fake user secret by concatenating three copies of the
+ // 16 byte MD5 hash of the testingSecret string encoded in UTF 8.
+ MessageDigest md5 = MessageDigest.getInstance("MD5");
+ byte[] digest = md5.digest(testingSecret.getBytes(StandardCharsets.UTF_8));
+ assertEquals(16, digest.length);
+ return ByteBuffer.allocate(48).put(digest).put(digest).put(digest).array();
+ }
+}
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
new file mode 100644
index 0000000..60416a7
--- /dev/null
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -0,0 +1,665 @@
+/*
+ * 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.graphics;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RawRes;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.NinePatchDrawable;
+
+import java.nio.ByteBuffer;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ArrayIndexOutOfBoundsException;
+import java.lang.NullPointerException;
+import java.lang.RuntimeException;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Class for decoding images as {@link Bitmap}s or {@link Drawable}s.
+ * @hide
+ */
+public final class ImageDecoder {
+ /**
+ * Source of the encoded image data.
+ */
+ public static abstract class Source {
+ /* @hide */
+ Resources getResources() { return null; }
+
+ /* @hide */
+ void close() {}
+
+ /* @hide */
+ abstract ImageDecoder createImageDecoder();
+ };
+
+ private static class ByteArraySource extends Source {
+ ByteArraySource(byte[] data, int offset, int length) {
+ mData = data;
+ mOffset = offset;
+ mLength = length;
+ };
+ private final byte[] mData;
+ private final int mOffset;
+ private final int mLength;
+
+ @Override
+ public ImageDecoder createImageDecoder() {
+ return nCreate(mData, mOffset, mLength);
+ }
+ }
+
+ private static class ByteBufferSource extends Source {
+ ByteBufferSource(ByteBuffer buffer) {
+ mBuffer = buffer;
+ }
+ private final ByteBuffer mBuffer;
+
+ @Override
+ public ImageDecoder createImageDecoder() {
+ if (!mBuffer.isDirect() && mBuffer.hasArray()) {
+ int offset = mBuffer.arrayOffset() + mBuffer.position();
+ int length = mBuffer.limit() - mBuffer.position();
+ return nCreate(mBuffer.array(), offset, length);
+ }
+ return nCreate(mBuffer, mBuffer.position(), mBuffer.limit());
+ }
+ }
+
+ private static class ResourceSource extends Source {
+ ResourceSource(Resources res, int resId)
+ throws Resources.NotFoundException {
+ // Test that the resource can be found.
+ InputStream is = null;
+ try {
+ is = res.openRawResource(resId);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ mResources = res;
+ mResId = resId;
+ }
+
+ final Resources mResources;
+ final int mResId;
+ // This is just stored here in order to keep the underlying Asset
+ // alive. FIXME: Can I access the Asset (and keep it alive) without
+ // this object?
+ InputStream mInputStream;
+
+ @Override
+ public Resources getResources() { return mResources; }
+
+ @Override
+ public ImageDecoder createImageDecoder() {
+ // FIXME: Can I bypass creating the stream?
+ try {
+ mInputStream = mResources.openRawResource(mResId);
+ } catch (Resources.NotFoundException e) {
+ // This should never happen, since we already tested in the
+ // constructor.
+ }
+ if (!(mInputStream instanceof AssetManager.AssetInputStream)) {
+ // This should never happen.
+ throw new RuntimeException("Resource is not an asset?");
+ }
+ long asset = ((AssetManager.AssetInputStream) mInputStream).getNativeAsset();
+ return nCreate(asset);
+ }
+
+ @Override
+ public void close() {
+ try {
+ mInputStream.close();
+ } catch (IOException e) {
+ } finally {
+ mInputStream = null;
+ }
+ }
+ }
+
+ /**
+ * Contains information about the encoded image.
+ */
+ public static class ImageInfo {
+ public final int width;
+ public final int height;
+ // TODO?: Add more info? mimetype, ninepatch etc?
+
+ ImageInfo(int width, int height) {
+ this.width = width;
+ this.height = height;
+ }
+ };
+
+ /**
+ * Used if the provided data is incomplete.
+ *
+ * There may be a partial image to display.
+ */
+ public class IncompleteException extends Exception {};
+
+ /**
+ * Used if the provided data is corrupt.
+ *
+ * There may be a partial image to display.
+ */
+ public class CorruptException extends Exception {};
+
+ /**
+ * Optional listener supplied to {@link #decodeDrawable} or
+ * {@link #decodeBitmap}.
+ */
+ public static interface OnHeaderDecodedListener {
+ /**
+ * Called when the header is decoded and the size is known.
+ *
+ * @param info Information about the encoded image.
+ * @param decoder allows changing the default settings of the decode.
+ */
+ public void onHeaderDecoded(ImageInfo info, ImageDecoder decoder);
+
+ };
+
+ /**
+ * Optional listener supplied to the ImageDecoder.
+ */
+ public static interface OnExceptionListener {
+ /**
+ * Called when there is a problem in the stream or in the data.
+ * FIXME: Or do not allow streams?
+ * FIXME: Report how much of the image has been decoded?
+ *
+ * @param e Exception containing information about the error.
+ * @return True to create and return a {@link Drawable}/
+ * {@link Bitmap} with partial data. False to return
+ * {@code null}. True is the default.
+ */
+ public boolean onException(Exception e);
+ };
+
+ // Fields
+ private long mNativePtr;
+ private final int mWidth;
+ private final int mHeight;
+
+ private int mDesiredWidth;
+ private int mDesiredHeight;
+ private int mAllocator = DEFAULT_ALLOCATOR;
+ private boolean mRequireUnpremultiplied = false;
+ private boolean mMutable = false;
+ private boolean mPreferRamOverQuality = false;
+ private boolean mAsAlphaMask = false;
+ private Rect mCropRect;
+
+ private PostProcess mPostProcess;
+ private OnExceptionListener mOnExceptionListener;
+
+
+ /**
+ * Private constructor called by JNI. {@link #recycle} must be
+ * called after decoding to delete native resources.
+ */
+ @SuppressWarnings("unused")
+ private ImageDecoder(long nativePtr, int width, int height) {
+ mNativePtr = nativePtr;
+ mWidth = width;
+ mHeight = height;
+ mDesiredWidth = width;
+ mDesiredHeight = height;
+ }
+
+ /**
+ * Create a new {@link Source} from an asset.
+ *
+ * @param res the {@link Resources} object containing the image data.
+ * @param resId resource ID of the image data.
+ * // FIXME: Can be an @DrawableRes?
+ * @return a new Source object, which can be passed to
+ * {@link #decodeDrawable} or {@link #decodeBitmap}.
+ * @throws Resources.NotFoundException if the asset does not exist.
+ */
+ public static Source createSource(@NonNull Resources res, @RawRes int resId)
+ throws Resources.NotFoundException {
+ return new ResourceSource(res, resId);
+ }
+
+ /**
+ * Create a new {@link Source} from a byte array.
+ * @param data byte array of compressed image data.
+ * @param offset offset into data for where the decoder should begin
+ * parsing.
+ * @param length number of bytes, beginning at offset, to parse.
+ * @throws NullPointerException if data is null.
+ * @throws ArrayIndexOutOfBoundsException if offset and length are
+ * not within data.
+ */
+ // TODO: Overloads that don't use offset, length
+ public static Source createSource(@NonNull byte[] data, int offset,
+ int length) throws ArrayIndexOutOfBoundsException {
+ if (data == null) {
+ throw new NullPointerException("null byte[] in createSource!");
+ }
+ if (offset < 0 || length < 0 || offset >= data.length ||
+ offset + length > data.length) {
+ throw new ArrayIndexOutOfBoundsException(
+ "invalid offset/length!");
+ }
+ return new ByteArraySource(data, offset, length);
+ }
+
+ /**
+ * Create a new {@link Source} from a {@link java.nio.ByteBuffer}.
+ *
+ * The returned {@link Source} effectively takes ownership of the
+ * {@link java.nio.ByteBuffer}; i.e. no other code should modify it after
+ * this call.
+ *
+ * Decoding will start from {@link java.nio.ByteBuffer#position()}.
+ */
+ public static Source createSource(ByteBuffer buffer) {
+ return new ByteBufferSource(buffer);
+ }
+
+ /**
+ * Return the width and height of a given sample size.
+ *
+ * This takes an input that functions like
+ * {@link BitmapFactory.Options#inSampleSize}. It returns a width and
+ * height that can be acheived by sampling the encoded image. Other widths
+ * and heights may be supported, but will require an additional (internal)
+ * scaling step. Such internal scaling is *not* supported with
+ * {@link #requireUnpremultiplied}.
+ *
+ * @param sampleSize Sampling rate of the encoded image.
+ * @return Point {@link Point#x} and {@link Point#y} correspond to the
+ * width and height after sampling.
+ */
+ public Point getSampledSize(int sampleSize) {
+ if (sampleSize <= 0) {
+ throw new IllegalArgumentException("sampleSize must be positive! "
+ + "provided " + sampleSize);
+ }
+ if (mNativePtr == 0) {
+ throw new IllegalStateException("ImageDecoder is recycled!");
+ }
+
+ return nGetSampledSize(mNativePtr, sampleSize);
+ }
+
+ // Modifiers
+ /**
+ * Resize the output to have the following size.
+ *
+ * @param width must be greater than 0.
+ * @param height must be greater than 0.
+ */
+ public void resize(int width, int height) {
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("Dimensions must be positive! "
+ + "provided (" + width + ", " + height + ")");
+ }
+
+ mDesiredWidth = width;
+ mDesiredHeight = height;
+ }
+
+ /**
+ * Resize based on a sample size.
+ *
+ * This has the same effect as passing the result of
+ * {@link #getSampledSize} to {@link #resize(int, int)}.
+ *
+ * @param sampleSize Sampling rate of the encoded image.
+ */
+ public void resize(int sampleSize) {
+ Point dimensions = this.getSampledSize(sampleSize);
+ this.resize(dimensions.x, dimensions.y);
+ }
+
+ // These need to stay in sync with ImageDecoder.cpp's Allocator enum.
+ /**
+ * Use the default allocation for the pixel memory.
+ *
+ * Will typically result in a {@link Bitmap.Config#HARDWARE}
+ * allocation, but may be software for small images. In addition, this will
+ * switch to software when HARDWARE is incompatible, e.g.
+ * {@link #setMutable}, {@link #setAsAlphaMask}.
+ */
+ public static final int DEFAULT_ALLOCATOR = 0;
+
+ /**
+ * Use a software allocation for the pixel memory.
+ *
+ * Useful for drawing to a software {@link Canvas} or for
+ * accessing the pixels on the final output.
+ */
+ public static final int SOFTWARE_ALLOCATOR = 1;
+
+ /**
+ * Use shared memory for the pixel memory.
+ *
+ * Useful for sharing across processes.
+ */
+ public static final int SHARED_MEMORY_ALLOCATOR = 2;
+
+ /**
+ * Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}.
+ *
+ * This will throw an {@link java.lang.IllegalStateException} when combined
+ * with incompatible options, like {@link #setMutable} or
+ * {@link #setAsAlphaMask}.
+ */
+ public static final int HARDWARE_ALLOCATOR = 3;
+
+ /** @hide **/
+ @Retention(SOURCE)
+ @IntDef({ DEFAULT_ALLOCATOR, SOFTWARE_ALLOCATOR, SHARED_MEMORY_ALLOCATOR,
+ HARDWARE_ALLOCATOR })
+ public @interface Allocator {};
+
+ /**
+ * Choose the backing for the pixel memory.
+ *
+ * This is ignored for animated drawables.
+ *
+ * TODO: Allow accessing the backing from the Bitmap.
+ *
+ * @param allocator Type of allocator to use.
+ */
+ public void setAllocator(@Allocator int allocator) {
+ if (allocator < DEFAULT_ALLOCATOR || allocator > HARDWARE_ALLOCATOR) {
+ throw new IllegalArgumentException("invalid allocator " + allocator);
+ }
+ mAllocator = allocator;
+ }
+
+ /**
+ * Create a {@link Bitmap} with unpremultiplied pixels.
+ *
+ * By default, ImageDecoder will create a {@link Bitmap} with
+ * premultiplied pixels, which is required for drawing with the
+ * {@link android.view.View} system (i.e. to a {@link Canvas}). Calling
+ * this method will result in {@link #decodeBitmap} returning a
+ * {@link Bitmap} with unpremultiplied pixels. See
+ * {@link Bitmap#isPremultiplied}. Incompatible with
+ * {@link #decodeDrawable}; attempting to decode an unpremultiplied
+ * {@link Drawable} will throw an {@link java.lang.IllegalStateException}.
+ */
+ public void requireUnpremultiplied() {
+ mRequireUnpremultiplied = true;
+ }
+
+ /**
+ * Modify the image after decoding and scaling.
+ *
+ * This allows adding effects prior to returning a {@link Drawable} or
+ * {@link Bitmap}. For a {@code Drawable} or an immutable {@code Bitmap},
+ * this is the only way to process the image after decoding.
+ *
+ * If set on a nine-patch image, the nine-patch data is ignored.
+ *
+ * For an animated image, the drawing commands drawn on the {@link Canvas}
+ * will be recorded immediately and then applied to each frame.
+ */
+ public void setPostProcess(PostProcess p) {
+ mPostProcess = p;
+ }
+
+ /**
+ * Set (replace) the {@link OnExceptionListener} on this object.
+ *
+ * Will be called if there is an error in the input. Without one, a
+ * partial {@link Bitmap} will be created.
+ */
+ public void setOnExceptionListener(OnExceptionListener l) {
+ mOnExceptionListener = l;
+ }
+
+ /**
+ * Crop the output to {@code subset} of the (possibly) scaled image.
+ *
+ * {@code subset} must be contained within the size set by {@link #resize}
+ * or the bounds of the image if resize was not called. Otherwise an
+ * {@link IllegalStateException} will be thrown.
+ *
+ * NOT intended as a replacement for
+ * {@link BitmapRegionDecoder#decodeRegion}. This supports all formats,
+ * but merely crops the output.
+ */
+ public void crop(Rect subset) {
+ mCropRect = subset;
+ }
+
+ /**
+ * Create a mutable {@link Bitmap}.
+ *
+ * By default, a {@link Bitmap} created will be immutable, but that can be
+ * changed with this call.
+ *
+ * Incompatible with {@link #HARDWARE_ALLOCATOR}, because
+ * {@link Bitmap.Config#HARDWARE} Bitmaps cannot be mutable. Attempting to
+ * combine them will throw an {@link java.lang.IllegalStateException}.
+ *
+ * Incompatible with {@link #decodeDrawable}, which would require
+ * retrieving the Bitmap from the returned Drawable in order to modify.
+ * Attempting to decode a mutable {@link Drawable} will throw an
+ * {@link java.lang.IllegalStateException}
+ */
+ public void setMutable() {
+ mMutable = true;
+ }
+
+ /**
+ * Potentially save RAM at the expense of quality.
+ *
+ * This may result in a {@link Bitmap} with a denser {@link Bitmap.Config},
+ * depending on the image. For example, for an opaque {@link Bitmap}, this
+ * may result in a {@link Bitmap.Config} with no alpha information.
+ */
+ public void setPreferRamOverQuality() {
+ mPreferRamOverQuality = true;
+ }
+
+ /**
+ * Potentially treat the output as an alpha mask.
+ *
+ * If the image is encoded in a format with only one channel, treat that
+ * channel as alpha. Otherwise this call has no effect.
+ *
+ * Incompatible with {@link #HARDWARE_ALLOCATOR}. Trying to combine them
+ * will throw an {@link java.lang.IllegalStateException}.
+ */
+ public void setAsAlphaMask() {
+ mAsAlphaMask = true;
+ }
+
+ /**
+ * Clean up resources.
+ *
+ * ImageDecoder has a private constructor, and will always be recycled
+ * by decodeDrawable or decodeBitmap which creates it, so there is no
+ * need for a finalizer.
+ */
+ private void recycle() {
+ if (mNativePtr == 0) {
+ return;
+ }
+ nRecycle(mNativePtr);
+ mNativePtr = 0;
+ }
+
+ private void checkState() {
+ if (mNativePtr == 0) {
+ throw new IllegalStateException("Cannot reuse ImageDecoder.Source!");
+ }
+
+ checkSubset(mDesiredWidth, mDesiredHeight, mCropRect);
+
+ if (mAllocator == HARDWARE_ALLOCATOR) {
+ if (mMutable) {
+ throw new IllegalStateException("Cannot make mutable HARDWARE Bitmap!");
+ }
+ if (mAsAlphaMask) {
+ throw new IllegalStateException("Cannot make HARDWARE Alpha mask Bitmap!");
+ }
+ }
+
+ if (mPostProcess != null && mRequireUnpremultiplied) {
+ throw new IllegalStateException("Cannot draw to unpremultiplied pixels!");
+ }
+ }
+
+ private static void checkSubset(int width, int height, Rect r) {
+ if (r == null) {
+ return;
+ }
+ if (r.left < 0 || r.top < 0 || r.right > width || r.bottom > height) {
+ throw new IllegalStateException("Subset " + r + " not contained by "
+ + "scaled image bounds: (" + width + " x " + height + ")");
+ }
+ }
+
+ /**
+ * Create a {@link Drawable}.
+ */
+ public static Drawable decodeDrawable(Source src, OnHeaderDecodedListener listener) {
+ ImageDecoder decoder = src.createImageDecoder();
+ if (decoder == null) {
+ return null;
+ }
+
+ if (listener != null) {
+ ImageInfo info = new ImageInfo(decoder.mWidth, decoder.mHeight);
+ listener.onHeaderDecoded(info, decoder);
+ }
+
+ decoder.checkState();
+
+ if (decoder.mRequireUnpremultiplied) {
+ // Though this could be supported (ignored) for opaque images, it
+ // seems better to always report this error.
+ throw new IllegalStateException("Cannot decode a Drawable with" +
+ " unpremultiplied pixels!");
+ }
+
+ if (decoder.mMutable) {
+ throw new IllegalStateException("Cannot decode a mutable Drawable!");
+ }
+
+ try {
+ Bitmap bm = nDecodeBitmap(decoder.mNativePtr,
+ decoder.mOnExceptionListener,
+ decoder.mPostProcess,
+ decoder.mDesiredWidth, decoder.mDesiredHeight,
+ decoder.mCropRect,
+ false, // decoder.mMutable
+ decoder.mAllocator,
+ false, // decoder.mRequireUnpremultiplied
+ decoder.mPreferRamOverQuality,
+ decoder.mAsAlphaMask
+ );
+ if (bm == null) {
+ return null;
+ }
+
+ Resources res = src.getResources();
+ if (res == null) {
+ bm.setDensity(Bitmap.DENSITY_NONE);
+ }
+
+ byte[] np = bm.getNinePatchChunk();
+ if (np != null && NinePatch.isNinePatchChunk(np)) {
+ Rect opticalInsets = new Rect();
+ bm.getOpticalInsets(opticalInsets);
+ Rect padding = new Rect();
+ nGetPadding(decoder.mNativePtr, padding);
+ return new NinePatchDrawable(res, bm, np, padding,
+ opticalInsets, null);
+ }
+
+ // TODO: Handle animation.
+ return new BitmapDrawable(res, bm);
+ } finally {
+ decoder.recycle();
+ src.close();
+ }
+ }
+
+ /**
+ * Create a {@link Bitmap}.
+ */
+ public static Bitmap decodeBitmap(Source src, OnHeaderDecodedListener listener) {
+ ImageDecoder decoder = src.createImageDecoder();
+ if (decoder == null) {
+ return null;
+ }
+
+ if (listener != null) {
+ ImageInfo info = new ImageInfo(decoder.mWidth, decoder.mHeight);
+ listener.onHeaderDecoded(info, decoder);
+ }
+
+ decoder.checkState();
+
+ try {
+ return nDecodeBitmap(decoder.mNativePtr,
+ decoder.mOnExceptionListener,
+ decoder.mPostProcess,
+ decoder.mDesiredWidth, decoder.mDesiredHeight,
+ decoder.mCropRect,
+ decoder.mMutable,
+ decoder.mAllocator,
+ decoder.mRequireUnpremultiplied,
+ decoder.mPreferRamOverQuality,
+ decoder.mAsAlphaMask);
+ } finally {
+ decoder.recycle();
+ src.close();
+ }
+ }
+
+ private static native ImageDecoder nCreate(long asset);
+ private static native ImageDecoder nCreate(ByteBuffer buffer,
+ int position,
+ int limit);
+ private static native ImageDecoder nCreate(byte[] data, int offset,
+ int length);
+ private static native Bitmap nDecodeBitmap(long nativePtr,
+ OnExceptionListener listener,
+ PostProcess postProcess,
+ int width, int height,
+ Rect cropRect, boolean mutable,
+ int allocator, boolean requireUnpremul,
+ boolean preferRamOverQuality, boolean asAlphaMask);
+ private static native Point nGetSampledSize(long nativePtr,
+ int sampleSize);
+ private static native void nGetPadding(long nativePtr, Rect outRect);
+ private static native void nRecycle(long nativePtr);
+}
diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java
index abcccbd..c6b6c66 100644
--- a/graphics/java/android/graphics/Point.java
+++ b/graphics/java/android/graphics/Point.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
@@ -121,6 +122,21 @@
out.writeInt(y);
}
+ /**
+ * Write to a protocol buffer output stream.
+ * Protocol buffer message definition at {@link android.graphics.PointProto}
+ *
+ * @param protoOutputStream Stream to write the Rect object to.
+ * @param fieldId Field Id of the Rect as defined in the parent message
+ * @hide
+ */
+ public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+ final long token = protoOutputStream.start(fieldId);
+ protoOutputStream.write(PointProto.X, x);
+ protoOutputStream.write(PointProto.Y, y);
+ protoOutputStream.end(token);
+ }
+
public static final Parcelable.Creator<Point> CREATOR = new Parcelable.Creator<Point>() {
/**
* Return a new point from the data in the specified parcel.
diff --git a/graphics/java/android/graphics/PostProcess.java b/graphics/java/android/graphics/PostProcess.java
new file mode 100644
index 0000000..c5a31e8
--- /dev/null
+++ b/graphics/java/android/graphics/PostProcess.java
@@ -0,0 +1,91 @@
+/*
+ * 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.graphics;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.graphics.drawable.Drawable;
+
+
+/**
+ * Helper interface for adding custom processing to an image.
+ *
+ * The image being processed may be a {@link Drawable}, {@link Bitmap} or frame
+ * of an animated image produced by {@link ImageDecoder}. This is called before
+ * the requested object is returned.
+ *
+ * This custom processing also applies to image types that are otherwise
+ * immutable, such as {@link Bitmap.Config#HARDWARE}.
+ *
+ * On an animated image, the callback will only be called once, but the drawing
+ * commands will be applied to each frame, as if the {@code Canvas} had been
+ * returned by {@link Picture#beginRecording}.
+ *
+ * Supplied to ImageDecoder via {@link ImageDecoder#setPostProcess}.
+ * @hide
+ */
+public interface PostProcess {
+ /**
+ * Do any processing after (for example) decoding.
+ *
+ * Drawing to the {@link Canvas} will behave as if the initial processing
+ * (e.g. decoding) already exists in the Canvas. An implementation can draw
+ * effects on top of this, or it can even draw behind it using
+ * {@link PorterDuff.Mode#DST_OVER}. A common effect is to add transparency
+ * to the corners to achieve rounded corners. That can be done with the
+ * following code:
+ *
+ * <code>
+ * Path path = new Path();
+ * path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
+ * path.addRoundRect(0, 0, width, height, 20, 20, Path.Direction.CW);
+ * Paint paint = new Paint();
+ * paint.setAntiAlias(true);
+ * paint.setColor(Color.TRANSPARENT);
+ * paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+ * canvas.drawPath(path, paint);
+ * return PixelFormat.TRANSLUCENT;
+ * </code>
+ *
+ *
+ * @param canvas The {@link Canvas} to draw to.
+ * @param width Width of {@code canvas}. Anything drawn outside of this
+ * will be ignored.
+ * @param height Height of {@code canvas}. Anything drawn outside of this
+ * will be ignored.
+ * @return Opacity of the result after drawing.
+ * {@link PixelFormat#UNKNOWN} means that the implementation did not
+ * change whether the image has alpha. Return this unless you added
+ * transparency (e.g. with the code above, in which case you should
+ * return {@code PixelFormat.TRANSLUCENT}) or you forced the image to
+ * be opaque (e.g. by drawing everywhere with an opaque color and
+ * {@code PorterDuff.Mode.DST_OVER}, in which case you should return
+ * {@code PixelFormat.OPAQUE}).
+ * {@link PixelFormat#TRANSLUCENT} means that the implementation added
+ * transparency. This is safe to return even if the image already had
+ * transparency. This is also safe to return if the result is opaque,
+ * though it may draw more slowly.
+ * {@link PixelFormat#OPAQUE} means that the implementation forced the
+ * image to be opaque. This is safe to return even if the image was
+ * already opaque.
+ * {@link PixelFormat#TRANSPARENT} (or any other integer) is not
+ * allowed, and will result in throwing an
+ * {@link java.lang.IllegalArgumentException}.
+ */
+ @PixelFormat.Opacity
+ public int postProcess(@NonNull Canvas canvas, int width, int height);
+}
diff --git a/graphics/java/android/graphics/drawable/RippleComponent.java b/graphics/java/android/graphics/drawable/RippleComponent.java
index 0e38826..626bcee 100644
--- a/graphics/java/android/graphics/drawable/RippleComponent.java
+++ b/graphics/java/android/graphics/drawable/RippleComponent.java
@@ -93,12 +93,8 @@
protected final void onHotspotBoundsChanged() {
if (!mHasMaxRadius) {
- final float halfWidth = mBounds.width() / 2.0f;
- final float halfHeight = mBounds.height() / 2.0f;
- final float targetRadius = (float) Math.sqrt(halfWidth * halfWidth
- + halfHeight * halfHeight);
-
- onTargetRadiusChanged(targetRadius);
+ mTargetRadius = getTargetRadius(mBounds);
+ onTargetRadiusChanged(mTargetRadius);
}
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 8b185f2..734cff5 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -299,6 +299,12 @@
onHotspotBoundsChanged();
}
+ final int count = mExitingRipplesCount;
+ final RippleForeground[] ripples = mExitingRipples;
+ for (int i = 0; i < count; i++) {
+ ripples[i].onBoundsChange();
+ }
+
if (mBackground != null) {
mBackground.onBoundsChange();
}
@@ -560,8 +566,7 @@
y = mHotspotBounds.exactCenterY();
}
- final boolean isBounded = isBounded();
- mRipple = new RippleForeground(this, mHotspotBounds, x, y, isBounded, mForceSoftware);
+ mRipple = new RippleForeground(this, mHotspotBounds, x, y, mForceSoftware);
}
mRipple.setup(mState.mMaxRadius, mDensity);
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index 0b5020c..ecbf578 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -30,6 +30,7 @@
import android.view.RenderNodeAnimator;
import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
+import android.view.animation.PathInterpolator;
import java.util.ArrayList;
@@ -38,18 +39,14 @@
*/
class RippleForeground extends RippleComponent {
private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
- private static final TimeInterpolator DECELERATE_INTERPOLATOR = new LogDecelerateInterpolator(
- 400f, 1.4f, 0);
+ // Matches R.interpolator.fast_out_slow_in but as we have no context we can't just import that
+ private static final TimeInterpolator DECELERATE_INTERPOLATOR =
+ new PathInterpolator(0.4f, 0f, 0.2f, 1f);
- // Pixel-based accelerations and velocities.
- private static final float WAVE_TOUCH_DOWN_ACCELERATION = 2048;
- private static final float WAVE_OPACITY_DECAY_VELOCITY = 3;
-
- // Bounded ripple animation properties.
- private static final int BOUNDED_ORIGIN_EXIT_DURATION = 300;
- private static final int BOUNDED_RADIUS_EXIT_DURATION = 800;
- private static final int BOUNDED_OPACITY_EXIT_DURATION = 400;
- private static final float MAX_BOUNDED_RADIUS = 350;
+ // Time it takes for the ripple to expand
+ private static final int RIPPLE_ENTER_DURATION = 225;
+ // Time it takes for the ripple to slide from the touch to the center point
+ private static final int RIPPLE_ORIGIN_DURATION = 225;
private static final int OPACITY_ENTER_DURATION = 75;
private static final int OPACITY_EXIT_DURATION = 150;
@@ -71,9 +68,6 @@
private float mTargetX = 0;
private float mTargetY = 0;
- /** Ripple target radius used when bounded. Not used for clamping. */
- private float mBoundedRadius = 0;
-
// Software rendering properties.
private float mOpacity = 0;
@@ -107,19 +101,13 @@
private float mStartRadius = 0;
public RippleForeground(RippleDrawable owner, Rect bounds, float startingX, float startingY,
- boolean isBounded, boolean forceSoftware) {
+ boolean forceSoftware) {
super(owner, bounds);
mForceSoftware = forceSoftware;
mStartingX = startingX;
mStartingY = startingY;
- if (isBounded) {
- mBoundedRadius = MAX_BOUNDED_RADIUS * 0.9f
- + (float) (MAX_BOUNDED_RADIUS * Math.random() * 0.1);
- } else {
- mBoundedRadius = 0;
- }
// 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;
}
@@ -127,6 +115,7 @@
@Override
protected void onTargetRadiusChanged(float targetRadius) {
clampStartingPosition();
+ switchToUiThreadAnimation();
}
private void drawSoftware(Canvas c, Paint p) {
@@ -228,16 +217,14 @@
}
mRunningSwAnimators.clear();
- final int duration = getRadiusDuration();
-
final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
- tweenRadius.setDuration(duration);
+ tweenRadius.setDuration(RIPPLE_ENTER_DURATION);
tweenRadius.setInterpolator(DECELERATE_INTERPOLATOR);
tweenRadius.start();
mRunningSwAnimators.add(tweenRadius);
final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1);
- tweenOrigin.setDuration(duration);
+ tweenOrigin.setDuration(RIPPLE_ORIGIN_DURATION);
tweenOrigin.setInterpolator(DECELERATE_INTERPOLATOR);
tweenOrigin.start();
mRunningSwAnimators.add(tweenOrigin);
@@ -267,20 +254,18 @@
final Paint paint = mOwner.getRipplePaint();
mPropPaint = CanvasProperty.createPaint(paint);
- final int radiusDuration = getRadiusDuration();
-
final RenderNodeAnimator radius = new RenderNodeAnimator(mPropRadius, mTargetRadius);
- radius.setDuration(radiusDuration);
+ radius.setDuration(RIPPLE_ORIGIN_DURATION);
radius.setInterpolator(DECELERATE_INTERPOLATOR);
mPendingHwAnimators.add(radius);
final RenderNodeAnimator x = new RenderNodeAnimator(mPropX, mTargetX);
- x.setDuration(radiusDuration);
+ x.setDuration(RIPPLE_ORIGIN_DURATION);
x.setInterpolator(DECELERATE_INTERPOLATOR);
mPendingHwAnimators.add(x);
final RenderNodeAnimator y = new RenderNodeAnimator(mPropY, mTargetY);
- y.setDuration(radiusDuration);
+ y.setDuration(RIPPLE_ORIGIN_DURATION);
y.setInterpolator(DECELERATE_INTERPOLATOR);
mPendingHwAnimators.add(y);
@@ -333,12 +318,6 @@
return MathUtils.lerp(mClampedStartingY - mBounds.exactCenterY(), mTargetY, mTweenY);
}
- private int getRadiusDuration() {
- final float remainingRadius = mTargetRadius - getCurrentRadius();
- return (int) (1000 * Math.sqrt(remainingRadius / WAVE_TOUCH_DOWN_ACCELERATION *
- mDensityScale) + 0.5);
- }
-
private float getCurrentRadius() {
return MathUtils.lerp(mStartRadius, mTargetRadius, mTweenRadius);
}
@@ -402,6 +381,14 @@
}
}
+ private void clearHwProps() {
+ mPropPaint = null;
+ mPropRadius = null;
+ mPropX = null;
+ mPropY = null;
+ mUsingProperties = false;
+ }
+
private final AnimatorListenerAdapter mAnimationListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animator) {
@@ -410,39 +397,20 @@
pruneSwFinished();
if (mRunningHwAnimators.isEmpty()) {
- mPropPaint = null;
- mPropRadius = null;
- mPropX = null;
- mPropY = null;
+ clearHwProps();
}
}
};
- /**
- * Interpolator with a smooth log deceleration.
- */
- private static final class LogDecelerateInterpolator implements TimeInterpolator {
- private final float mBase;
- private final float mDrift;
- private final float mTimeScale;
- private final float mOutputScale;
-
- public LogDecelerateInterpolator(float base, float timeScale, float drift) {
- mBase = base;
- mDrift = drift;
- mTimeScale = 1f / timeScale;
-
- mOutputScale = 1f / computeLog(1f);
+ private void switchToUiThreadAnimation() {
+ for (int i = 0; i < mRunningHwAnimators.size(); i++) {
+ Animator animator = mRunningHwAnimators.get(i);
+ animator.removeListener(mAnimationListener);
+ animator.end();
}
-
- private float computeLog(float t) {
- return 1f - (float) Math.pow(mBase, -t * mTimeScale) + (mDrift * t);
- }
-
- @Override
- public float getInterpolation(float t) {
- return computeLog(t) * mOutputScale;
- }
+ mRunningHwAnimators.clear();
+ clearHwProps();
+ invalidateSelf();
}
/**
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 6830a74..57db20b 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -60,10 +60,12 @@
/** Key prefix for user certificates. */
public static final String USER_CERTIFICATE = "USRCERT_";
- /** Key prefix for user private keys. */
+ /** Key prefix for user private and secret keys. */
public static final String USER_PRIVATE_KEY = "USRPKEY_";
- /** Key prefix for user secret keys. */
+ /** Key prefix for user secret keys.
+ * @deprecated use {@code USER_PRIVATE_KEY} for this category instead.
+ */
public static final String USER_SECRET_KEY = "USRSKEY_";
/** Key prefix for VPN. */
@@ -235,8 +237,7 @@
* Make sure every type is deleted. There can be all three types, so
* don't use a conditional here.
*/
- return deletePrivateKeyTypeForAlias(keystore, alias, uid)
- & deleteSecretKeyTypeForAlias(keystore, alias, uid)
+ return deleteUserKeyTypeForAlias(keystore, alias, uid)
& deleteCertificateTypesForAlias(keystore, alias, uid);
}
@@ -264,34 +265,27 @@
}
/**
- * Delete private key for a particular {@code alias}.
+ * Delete user key for a particular {@code alias}.
* Returns {@code true} if the entry no longer exists.
*/
- static boolean deletePrivateKeyTypeForAlias(KeyStore keystore, String alias) {
- return deletePrivateKeyTypeForAlias(keystore, alias, KeyStore.UID_SELF);
+ public static boolean deleteUserKeyTypeForAlias(KeyStore keystore, String alias) {
+ return deleteUserKeyTypeForAlias(keystore, alias, KeyStore.UID_SELF);
}
/**
- * Delete private key for a particular {@code alias}.
+ * Delete user key for a particular {@code alias}.
* Returns {@code true} if the entry no longer exists.
*/
- static boolean deletePrivateKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
- return keystore.delete(Credentials.USER_PRIVATE_KEY + alias, uid);
+ public static boolean deleteUserKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
+ return keystore.delete(Credentials.USER_PRIVATE_KEY + alias, uid) ||
+ keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
}
/**
- * Delete secret key for a particular {@code alias}.
+ * Delete legacy prefixed entry for a particular {@code alias}
* Returns {@code true} if the entry no longer exists.
*/
- public static boolean deleteSecretKeyTypeForAlias(KeyStore keystore, String alias) {
- return deleteSecretKeyTypeForAlias(keystore, alias, KeyStore.UID_SELF);
- }
-
- /**
- * Delete secret key for a particular {@code alias}.
- * Returns {@code true} if the entry no longer exists.
- */
- public static boolean deleteSecretKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
+ public static boolean deleteLegacyKeyForAlias(KeyStore keystore, String alias, int uid) {
return keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
}
}
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index b4331b2..7c7417d 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -16,6 +16,7 @@
package android.security;
import android.content.pm.StringParceledListSlice;
+import android.security.keymaster.KeymasterCertificateChain;
import android.security.keystore.ParcelableKeyGenParameterSpec;
/**
@@ -33,6 +34,8 @@
void setUserSelectable(String alias, boolean isUserSelectable);
boolean generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
+ boolean attestKey(in String alias, in byte[] challenge, out KeymasterCertificateChain chain);
+ boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain);
// APIs used by CertInstaller and DevicePolicyManager
String installCaCertificate(in byte[] caCertificate);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 988e32c..f1d1e16 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -305,7 +305,7 @@
KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
mRng, (mKeySizeBits + 7) / 8);
int flags = 0;
- String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias();
+ String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + spec.getKeystoreAlias();
KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
boolean success = false;
try {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index f36c00c..55e6519 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -196,7 +196,7 @@
}
@NonNull
- public static AndroidKeyStorePrivateKey getAndroidKeyStorePrivateKey(
+ private static AndroidKeyStorePrivateKey getAndroidKeyStorePrivateKey(
@NonNull AndroidKeyStorePublicKey publicKey) {
String keyAlgorithm = publicKey.getAlgorithm();
if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
@@ -212,17 +212,25 @@
}
@NonNull
- public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
+ private static KeyCharacteristics getKeyCharacteristics(@NonNull KeyStore keyStore,
+ @NonNull String alias, int uid)
throws UnrecoverableKeyException {
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
int errorCode = keyStore.getKeyCharacteristics(
- privateKeyAlias, null, null, uid, keyCharacteristics);
+ alias, null, null, uid, keyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
throw (UnrecoverableKeyException)
- new UnrecoverableKeyException("Failed to obtain information about private key")
- .initCause(KeyStore.getKeyStoreException(errorCode));
+ new UnrecoverableKeyException("Failed to obtain information about key")
+ .initCause(KeyStore.getKeyStoreException(errorCode));
}
+ return keyCharacteristics;
+ }
+
+ @NonNull
+ private static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
+ @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid,
+ KeyCharacteristics keyCharacteristics)
+ throws UnrecoverableKeyException {
ExportResult exportResult = keyStore.exportKey(
privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null, uid);
if (exportResult.resultCode != KeyStore.NO_ERROR) {
@@ -252,37 +260,56 @@
}
@NonNull
- public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
+ public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
@NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
throws UnrecoverableKeyException {
+ return loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid,
+ getKeyCharacteristics(keyStore, privateKeyAlias, uid));
+ }
+
+ @NonNull
+ private static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
+ @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid,
+ @NonNull KeyCharacteristics keyCharacteristics)
+ throws UnrecoverableKeyException {
AndroidKeyStorePublicKey publicKey =
- loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid);
+ loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid,
+ keyCharacteristics);
AndroidKeyStorePrivateKey privateKey =
AndroidKeyStoreProvider.getAndroidKeyStorePrivateKey(publicKey);
return new KeyPair(publicKey, privateKey);
}
@NonNull
- public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
+ public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
@NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
throws UnrecoverableKeyException {
- KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid);
+ return loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid,
+ getKeyCharacteristics(keyStore, privateKeyAlias, uid));
+ }
+
+ @NonNull
+ private static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
+ @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid,
+ @NonNull KeyCharacteristics keyCharacteristics)
+ throws UnrecoverableKeyException {
+ KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid,
+ keyCharacteristics);
return (AndroidKeyStorePrivateKey) keyPair.getPrivate();
}
@NonNull
- public static AndroidKeyStoreSecretKey loadAndroidKeyStoreSecretKeyFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String secretKeyAlias, int uid)
+ public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
+ @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
throws UnrecoverableKeyException {
- KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
- int errorCode = keyStore.getKeyCharacteristics(
- secretKeyAlias, null, null, uid, keyCharacteristics);
- if (errorCode != KeyStore.NO_ERROR) {
- throw (UnrecoverableKeyException)
- new UnrecoverableKeyException("Failed to obtain information about key")
- .initCause(KeyStore.getKeyStoreException(errorCode));
- }
+ return loadAndroidKeyStorePrivateKeyFromKeystore(keyStore, privateKeyAlias, uid,
+ getKeyCharacteristics(keyStore, privateKeyAlias, uid));
+ }
+ @NonNull
+ private static AndroidKeyStoreSecretKey loadAndroidKeyStoreSecretKeyFromKeystore(
+ @NonNull String secretKeyAlias, int uid, @NonNull KeyCharacteristics keyCharacteristics)
+ throws UnrecoverableKeyException {
Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM);
if (keymasterAlgorithm == null) {
throw new UnrecoverableKeyException("Key algorithm unknown");
@@ -310,6 +337,29 @@
return new AndroidKeyStoreSecretKey(secretKeyAlias, uid, keyAlgorithmString);
}
+ public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
+ @NonNull KeyStore keyStore, @NonNull String userKeyAlias, int uid)
+ throws UnrecoverableKeyException {
+ KeyCharacteristics keyCharacteristics = getKeyCharacteristics(keyStore, userKeyAlias, uid);
+
+ Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM);
+ if (keymasterAlgorithm == null) {
+ throw new UnrecoverableKeyException("Key algorithm unknown");
+ }
+
+ if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC ||
+ keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES) {
+ return loadAndroidKeyStoreSecretKeyFromKeystore(userKeyAlias, uid,
+ keyCharacteristics);
+ } else if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA ||
+ keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) {
+ return loadAndroidKeyStorePrivateKeyFromKeystore(keyStore, userKeyAlias, uid,
+ keyCharacteristics);
+ } else {
+ throw new UnrecoverableKeyException("Key algorithm unknown");
+ }
+ }
+
/**
* Returns an {@code AndroidKeyStore} {@link java.security.KeyStore}} of the specified UID.
* The {@code KeyStore} contains keys and certificates owned by that UID. Such cross-UID
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 0379863..fdb885db 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -64,7 +64,10 @@
AndroidKeyStoreKey keystoreKey = (AndroidKeyStoreKey) key;
String keyAliasInKeystore = keystoreKey.getAlias();
String entryAlias;
- if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
+ if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) {
+ entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length());
+ } else if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)){
+ // key has legacy prefix
entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
} else {
throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index bab4010..d73a9e2 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -89,18 +89,14 @@
@Override
public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
UnrecoverableKeyException {
- if (isPrivateKeyEntry(alias)) {
- String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
- return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
- mKeyStore, privateKeyAlias, mUid);
- } else if (isSecretKeyEntry(alias)) {
- String secretKeyAlias = Credentials.USER_SECRET_KEY + alias;
- return AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore(
- mKeyStore, secretKeyAlias, mUid);
- } else {
- // Key not found
- return null;
+ String userKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
+ if (!mKeyStore.contains(userKeyAlias, mUid)) {
+ // try legacy prefix for backward compatibility
+ userKeyAlias = Credentials.USER_SECRET_KEY + alias;
+ if (!mKeyStore.contains(userKeyAlias, mUid)) return null;
}
+ return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(mKeyStore, userKeyAlias,
+ mUid);
}
@Override
@@ -540,7 +536,7 @@
} else {
// Keep the stored private key around -- delete all other entry types
Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
- Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
+ Credentials.deleteLegacyKeyForAlias(mKeyStore, alias, mUid);
}
// Store the leaf certificate
@@ -565,7 +561,7 @@
Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
} else {
Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
- Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
+ Credentials.deleteLegacyKeyForAlias(mKeyStore, alias, mUid);
}
}
}
@@ -588,12 +584,17 @@
if (keyAliasInKeystore == null) {
throw new KeyStoreException("KeyStore-backed secret key does not have an alias");
}
- if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
- throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
- + keyAliasInKeystore);
+ String keyAliasPrefix = Credentials.USER_PRIVATE_KEY;
+ if (!keyAliasInKeystore.startsWith(keyAliasPrefix)) {
+ // try legacy prefix
+ keyAliasPrefix = Credentials.USER_SECRET_KEY;
+ if (!keyAliasInKeystore.startsWith(keyAliasPrefix)) {
+ throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
+ + keyAliasInKeystore);
+ }
}
String keyEntryAlias =
- keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
+ keyAliasInKeystore.substring(keyAliasPrefix.length());
if (!entryAlias.equals(keyEntryAlias)) {
throw new KeyStoreException("Can only replace KeyStore-backed keys with same"
+ " alias: " + entryAlias + " != " + keyEntryAlias);
@@ -728,7 +729,7 @@
}
Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias, mUid);
- String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
+ String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + entryAlias;
int errorCode = mKeyStore.importKey(
keyAliasInKeystore,
args,
@@ -827,24 +828,10 @@
}
private boolean isKeyEntry(String alias) {
- return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias);
+ return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid) ||
+ mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid);
}
- private boolean isPrivateKeyEntry(String alias) {
- if (alias == null) {
- throw new NullPointerException("alias == null");
- }
-
- return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid);
- }
-
- private boolean isSecretKeyEntry(String alias) {
- if (alias == null) {
- throw new NullPointerException("alias == null");
- }
-
- return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid);
- }
private boolean isCertificateEntry(String alias) {
if (alias == null) {
diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java
index cf4347d..0811100 100644
--- a/keystore/java/android/security/keystore/AttestationUtils.java
+++ b/keystore/java/android/security/keystore/AttestationUtils.java
@@ -73,6 +73,33 @@
public static final int ID_TYPE_MEID = 3;
/**
+ * Creates an array of X509Certificates from the provided KeymasterCertificateChain.
+ *
+ * @hide Only called by the DevicePolicyManager.
+ */
+ @NonNull public static X509Certificate[] parseCertificateChain(
+ final KeymasterCertificateChain kmChain) throws
+ KeyAttestationException {
+ // Extract certificate chain.
+ final Collection<byte[]> rawChain = kmChain.getCertificates();
+ if (rawChain.size() < 2) {
+ throw new KeyAttestationException("Attestation certificate chain contained "
+ + rawChain.size() + " entries. At least two are required.");
+ }
+ final ByteArrayOutputStream concatenatedRawChain = new ByteArrayOutputStream();
+ try {
+ for (final byte[] cert : rawChain) {
+ concatenatedRawChain.write(cert);
+ }
+ return CertificateFactory.getInstance("X.509").generateCertificates(
+ new ByteArrayInputStream(concatenatedRawChain.toByteArray()))
+ .toArray(new X509Certificate[0]);
+ } catch (Exception e) {
+ throw new KeyAttestationException("Unable to construct certificate chain", e);
+ }
+ }
+
+ /**
* Performs attestation of the device's identifiers. This method returns a certificate chain
* whose first element contains the requested device identifiers in an extension. The device's
* manufacturer, model, brand, device and product are always also included in the attestation.
@@ -173,22 +200,18 @@
KeyStore.getKeyStoreException(errorCode));
}
- // Extract certificate chain.
- final Collection<byte[]> rawChain = outChain.getCertificates();
- if (rawChain.size() < 2) {
- throw new DeviceIdAttestationException("Attestation certificate chain contained "
- + rawChain.size() + " entries. At least two are required.");
- }
- final ByteArrayOutputStream concatenatedRawChain = new ByteArrayOutputStream();
try {
- for (final byte[] cert : rawChain) {
- concatenatedRawChain.write(cert);
- }
- return CertificateFactory.getInstance("X.509").generateCertificates(
- new ByteArrayInputStream(concatenatedRawChain.toByteArray()))
- .toArray(new X509Certificate[0]);
- } catch (Exception e) {
- throw new DeviceIdAttestationException("Unable to construct certificate chain", e);
+ return parseCertificateChain(outChain);
+ } catch (KeyAttestationException e) {
+ throw new DeviceIdAttestationException(e.getMessage(), e);
}
}
+
+ /**
+ * Returns true if the attestation chain provided is a valid key attestation chain.
+ * @hide
+ */
+ public static boolean isChainValid(KeymasterCertificateChain chain) {
+ return chain != null && chain.getCertificates().size() >= 2;
+ }
}
diff --git a/keystore/java/android/security/keystore/KeyAttestationException.java b/keystore/java/android/security/keystore/KeyAttestationException.java
new file mode 100644
index 0000000..6cf5fb2
--- /dev/null
+++ b/keystore/java/android/security/keystore/KeyAttestationException.java
@@ -0,0 +1,46 @@
+/*
+ * 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.security.keystore;
+
+/**
+ * Thrown when {@link AttestationUtils} is unable to attest the given key or handle
+ * the resulting attestation record.
+ *
+ * @hide
+ */
+public class KeyAttestationException extends Exception {
+ /**
+ * Constructs a new {@code KeyAttestationException} with the current stack trace and the
+ * specified detail message.
+ *
+ * @param detailMessage the detail message for this exception.
+ */
+ public KeyAttestationException(String detailMessage) {
+ super(detailMessage);
+ }
+
+ /**
+ * Constructs a new {@code KeyAttestationException} with the current stack trace, the
+ * specified detail message and the specified cause.
+ *
+ * @param message the detail message for this exception.
+ * @param cause the cause of this exception, may be {@code null}.
+ */
+ public KeyAttestationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 87677d4..1238d87 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -680,6 +680,40 @@
}
/**
+ * A Builder constructor taking in an already-built KeyGenParameterSpec, useful for
+ * changing values of the KeyGenParameterSpec quickly.
+ * @hide Should be used internally only.
+ */
+ public Builder(@NonNull KeyGenParameterSpec sourceSpec) {
+ this(sourceSpec.getKeystoreAlias(), sourceSpec.getPurposes());
+ mUid = sourceSpec.getUid();
+ mKeySize = sourceSpec.getKeySize();
+ mSpec = sourceSpec.getAlgorithmParameterSpec();
+ mCertificateSubject = sourceSpec.getCertificateSubject();
+ mCertificateSerialNumber = sourceSpec.getCertificateSerialNumber();
+ mCertificateNotBefore = sourceSpec.getCertificateNotBefore();
+ mCertificateNotAfter = sourceSpec.getCertificateNotAfter();
+ mKeyValidityStart = sourceSpec.getKeyValidityStart();
+ mKeyValidityForOriginationEnd = sourceSpec.getKeyValidityForOriginationEnd();
+ mKeyValidityForConsumptionEnd = sourceSpec.getKeyValidityForConsumptionEnd();
+ mPurposes = sourceSpec.getPurposes();
+ if (sourceSpec.isDigestsSpecified()) {
+ mDigests = sourceSpec.getDigests();
+ }
+ mEncryptionPaddings = sourceSpec.getEncryptionPaddings();
+ mSignaturePaddings = sourceSpec.getSignaturePaddings();
+ mBlockModes = sourceSpec.getBlockModes();
+ mRandomizedEncryptionRequired = sourceSpec.isRandomizedEncryptionRequired();
+ mUserAuthenticationRequired = sourceSpec.isUserAuthenticationRequired();
+ mUserAuthenticationValidityDurationSeconds =
+ sourceSpec.getUserAuthenticationValidityDurationSeconds();
+ mAttestationChallenge = sourceSpec.getAttestationChallenge();
+ mUniqueIdIncluded = sourceSpec.isUniqueIdIncluded();
+ mUserAuthenticationValidWhileOnBody = sourceSpec.isUserAuthenticationValidWhileOnBody();
+ mInvalidatedByBiometricEnrollment = sourceSpec.isInvalidatedByBiometricEnrollment();
+ }
+
+ /**
* Sets the UID which will own the key.
*
* @param uid UID or {@code -1} for the UID of the current process.
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
index 73b489f..254b6be 100644
--- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -55,7 +55,7 @@
static final int USER_AUTHENTICATION_DURATION = 300;
static final byte[] ATTESTATION_CHALLENGE = new byte[] {'c', 'h'};
- KeyGenParameterSpec configureDefaultSpec() {
+ public static KeyGenParameterSpec configureDefaultSpec() {
return new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
.setUid(UID)
.setKeySize(KEYSIZE)
@@ -80,7 +80,7 @@
.build();
}
- void validateSpecValues(KeyGenParameterSpec spec, int uid, String alias) {
+ public static void validateSpecValues(KeyGenParameterSpec spec, int uid, String alias) {
assertThat(spec.getKeystoreAlias(), is(alias));
assertThat(spec.getPurposes(), is(KEY_PURPOSES));
assertThat(spec.getUid(), is(uid));
diff --git a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
new file mode 100644
index 0000000..865cad4
--- /dev/null
+++ b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.security.keystore;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.security.ParcelableKeyGenParameterSpecTest;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link KeyGenParameterSpec}. */
+@RunWith(AndroidJUnit4.class)
+public final class KeyGenParameterSpecTest {
+ static final String ALIAS = "keystore-alias";
+ static final int KEY_PURPOSES = KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY;
+
+ @Test
+ public void testBuilderCopyingValues() {
+ KeyGenParameterSpec spec = ParcelableKeyGenParameterSpecTest.configureDefaultSpec();
+ KeyGenParameterSpec copiedSpec =
+ new KeyGenParameterSpec.Builder(spec).build();
+ ParcelableKeyGenParameterSpecTest.validateSpecValues(
+ copiedSpec, spec.getUid(), spec.getKeystoreAlias());
+ }
+
+ @Test
+ public void testBuilderCopyingEmptyValues() {
+ KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES).build();
+ KeyGenParameterSpec copiedSpec = new KeyGenParameterSpec.Builder(spec).build();
+
+ assertThat(copiedSpec.getKeystoreAlias(), is(ALIAS));
+ assertThat(copiedSpec.getPurposes(), is(KEY_PURPOSES));
+ }
+
+ @Test
+ public void testCanModifyValuesInCopiedBuilder() {
+ KeyGenParameterSpec spec = ParcelableKeyGenParameterSpecTest.configureDefaultSpec();
+ KeyGenParameterSpec copiedSpec =
+ new KeyGenParameterSpec.Builder(spec)
+ .setAttestationChallenge(null)
+ .build();
+
+ assertEquals(copiedSpec.getAttestationChallenge(), null);
+ }
+}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 3fe75cf..5b95c81 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -6880,6 +6880,9 @@
return UNKNOWN_ERROR;
}
+ // The number of resources overlaid that were not explicitly marked overlayable.
+ size_t forcedOverlayCount = 0u;
+
KeyedVector<uint8_t, IdmapTypeMap> map;
// overlaid packages are assumed to contain only one package group
@@ -6919,6 +6922,7 @@
continue;
}
+ uint32_t typeSpecFlags = 0u;
const String16 overlayType(resName.type, resName.typeLen);
const String16 overlayName(resName.name, resName.nameLen);
uint32_t overlayResID = overlay.identifierForName(overlayName.string(),
@@ -6926,14 +6930,23 @@
overlayType.string(),
overlayType.size(),
overlayPackage.string(),
- overlayPackage.size());
+ overlayPackage.size(),
+ &typeSpecFlags);
if (overlayResID == 0) {
+ // No such target resource was found.
if (typeMap.entryMap.isEmpty()) {
typeMap.entryOffset++;
}
continue;
}
+ // Now that we know this is being overlaid, check if it can be, and emit a warning if
+ // it can't.
+ if ((dtohl(typeConfigs->typeSpecFlags[entryIndex]) &
+ ResTable_typeSpec::SPEC_OVERLAYABLE) == 0) {
+ forcedOverlayCount++;
+ }
+
if (typeMap.overlayTypeId == -1) {
typeMap.overlayTypeId = Res_GETTYPE(overlayResID) + 1;
}
@@ -7012,6 +7025,10 @@
typeData += entryCount * 2;
}
+ if (forcedOverlayCount > 0) {
+ ALOGW("idmap: overlaid %zu resources not marked overlayable", forcedOverlayCount);
+ }
+
return NO_ERROR;
}
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 20d0178..8cf4de9 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1339,9 +1339,13 @@
// Number of uint32_t entry configuration masks that follow.
uint32_t entryCount;
- enum {
+ enum : uint32_t {
// Additional flag indicating an entry is public.
- SPEC_PUBLIC = 0x40000000
+ SPEC_PUBLIC = 0x40000000u,
+
+ // Additional flag indicating an entry is overlayable at runtime.
+ // Added in Android-P.
+ SPEC_OVERLAYABLE = 0x80000000u,
};
};
diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk
index 0c17328..18ef75e 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/basic_de_fr.apk b/libs/androidfw/tests/data/basic/basic_de_fr.apk
index e45258c..767dff6 100644
--- a/libs/androidfw/tests/data/basic/basic_de_fr.apk
+++ b/libs/androidfw/tests/data/basic/basic_de_fr.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk
index 4ae1a7c..58953f5 100644
--- a/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk
+++ b/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk
index a240d4c..103f656 100644
--- a/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk
+++ b/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk
index fd3d9b2..61369d5 100644
--- a/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk
+++ b/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/build b/libs/androidfw/tests/data/basic/build
index d619800..5682ed4 100755
--- a/libs/androidfw/tests/data/basic/build
+++ b/libs/androidfw/tests/data/basic/build
@@ -19,11 +19,15 @@
PATH_TO_FRAMEWORK_RES=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/android.jar
-aapt package \
- -M AndroidManifest.xml \
- -S res \
- -A assets \
+aapt2 compile --dir res -o compiled.flata
+aapt2 link \
-I $PATH_TO_FRAMEWORK_RES \
- --split hdpi --split xhdpi --split xxhdpi --split fr,de \
- -F basic.apk \
- -f
+ --manifest AndroidManifest.xml \
+ -A assets \
+ --split basic_hdpi-v4.apk:hdpi \
+ --split basic_xhdpi-v4.apk:xhdpi \
+ --split basic_xxhdpi-v4.apk:xxhdpi \
+ --split basic_de_fr.apk:de,fr \
+ -o basic.apk \
+ compiled.flata
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml
index 638c983..6c47459 100644
--- a/libs/androidfw/tests/data/basic/res/values/values.xml
+++ b/libs/androidfw/tests/data/basic/res/values/values.xml
@@ -60,4 +60,9 @@
<item>2</item>
<item>3</item>
</integer-array>
+
+ <overlayable>
+ <item type="string" name="test2" />
+ <item type="array" name="integerArray1" />
+ </overlayable>
</resources>
diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build
index 112f373..716b1bd 100755
--- a/libs/androidfw/tests/data/overlay/build
+++ b/libs/androidfw/tests/data/overlay/build
@@ -17,4 +17,6 @@
set -e
-aapt package -M AndroidManifest.xml -S res -F overlay.apk -f
+aapt2 compile --dir res -o compiled.flata
+aapt2 link --manifest AndroidManifest.xml -o overlay.apk compiled.flata
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk
index 40bf17c..33f9611 100644
--- a/libs/androidfw/tests/data/overlay/overlay.apk
+++ b/libs/androidfw/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index afdd339..f41956c 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -27,9 +27,11 @@
#include <cutils/ashmem.h>
#include <log/log.h>
+#include <sstream>
#include "Properties.h"
#include "utils/TimeUtils.h"
+#include "utils/Trace.h"
namespace android {
namespace uirenderer {
@@ -150,6 +152,19 @@
(*mGlobalData)->reportJankType((JankType)i);
}
}
+
+ // Log daveys since they are weird and we don't know what they are (b/70339576)
+ if (totalDuration >= 700_ms) {
+ static int sDaveyCount = 0;
+ std::stringstream ss;
+ ss << "Davey! duration=" << ns2ms(totalDuration) << "ms; ";
+ for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
+ ss << FrameInfoNames[i] << "=" << frame[i] << ", ";
+ }
+ 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);
+ }
}
void JankTracker::dumpData(int fd, const ProfileDataDescription* description,
diff --git a/libs/hwui/ProfileDataContainer.cpp b/libs/hwui/ProfileDataContainer.cpp
index 8e0b4e2..38e0f0a 100644
--- a/libs/hwui/ProfileDataContainer.cpp
+++ b/libs/hwui/ProfileDataContainer.cpp
@@ -21,6 +21,7 @@
#include <cutils/ashmem.h>
#include <log/log.h>
+#include <errno.h>
#include <sys/mman.h>
namespace android {
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 4ba368f..9db39d9 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -22,8 +22,6 @@
#include <SkOverdrawColorFilter.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>
-#include <SkPixelSerializer.h>
-#include <SkStream.h>
#include "VectorDrawable.h"
#include "utils/TraceUtils.h"
@@ -207,19 +205,6 @@
}
}
-// Encodes to PNG, unless there is already encoded data, in which case that gets
-// used.
-class PngPixelSerializer : public SkPixelSerializer {
-public:
- bool onUseEncodedData(const void*, size_t) override { return true; }
- SkData* onEncode(const SkPixmap& pixmap) override {
- SkDynamicMemoryWStream buf;
- return SkEncodeImage(&buf, pixmap, SkEncodedImageFormat::kPNG, 100)
- ? buf.detachAsData().release()
- : nullptr;
- }
-};
-
void SkiaPipeline::renderVectorDrawableCache() {
if (!mVectorDrawables.empty()) {
sp<VectorDrawableAtlas> atlas = mRenderThread.cacheManager().acquireVectorDrawableAtlas();
@@ -296,9 +281,7 @@
sk_sp<SkPicture> picture = mRecorder->finishRecordingAsPicture();
surface->getCanvas()->drawPicture(picture);
if (picture->approximateOpCount() > 0) {
- SkDynamicMemoryWStream stream;
- PngPixelSerializer serializer;
- picture->serialize(&stream, &serializer);
+ auto data = picture->serialize();
// offload saving to file in a different thread
if (!mSavePictureProcessor.get()) {
@@ -307,10 +290,10 @@
taskManager->canRunTasks() ? taskManager : nullptr);
}
if (1 == mCaptureSequence) {
- mSavePictureProcessor->savePicture(stream.detachAsData(), mCapturedFile);
+ mSavePictureProcessor->savePicture(data, mCapturedFile);
} else {
mSavePictureProcessor->savePicture(
- stream.detachAsData(),
+ data,
mCapturedFile + "_" + std::to_string(mCaptureSequence));
}
mCaptureSequence--;
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 848c6a8..5b87e10 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -239,9 +239,9 @@
if (!eglChooseConfig(mEglDisplay, attribs16F, &mEglConfigWideGamut, numConfigs,
&numConfigs) ||
numConfigs != 1) {
- LOG_ALWAYS_FATAL(
- "Device claims wide gamut support, cannot find matching config, error = %s",
+ ALOGE("Device claims wide gamut support, cannot find matching config, error = %s",
eglErrorString());
+ EglExtensions.pixelFormatFloat = false;
}
}
}
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index 3ac1a45..e0303a8 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -42,7 +42,7 @@
constexpr int sHistogramSize = ProfileData::HistogramSize();
static bool mergeProfileDataIntoProto(service::GraphicsStatsProto* proto,
- const std::string& package, int versionCode,
+ const std::string& package, int64_t versionCode,
int64_t startTime, int64_t endTime, const ProfileData* data);
static void dumpAsTextToFd(service::GraphicsStatsProto* proto, int outFd);
@@ -154,7 +154,7 @@
}
bool mergeProfileDataIntoProto(service::GraphicsStatsProto* proto, const std::string& package,
- int versionCode, int64_t startTime, int64_t endTime,
+ int64_t versionCode, int64_t startTime, int64_t endTime,
const ProfileData* data) {
if (proto->stats_start() == 0 || proto->stats_start() > startTime) {
proto->set_stats_start(startTime);
@@ -230,7 +230,7 @@
return;
}
dprintf(fd, "\nPackage: %s", proto->package_name().c_str());
- dprintf(fd, "\nVersion: %d", proto->version_code());
+ dprintf(fd, "\nVersion: %lld", proto->version_code());
dprintf(fd, "\nStats since: %lldns", proto->stats_start());
dprintf(fd, "\nStats end: %lldns", proto->stats_end());
auto summary = proto->summary();
@@ -254,7 +254,7 @@
}
void GraphicsStatsService::saveBuffer(const std::string& path, const std::string& package,
- int versionCode, int64_t startTime, int64_t endTime,
+ int64_t versionCode, int64_t startTime, int64_t endTime,
const ProfileData* data) {
service::GraphicsStatsProto statsProto;
if (!parseFromFile(path, &statsProto)) {
@@ -320,8 +320,8 @@
}
void GraphicsStatsService::addToDump(Dump* dump, const std::string& path,
- const std::string& package, int versionCode, int64_t startTime,
- int64_t endTime, const ProfileData* data) {
+ const std::string& package, int64_t versionCode,
+ int64_t startTime, int64_t endTime, const ProfileData* data) {
service::GraphicsStatsProto statsProto;
if (!path.empty() && !parseFromFile(path, &statsProto)) {
statsProto.Clear();
diff --git a/libs/hwui/service/GraphicsStatsService.h b/libs/hwui/service/GraphicsStatsService.h
index 7ddc219..bce0f3d 100644
--- a/libs/hwui/service/GraphicsStatsService.h
+++ b/libs/hwui/service/GraphicsStatsService.h
@@ -44,12 +44,12 @@
};
ANDROID_API static void saveBuffer(const std::string& path, const std::string& package,
- int versionCode, int64_t startTime, int64_t endTime,
+ int64_t versionCode, int64_t startTime, int64_t endTime,
const ProfileData* data);
ANDROID_API static Dump* createDump(int outFd, DumpType type);
ANDROID_API static void addToDump(Dump* dump, const std::string& path,
- const std::string& package, int versionCode,
+ const std::string& package, int64_t versionCode,
int64_t startTime, int64_t endTime, const ProfileData* data);
ANDROID_API static void addToDump(Dump* dump, const std::string& path);
ANDROID_API static void finishDump(Dump* dump);
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index 8c0ca5c..0a9a74e 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -21,7 +21,6 @@
#include "hwui/Typeface.h"
#include "protos/hwui.pb.h"
-#include <../src/sysinfo.h>
#include <benchmark/benchmark.h>
#include <getopt.h>
#include <pthread.h>
@@ -345,9 +344,6 @@
name_field_width += 5;
benchmark::BenchmarkReporter::Context context;
- context.num_cpus = benchmark::NumCPUs();
- context.mhz_per_cpu = benchmark::CyclesPerSecond() / 1000000.0f;
- context.cpu_scaling_enabled = benchmark::CpuScalingEnabled();
context.name_field_width = name_field_width;
gBenchmarkReporter->ReportContext(context);
}
diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk
index 439e86d..5f3e407 100644
--- a/libs/incident/Android.mk
+++ b/libs/incident/Android.mk
@@ -31,11 +31,11 @@
LOCAL_SRC_FILES := \
../../core/java/android/os/IIncidentManager.aidl \
- ../../core/java/android/os/IIncidentReportCompletedListener.aidl \
../../core/java/android/os/IIncidentReportStatusListener.aidl \
src/IncidentReportArgs.cpp
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_PROTO_OPTIMIZE_TYPE := lite
include $(BUILD_SHARED_LIBRARY)
diff --git a/location/java/android/location/GnssMeasurementsEvent.java b/location/java/android/location/GnssMeasurementsEvent.java
index d66fd9c..072a7fe 100644
--- a/location/java/android/location/GnssMeasurementsEvent.java
+++ b/location/java/android/location/GnssMeasurementsEvent.java
@@ -49,7 +49,7 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({STATUS_NOT_SUPPORTED, STATUS_READY, STATUS_LOCATION_DISABLED})
+ @IntDef({STATUS_NOT_SUPPORTED, STATUS_READY, STATUS_LOCATION_DISABLED, STATUS_NOT_ALLOWED})
public @interface GnssMeasurementsStatus {}
/**
@@ -72,6 +72,12 @@
public static final int STATUS_LOCATION_DISABLED = 2;
/**
+ * The client is not allowed to register for GNSS Measurements in general or in the
+ * requested mode.
+ */
+ public static final int STATUS_NOT_ALLOWED = 3;
+
+ /**
* Reports the latest collected GNSS Measurements.
*/
public void onGnssMeasurementsReceived(GnssMeasurementsEvent eventArgs) {}
diff --git a/location/java/android/location/LocalListenerHelper.java b/location/java/android/location/LocalListenerHelper.java
index d7d2c513..592d01d 100644
--- a/location/java/android/location/LocalListenerHelper.java
+++ b/location/java/android/location/LocalListenerHelper.java
@@ -16,14 +16,14 @@
package android.location;
-import com.android.internal.util.Preconditions;
-
import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -46,6 +46,11 @@
mTag = name;
}
+ /**
+ * Adds a {@param listener} to the list of listeners on which callbacks will be executed. The
+ * execution will happen on the {@param handler} thread or alternatively in the callback thread
+ * if a {@code null} handler value is passed.
+ */
public boolean add(@NonNull TListener listener, Handler handler) {
Preconditions.checkNotNull(listener);
synchronized (mListeners) {
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 65e7ced..6abba95 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -143,7 +143,7 @@
private int mQuality = POWER_LOW;
private long mInterval = 60 * 60 * 1000; // 60 minutes
- private long mFastestInterval = (long)(mInterval / FASTEST_INTERVAL_FACTOR); // 10 minutes
+ private long mFastestInterval = (long) (mInterval / FASTEST_INTERVAL_FACTOR); // 10 minutes
private boolean mExplicitFastestInterval = false;
private long mExpireAt = Long.MAX_VALUE; // no expiry
private int mNumUpdates = Integer.MAX_VALUE; // no expiry
@@ -151,7 +151,11 @@
private WorkSource mWorkSource = null;
private boolean mHideFromAppOps = false; // True if this request shouldn't be counted by AppOps
- private String mProvider = LocationManager.FUSED_PROVIDER; // for deprecated APIs that explicitly request a provider
+ private String mProvider = LocationManager.FUSED_PROVIDER;
+ // for deprecated APIs that explicitly request a provider
+
+ /** If true, GNSS chipset will make strong tradeoffs to substantially restrict power use */
+ private boolean mLowPowerMode = false;
/**
* Create a location request with default parameters.
@@ -184,11 +188,11 @@
}
LocationRequest request = new LocationRequest()
- .setProvider(provider)
- .setQuality(quality)
- .setInterval(minTime)
- .setFastestInterval(minTime)
- .setSmallestDisplacement(minDistance);
+ .setProvider(provider)
+ .setQuality(quality)
+ .setInterval(minTime)
+ .setFastestInterval(minTime)
+ .setSmallestDisplacement(minDistance);
if (singleShot) request.setNumUpdates(1);
return request;
}
@@ -220,16 +224,17 @@
}
LocationRequest request = new LocationRequest()
- .setQuality(quality)
- .setInterval(minTime)
- .setFastestInterval(minTime)
- .setSmallestDisplacement(minDistance);
+ .setQuality(quality)
+ .setInterval(minTime)
+ .setFastestInterval(minTime)
+ .setSmallestDisplacement(minDistance);
if (singleShot) request.setNumUpdates(1);
return request;
}
/** @hide */
- public LocationRequest() { }
+ public LocationRequest() {
+ }
/** @hide */
public LocationRequest(LocationRequest src) {
@@ -243,6 +248,7 @@
mProvider = src.mProvider;
mWorkSource = src.mWorkSource;
mHideFromAppOps = src.mHideFromAppOps;
+ mLowPowerMode = src.mLowPowerMode;
}
/**
@@ -263,8 +269,8 @@
* on a location request.
*
* @param quality an accuracy or power constant
- * @throws InvalidArgumentException if the quality constant is not valid
* @return the same object, so that setters can be chained
+ * @throws InvalidArgumentException if the quality constant is not valid
*/
public LocationRequest setQuality(int quality) {
checkQuality(quality);
@@ -306,14 +312,14 @@
* on a location request.
*
* @param millis desired interval in millisecond, inexact
- * @throws InvalidArgumentException if the interval is less than zero
* @return the same object, so that setters can be chained
+ * @throws InvalidArgumentException if the interval is less than zero
*/
public LocationRequest setInterval(long millis) {
checkInterval(millis);
mInterval = millis;
if (!mExplicitFastestInterval) {
- mFastestInterval = (long)(mInterval / FASTEST_INTERVAL_FACTOR);
+ mFastestInterval = (long) (mInterval / FASTEST_INTERVAL_FACTOR);
}
return this;
}
@@ -327,6 +333,34 @@
return mInterval;
}
+
+ /**
+ * Requests the GNSS chipset to run in a low power mode and make strong tradeoffs to
+ * substantially restrict power.
+ *
+ * <p>In this mode, the GNSS chipset will not, on average, run power hungry operations like RF &
+ * signal searches for more than one second per interval {@link #mInterval}
+ *
+ * @param enabled Enable or disable low power mode
+ * @return the same object, so that setters can be chained
+ * @hide
+ */
+ @SystemApi
+ public LocationRequest setLowPowerMode(boolean enabled) {
+ mLowPowerMode = enabled;
+ return this;
+ }
+
+ /**
+ * Returns true if low power mode is enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isLowPowerMode() {
+ return mLowPowerMode;
+ }
+
/**
* Explicitly set the fastest interval for location updates, in
* milliseconds.
@@ -353,8 +387,8 @@
* then your effective fastest interval is {@link #setInterval}.
*
* @param millis fastest interval for updates in milliseconds, exact
- * @throws InvalidArgumentException if the interval is less than zero
* @return the same object, so that setters can be chained
+ * @throws InvalidArgumentException if the interval is less than zero
*/
public LocationRequest setFastestInterval(long millis) {
checkInterval(millis);
@@ -397,9 +431,9 @@
// Check for > Long.MAX_VALUE overflow (elapsedRealtime > 0):
if (millis > Long.MAX_VALUE - elapsedRealtime) {
- mExpireAt = Long.MAX_VALUE;
+ mExpireAt = Long.MAX_VALUE;
} else {
- mExpireAt = millis + elapsedRealtime;
+ mExpireAt = millis + elapsedRealtime;
}
if (mExpireAt < 0) mExpireAt = 0;
@@ -448,11 +482,14 @@
* to the location manager.
*
* @param numUpdates the number of location updates requested
- * @throws InvalidArgumentException if numUpdates is 0 or less
* @return the same object, so that setters can be chained
+ * @throws InvalidArgumentException if numUpdates is 0 or less
*/
public LocationRequest setNumUpdates(int numUpdates) {
- if (numUpdates <= 0) throw new IllegalArgumentException("invalid numUpdates: " + numUpdates);
+ if (numUpdates <= 0) {
+ throw new IllegalArgumentException(
+ "invalid numUpdates: " + numUpdates);
+ }
mNumUpdates = numUpdates;
return this;
}
@@ -462,6 +499,7 @@
*
* <p>By default this is {@link Integer#MAX_VALUE}, which indicates that
* locations are updated until the request is explicitly removed.
+ *
* @return number of updates
*/
public int getNumUpdates() {
@@ -539,8 +577,8 @@
* doesn't have the {@link android.Manifest.permission#UPDATE_APP_OPS_STATS} permission.
*
* @param hideFromAppOps If true AppOps won't keep track of this location request.
- * @see android.app.AppOpsManager
* @hide
+ * @see android.app.AppOpsManager
*/
@SystemApi
public void setHideFromAppOps(boolean hideFromAppOps) {
@@ -587,27 +625,29 @@
public static final Parcelable.Creator<LocationRequest> CREATOR =
new Parcelable.Creator<LocationRequest>() {
- @Override
- public LocationRequest createFromParcel(Parcel in) {
- LocationRequest request = new LocationRequest();
- request.setQuality(in.readInt());
- request.setFastestInterval(in.readLong());
- request.setInterval(in.readLong());
- request.setExpireAt(in.readLong());
- request.setNumUpdates(in.readInt());
- request.setSmallestDisplacement(in.readFloat());
- request.setHideFromAppOps(in.readInt() != 0);
- String provider = in.readString();
- if (provider != null) request.setProvider(provider);
- WorkSource workSource = in.readParcelable(null);
- if (workSource != null) request.setWorkSource(workSource);
- return request;
- }
- @Override
- public LocationRequest[] newArray(int size) {
- return new LocationRequest[size];
- }
- };
+ @Override
+ public LocationRequest createFromParcel(Parcel in) {
+ LocationRequest request = new LocationRequest();
+ request.setQuality(in.readInt());
+ request.setFastestInterval(in.readLong());
+ request.setInterval(in.readLong());
+ request.setExpireAt(in.readLong());
+ request.setNumUpdates(in.readInt());
+ request.setSmallestDisplacement(in.readFloat());
+ request.setHideFromAppOps(in.readInt() != 0);
+ request.setLowPowerMode(in.readInt() != 0);
+ String provider = in.readString();
+ if (provider != null) request.setProvider(provider);
+ WorkSource workSource = in.readParcelable(null);
+ if (workSource != null) request.setWorkSource(workSource);
+ return request;
+ }
+
+ @Override
+ public LocationRequest[] newArray(int size) {
+ return new LocationRequest[size];
+ }
+ };
@Override
public int describeContents() {
@@ -623,6 +663,7 @@
parcel.writeInt(mNumUpdates);
parcel.writeFloat(mSmallestDisplacement);
parcel.writeInt(mHideFromAppOps ? 1 : 0);
+ parcel.writeInt(mLowPowerMode ? 1 : 0);
parcel.writeString(mProvider);
parcel.writeParcelable(mWorkSource, 0);
}
@@ -663,9 +704,10 @@
s.append(" expireIn=");
TimeUtils.formatDuration(expireIn, s);
}
- if (mNumUpdates != Integer.MAX_VALUE){
+ if (mNumUpdates != Integer.MAX_VALUE) {
s.append(" num=").append(mNumUpdates);
}
+ s.append(" lowPowerMode=").append(mLowPowerMode);
s.append(']');
return s.toString();
}
diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java
index 26243e7..45fdb76 100644
--- a/location/java/com/android/internal/location/ProviderRequest.java
+++ b/location/java/com/android/internal/location/ProviderRequest.java
@@ -33,6 +33,12 @@
public long interval = Long.MAX_VALUE;
/**
+ * Whether provider shall make stronger than normal tradeoffs to substantially restrict power
+ * use.
+ */
+ public boolean lowPowerMode = false;
+
+ /**
* A more detailed set of requests.
* <p>Location Providers can optionally use this to
* fine tune location updates, for example when there
@@ -41,26 +47,29 @@
*/
public List<LocationRequest> locationRequests = new ArrayList<LocationRequest>();
- public ProviderRequest() { }
+ public ProviderRequest() {
+ }
public static final Parcelable.Creator<ProviderRequest> CREATOR =
new Parcelable.Creator<ProviderRequest>() {
- @Override
- public ProviderRequest createFromParcel(Parcel in) {
- ProviderRequest request = new ProviderRequest();
- request.reportLocation = in.readInt() == 1;
- request.interval = in.readLong();
- int count = in.readInt();
- for (int i = 0; i < count; i++) {
- request.locationRequests.add(LocationRequest.CREATOR.createFromParcel(in));
- }
- return request;
- }
- @Override
- public ProviderRequest[] newArray(int size) {
- return new ProviderRequest[size];
- }
- };
+ @Override
+ public ProviderRequest createFromParcel(Parcel in) {
+ ProviderRequest request = new ProviderRequest();
+ request.reportLocation = in.readInt() == 1;
+ request.interval = in.readLong();
+ request.lowPowerMode = in.readBoolean();
+ int count = in.readInt();
+ for (int i = 0; i < count; i++) {
+ request.locationRequests.add(LocationRequest.CREATOR.createFromParcel(in));
+ }
+ return request;
+ }
+
+ @Override
+ public ProviderRequest[] newArray(int size) {
+ return new ProviderRequest[size];
+ }
+ };
@Override
public int describeContents() {
@@ -71,6 +80,7 @@
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(reportLocation ? 1 : 0);
parcel.writeLong(interval);
+ parcel.writeBoolean(lowPowerMode);
parcel.writeInt(locationRequests.size());
for (LocationRequest request : locationRequests) {
request.writeToParcel(parcel, flags);
@@ -85,6 +95,7 @@
s.append("ON");
s.append(" interval=");
TimeUtils.formatDuration(interval, s);
+ s.append(" lowPowerMode=" + lowPowerMode);
} else {
s.append("OFF");
}
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 1b89c96..19d467a 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -127,6 +127,14 @@
}
/**
+ * @hide
+ * @return The underlying {@link AudioDevicePort} instance.
+ */
+ public AudioDevicePort getPort() {
+ return mPort;
+ }
+
+ /**
* @return The internal device ID.
*/
public int getId() {
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index ba29d2d..a647dcc 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -30,6 +30,7 @@
import android.os.BatteryManager;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.os.storage.StorageVolume;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Files;
@@ -40,21 +41,31 @@
import dalvik.system.CloseGuard;
+import com.google.android.collect.Sets;
+
import java.io.File;
-import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
/**
+ * MtpDatabase provides an interface for MTP operations that MtpServer can use. To do this, it uses
+ * MtpStorageManager for filesystem operations and MediaProvider to get media metadata. File
+ * operations are also reflected in MediaProvider if possible.
+ * operations
* {@hide}
*/
public class MtpDatabase implements AutoCloseable {
- private static final String TAG = "MtpDatabase";
+ private static final String TAG = MtpDatabase.class.getSimpleName();
- private final Context mUserContext;
private final Context mContext;
- private final String mPackageName;
private final ContentProviderClient mMediaProvider;
private final String mVolumeName;
private final Uri mObjectsUri;
@@ -63,527 +74,36 @@
private final AtomicBoolean mClosed = new AtomicBoolean();
private final CloseGuard mCloseGuard = CloseGuard.get();
- // path to primary storage
- private final String mMediaStoragePath;
- // if not null, restrict all queries to these subdirectories
- private final String[] mSubDirectories;
- // where clause for restricting queries to files in mSubDirectories
- private String mSubDirectoriesWhere;
- // where arguments for restricting queries to files in mSubDirectories
- private String[] mSubDirectoriesWhereArgs;
-
- private final HashMap<String, MtpStorage> mStorageMap = new HashMap<String, MtpStorage>();
+ private final HashMap<String, MtpStorage> mStorageMap = new HashMap<>();
// cached property groups for single properties
- private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByProperty
- = new HashMap<Integer, MtpPropertyGroup>();
+ private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByProperty = new HashMap<>();
// cached property groups for all properties for a given format
- private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByFormat
- = new HashMap<Integer, MtpPropertyGroup>();
-
- // true if the database has been modified in the current MTP session
- private boolean mDatabaseModified;
+ private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByFormat = new HashMap<>();
// SharedPreferences for writable MTP device properties
private SharedPreferences mDeviceProperties;
- private static final int DEVICE_PROPERTIES_DATABASE_VERSION = 1;
- private static final String[] ID_PROJECTION = new String[] {
- Files.FileColumns._ID, // 0
- };
- private static final String[] PATH_PROJECTION = new String[] {
- Files.FileColumns._ID, // 0
- Files.FileColumns.DATA, // 1
- };
- private static final String[] FORMAT_PROJECTION = new String[] {
- Files.FileColumns._ID, // 0
- Files.FileColumns.FORMAT, // 1
- };
- private static final String[] PATH_FORMAT_PROJECTION = new String[] {
- Files.FileColumns._ID, // 0
- Files.FileColumns.DATA, // 1
- Files.FileColumns.FORMAT, // 2
- };
- private static final String[] OBJECT_INFO_PROJECTION = new String[] {
- Files.FileColumns._ID, // 0
- Files.FileColumns.STORAGE_ID, // 1
- Files.FileColumns.FORMAT, // 2
- Files.FileColumns.PARENT, // 3
- Files.FileColumns.DATA, // 4
- Files.FileColumns.DATE_ADDED, // 5
- Files.FileColumns.DATE_MODIFIED, // 6
- };
- private static final String ID_WHERE = Files.FileColumns._ID + "=?";
- private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
-
- private static final String STORAGE_WHERE = Files.FileColumns.STORAGE_ID + "=?";
- private static final String FORMAT_WHERE = Files.FileColumns.FORMAT + "=?";
- private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
- private static final String STORAGE_FORMAT_WHERE = STORAGE_WHERE + " AND "
- + Files.FileColumns.FORMAT + "=?";
- private static final String STORAGE_PARENT_WHERE = STORAGE_WHERE + " AND "
- + Files.FileColumns.PARENT + "=?";
- private static final String FORMAT_PARENT_WHERE = FORMAT_WHERE + " AND "
- + Files.FileColumns.PARENT + "=?";
- private static final String STORAGE_FORMAT_PARENT_WHERE = STORAGE_FORMAT_WHERE + " AND "
- + Files.FileColumns.PARENT + "=?";
-
- private MtpServer mServer;
-
- // read from native code
+ // Cached device properties
private int mBatteryLevel;
private int mBatteryScale;
-
private int mDeviceType;
+ private MtpServer mServer;
+ private MtpStorageManager mManager;
+
+ private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
+ private static final String[] ID_PROJECTION = new String[] {Files.FileColumns._ID};
+ private static final String[] PATH_PROJECTION = new String[] {Files.FileColumns.DATA};
+ private static final String NO_MEDIA = ".nomedia";
+
static {
System.loadLibrary("media_jni");
}
- private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
- mBatteryScale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
- int newLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
- if (newLevel != mBatteryLevel) {
- mBatteryLevel = newLevel;
- if (mServer != null) {
- // send device property changed event
- mServer.sendDevicePropertyChanged(
- MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL);
- }
- }
- }
- }
- };
-
- public MtpDatabase(Context context, Context userContext, String volumeName, String storagePath,
- String[] subDirectories) {
- native_setup();
-
- mContext = context;
- mUserContext = userContext;
- mPackageName = context.getPackageName();
- mMediaProvider = userContext.getContentResolver()
- .acquireContentProviderClient(MediaStore.AUTHORITY);
- mVolumeName = volumeName;
- mMediaStoragePath = storagePath;
- mObjectsUri = Files.getMtpObjectsUri(volumeName);
- mMediaScanner = new MediaScanner(context, mVolumeName);
-
- mSubDirectories = subDirectories;
- if (subDirectories != null) {
- // Compute "where" string for restricting queries to subdirectories
- StringBuilder builder = new StringBuilder();
- builder.append("(");
- int count = subDirectories.length;
- for (int i = 0; i < count; i++) {
- builder.append(Files.FileColumns.DATA + "=? OR "
- + Files.FileColumns.DATA + " LIKE ?");
- if (i != count - 1) {
- builder.append(" OR ");
- }
- }
- builder.append(")");
- mSubDirectoriesWhere = builder.toString();
-
- // Compute "where" arguments for restricting queries to subdirectories
- mSubDirectoriesWhereArgs = new String[count * 2];
- for (int i = 0, j = 0; i < count; i++) {
- String path = subDirectories[i];
- mSubDirectoriesWhereArgs[j++] = path;
- mSubDirectoriesWhereArgs[j++] = path + "/%";
- }
- }
-
- initDeviceProperties(context);
- mDeviceType = SystemProperties.getInt("sys.usb.mtp.device_type", 0);
-
- mCloseGuard.open("close");
- }
-
- public void setServer(MtpServer server) {
- mServer = server;
-
- // always unregister before registering
- try {
- mContext.unregisterReceiver(mBatteryReceiver);
- } catch (IllegalArgumentException e) {
- // wasn't previously registered, ignore
- }
-
- // register for battery notifications when we are connected
- if (server != null) {
- mContext.registerReceiver(mBatteryReceiver,
- new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
- }
- }
-
- @Override
- public void close() {
- mCloseGuard.close();
- if (mClosed.compareAndSet(false, true)) {
- mMediaScanner.close();
- mMediaProvider.close();
- native_finalize();
- }
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- if (mCloseGuard != null) {
- mCloseGuard.warnIfOpen();
- }
-
- close();
- } finally {
- super.finalize();
- }
- }
-
- public void addStorage(MtpStorage storage) {
- mStorageMap.put(storage.getPath(), storage);
- }
-
- public void removeStorage(MtpStorage storage) {
- mStorageMap.remove(storage.getPath());
- }
-
- private void initDeviceProperties(Context context) {
- final String devicePropertiesName = "device-properties";
- mDeviceProperties = context.getSharedPreferences(devicePropertiesName, Context.MODE_PRIVATE);
- File databaseFile = context.getDatabasePath(devicePropertiesName);
-
- if (databaseFile.exists()) {
- // for backward compatibility - read device properties from sqlite database
- // and migrate them to shared prefs
- SQLiteDatabase db = null;
- Cursor c = null;
- try {
- db = context.openOrCreateDatabase("device-properties", Context.MODE_PRIVATE, null);
- if (db != null) {
- c = db.query("properties", new String[] { "_id", "code", "value" },
- null, null, null, null, null);
- if (c != null) {
- SharedPreferences.Editor e = mDeviceProperties.edit();
- while (c.moveToNext()) {
- String name = c.getString(1);
- String value = c.getString(2);
- e.putString(name, value);
- }
- e.commit();
- }
- }
- } catch (Exception e) {
- Log.e(TAG, "failed to migrate device properties", e);
- } finally {
- if (c != null) c.close();
- if (db != null) db.close();
- }
- context.deleteDatabase(devicePropertiesName);
- }
- }
-
- // check to see if the path is contained in one of our storage subdirectories
- // returns true if we have no special subdirectories
- private boolean inStorageSubDirectory(String path) {
- if (mSubDirectories == null) return true;
- if (path == null) return false;
-
- boolean allowed = false;
- int pathLength = path.length();
- for (int i = 0; i < mSubDirectories.length && !allowed; i++) {
- String subdir = mSubDirectories[i];
- int subdirLength = subdir.length();
- if (subdirLength < pathLength &&
- path.charAt(subdirLength) == '/' &&
- path.startsWith(subdir)) {
- allowed = true;
- }
- }
- return allowed;
- }
-
- // check to see if the path matches one of our storage subdirectories
- // returns true if we have no special subdirectories
- private boolean isStorageSubDirectory(String path) {
- if (mSubDirectories == null) return false;
- for (int i = 0; i < mSubDirectories.length; i++) {
- if (path.equals(mSubDirectories[i])) {
- return true;
- }
- }
- return false;
- }
-
- // returns true if the path is in the storage root
- private boolean inStorageRoot(String path) {
- try {
- File f = new File(path);
- String canonical = f.getCanonicalPath();
- for (String root: mStorageMap.keySet()) {
- if (canonical.startsWith(root)) {
- return true;
- }
- }
- } catch (IOException e) {
- // ignore
- }
- return false;
- }
-
- private int beginSendObject(String path, int format, int parent,
- int storageId, long size, long modified) {
- // if the path is outside of the storage root, do not allow access
- if (!inStorageRoot(path)) {
- Log.e(TAG, "attempt to put file outside of storage area: " + path);
- return -1;
- }
- // if mSubDirectories is not null, do not allow copying files to any other locations
- if (!inStorageSubDirectory(path)) return -1;
-
- // make sure the object does not exist
- if (path != null) {
- Cursor c = null;
- try {
- c = mMediaProvider.query(mObjectsUri, ID_PROJECTION, PATH_WHERE,
- new String[] { path }, null, null);
- if (c != null && c.getCount() > 0) {
- Log.w(TAG, "file already exists in beginSendObject: " + path);
- return -1;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in beginSendObject", e);
- } finally {
- if (c != null) {
- c.close();
- }
- }
- }
-
- mDatabaseModified = true;
- ContentValues values = new ContentValues();
- values.put(Files.FileColumns.DATA, path);
- values.put(Files.FileColumns.FORMAT, format);
- values.put(Files.FileColumns.PARENT, parent);
- values.put(Files.FileColumns.STORAGE_ID, storageId);
- values.put(Files.FileColumns.SIZE, size);
- values.put(Files.FileColumns.DATE_MODIFIED, modified);
-
- try {
- Uri uri = mMediaProvider.insert(mObjectsUri, values);
- if (uri != null) {
- return Integer.parseInt(uri.getPathSegments().get(2));
- } else {
- return -1;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in beginSendObject", e);
- return -1;
- }
- }
-
- private void endSendObject(String path, int handle, int format, boolean succeeded) {
- if (succeeded) {
- // handle abstract playlists separately
- // they do not exist in the file system so don't use the media scanner here
- if (format == MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST) {
- // extract name from path
- String name = path;
- int lastSlash = name.lastIndexOf('/');
- if (lastSlash >= 0) {
- name = name.substring(lastSlash + 1);
- }
- // strip trailing ".pla" from the name
- if (name.endsWith(".pla")) {
- name = name.substring(0, name.length() - 4);
- }
-
- ContentValues values = new ContentValues(1);
- values.put(Audio.Playlists.DATA, path);
- values.put(Audio.Playlists.NAME, name);
- values.put(Files.FileColumns.FORMAT, format);
- values.put(Files.FileColumns.DATE_MODIFIED, System.currentTimeMillis() / 1000);
- values.put(MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, handle);
- try {
- Uri uri = mMediaProvider.insert(
- Audio.Playlists.EXTERNAL_CONTENT_URI, values);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in endSendObject", e);
- }
- } else {
- mMediaScanner.scanMtpFile(path, handle, format);
- }
- } else {
- deleteFile(handle);
- }
- }
-
- private void doScanDirectory(String path) {
- String[] scanPath;
- scanPath = new String[] { path };
- mMediaScanner.scanDirectories(scanPath);
- }
-
- private Cursor createObjectQuery(int storageID, int format, int parent) throws RemoteException {
- String where;
- String[] whereArgs;
-
- if (storageID == 0xFFFFFFFF) {
- // query all stores
- if (format == 0) {
- // query all formats
- if (parent == 0) {
- // query all objects
- where = null;
- whereArgs = null;
- } else {
- if (parent == 0xFFFFFFFF) {
- // all objects in root of store
- parent = 0;
- }
- where = PARENT_WHERE;
- whereArgs = new String[] { Integer.toString(parent) };
- }
- } else {
- // query specific format
- if (parent == 0) {
- // query all objects
- where = FORMAT_WHERE;
- whereArgs = new String[] { Integer.toString(format) };
- } else {
- if (parent == 0xFFFFFFFF) {
- // all objects in root of store
- parent = 0;
- }
- where = FORMAT_PARENT_WHERE;
- whereArgs = new String[] { Integer.toString(format),
- Integer.toString(parent) };
- }
- }
- } else {
- // query specific store
- if (format == 0) {
- // query all formats
- if (parent == 0) {
- // query all objects
- where = STORAGE_WHERE;
- whereArgs = new String[] { Integer.toString(storageID) };
- } else {
- if (parent == 0xFFFFFFFF) {
- // all objects in root of store
- parent = 0;
- where = STORAGE_PARENT_WHERE;
- whereArgs = new String[]{Integer.toString(storageID),
- Integer.toString(parent)};
- } else {
- // If a parent is specified, the storage is redundant
- where = PARENT_WHERE;
- whereArgs = new String[]{Integer.toString(parent)};
- }
- }
- } else {
- // query specific format
- if (parent == 0) {
- // query all objects
- where = STORAGE_FORMAT_WHERE;
- whereArgs = new String[] { Integer.toString(storageID),
- Integer.toString(format) };
- } else {
- if (parent == 0xFFFFFFFF) {
- // all objects in root of store
- parent = 0;
- where = STORAGE_FORMAT_PARENT_WHERE;
- whereArgs = new String[]{Integer.toString(storageID),
- Integer.toString(format),
- Integer.toString(parent)};
- } else {
- // If a parent is specified, the storage is redundant
- where = FORMAT_PARENT_WHERE;
- whereArgs = new String[]{Integer.toString(format),
- Integer.toString(parent)};
- }
- }
- }
- }
-
- // if we are restricting queries to mSubDirectories, we need to add the restriction
- // onto our "where" arguments
- if (mSubDirectoriesWhere != null) {
- if (where == null) {
- where = mSubDirectoriesWhere;
- whereArgs = mSubDirectoriesWhereArgs;
- } else {
- where = where + " AND " + mSubDirectoriesWhere;
-
- // create new array to hold whereArgs and mSubDirectoriesWhereArgs
- String[] newWhereArgs =
- new String[whereArgs.length + mSubDirectoriesWhereArgs.length];
- int i, j;
- for (i = 0; i < whereArgs.length; i++) {
- newWhereArgs[i] = whereArgs[i];
- }
- for (j = 0; j < mSubDirectoriesWhereArgs.length; i++, j++) {
- newWhereArgs[i] = mSubDirectoriesWhereArgs[j];
- }
- whereArgs = newWhereArgs;
- }
- }
-
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION, where,
- whereArgs, null, null);
- }
-
- private int[] getObjectList(int storageID, int format, int parent) {
- Cursor c = null;
- try {
- c = createObjectQuery(storageID, format, parent);
- if (c == null) {
- return null;
- }
- int count = c.getCount();
- if (count > 0) {
- int[] result = new int[count];
- for (int i = 0; i < count; i++) {
- c.moveToNext();
- result[i] = c.getInt(0);
- }
- return result;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in getObjectList", e);
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return null;
- }
-
- private int getNumObjects(int storageID, int format, int parent) {
- Cursor c = null;
- try {
- c = createObjectQuery(storageID, format, parent);
- if (c != null) {
- return c.getCount();
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in getNumObjects", e);
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return -1;
- }
-
- private int[] getSupportedPlaybackFormats() {
- return new int[] {
- // allow transfering arbitrary files
+ private static final int[] PLAYBACK_FORMATS = {
+ // allow transferring arbitrary files
MtpConstants.FORMAT_UNDEFINED,
MtpConstants.FORMAT_ASSOCIATION,
@@ -613,45 +133,23 @@
MtpConstants.FORMAT_FLAC,
MtpConstants.FORMAT_DNG,
MtpConstants.FORMAT_HEIF,
- };
- }
+ };
- private int[] getSupportedCaptureFormats() {
- // no capture formats yet
- return null;
- }
-
- static final int[] FILE_PROPERTIES = {
- // NOTE must match beginning of AUDIO_PROPERTIES, VIDEO_PROPERTIES
- // and IMAGE_PROPERTIES below
+ private static final int[] FILE_PROPERTIES = {
MtpConstants.PROPERTY_STORAGE_ID,
MtpConstants.PROPERTY_OBJECT_FORMAT,
MtpConstants.PROPERTY_PROTECTION_STATUS,
MtpConstants.PROPERTY_OBJECT_SIZE,
MtpConstants.PROPERTY_OBJECT_FILE_NAME,
MtpConstants.PROPERTY_DATE_MODIFIED,
- MtpConstants.PROPERTY_PARENT_OBJECT,
MtpConstants.PROPERTY_PERSISTENT_UID,
+ MtpConstants.PROPERTY_PARENT_OBJECT,
MtpConstants.PROPERTY_NAME,
MtpConstants.PROPERTY_DISPLAY_NAME,
MtpConstants.PROPERTY_DATE_ADDED,
};
- static final int[] AUDIO_PROPERTIES = {
- // NOTE must match FILE_PROPERTIES above
- MtpConstants.PROPERTY_STORAGE_ID,
- MtpConstants.PROPERTY_OBJECT_FORMAT,
- MtpConstants.PROPERTY_PROTECTION_STATUS,
- MtpConstants.PROPERTY_OBJECT_SIZE,
- MtpConstants.PROPERTY_OBJECT_FILE_NAME,
- MtpConstants.PROPERTY_DATE_MODIFIED,
- MtpConstants.PROPERTY_PARENT_OBJECT,
- MtpConstants.PROPERTY_PERSISTENT_UID,
- MtpConstants.PROPERTY_NAME,
- MtpConstants.PROPERTY_DISPLAY_NAME,
- MtpConstants.PROPERTY_DATE_ADDED,
-
- // audio specific properties
+ private static final int[] AUDIO_PROPERTIES = {
MtpConstants.PROPERTY_ARTIST,
MtpConstants.PROPERTY_ALBUM_NAME,
MtpConstants.PROPERTY_ALBUM_ARTIST,
@@ -667,45 +165,25 @@
MtpConstants.PROPERTY_SAMPLE_RATE,
};
- static final int[] VIDEO_PROPERTIES = {
- // NOTE must match FILE_PROPERTIES above
- MtpConstants.PROPERTY_STORAGE_ID,
- MtpConstants.PROPERTY_OBJECT_FORMAT,
- MtpConstants.PROPERTY_PROTECTION_STATUS,
- MtpConstants.PROPERTY_OBJECT_SIZE,
- MtpConstants.PROPERTY_OBJECT_FILE_NAME,
- MtpConstants.PROPERTY_DATE_MODIFIED,
- MtpConstants.PROPERTY_PARENT_OBJECT,
- MtpConstants.PROPERTY_PERSISTENT_UID,
- MtpConstants.PROPERTY_NAME,
- MtpConstants.PROPERTY_DISPLAY_NAME,
- MtpConstants.PROPERTY_DATE_ADDED,
-
- // video specific properties
+ private static final int[] VIDEO_PROPERTIES = {
MtpConstants.PROPERTY_ARTIST,
MtpConstants.PROPERTY_ALBUM_NAME,
MtpConstants.PROPERTY_DURATION,
MtpConstants.PROPERTY_DESCRIPTION,
};
- static final int[] IMAGE_PROPERTIES = {
- // NOTE must match FILE_PROPERTIES above
- MtpConstants.PROPERTY_STORAGE_ID,
- MtpConstants.PROPERTY_OBJECT_FORMAT,
- MtpConstants.PROPERTY_PROTECTION_STATUS,
- MtpConstants.PROPERTY_OBJECT_SIZE,
- MtpConstants.PROPERTY_OBJECT_FILE_NAME,
- MtpConstants.PROPERTY_DATE_MODIFIED,
- MtpConstants.PROPERTY_PARENT_OBJECT,
- MtpConstants.PROPERTY_PERSISTENT_UID,
- MtpConstants.PROPERTY_NAME,
- MtpConstants.PROPERTY_DISPLAY_NAME,
- MtpConstants.PROPERTY_DATE_ADDED,
-
- // image specific properties
+ private static final int[] IMAGE_PROPERTIES = {
MtpConstants.PROPERTY_DESCRIPTION,
};
+ private static final int[] DEVICE_PROPERTIES = {
+ MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,
+ MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,
+ MtpConstants.DEVICE_PROPERTY_IMAGE_SIZE,
+ MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL,
+ MtpConstants.DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE,
+ };
+
private int[] getSupportedObjectProperties(int format) {
switch (format) {
case MtpConstants.FORMAT_MP3:
@@ -713,183 +191,541 @@
case MtpConstants.FORMAT_WMA:
case MtpConstants.FORMAT_OGG:
case MtpConstants.FORMAT_AAC:
- return AUDIO_PROPERTIES;
+ return IntStream.concat(Arrays.stream(FILE_PROPERTIES),
+ Arrays.stream(AUDIO_PROPERTIES)).toArray();
case MtpConstants.FORMAT_MPEG:
case MtpConstants.FORMAT_3GP_CONTAINER:
case MtpConstants.FORMAT_WMV:
- return VIDEO_PROPERTIES;
+ return IntStream.concat(Arrays.stream(FILE_PROPERTIES),
+ Arrays.stream(VIDEO_PROPERTIES)).toArray();
case MtpConstants.FORMAT_EXIF_JPEG:
case MtpConstants.FORMAT_GIF:
case MtpConstants.FORMAT_PNG:
case MtpConstants.FORMAT_BMP:
case MtpConstants.FORMAT_DNG:
case MtpConstants.FORMAT_HEIF:
- return IMAGE_PROPERTIES;
+ return IntStream.concat(Arrays.stream(FILE_PROPERTIES),
+ Arrays.stream(IMAGE_PROPERTIES)).toArray();
default:
return FILE_PROPERTIES;
}
}
private int[] getSupportedDeviceProperties() {
- return new int[] {
- MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,
- MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,
- MtpConstants.DEVICE_PROPERTY_IMAGE_SIZE,
- MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL,
- MtpConstants.DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE,
- };
+ return DEVICE_PROPERTIES;
+ }
+
+ private int[] getSupportedPlaybackFormats() {
+ return PLAYBACK_FORMATS;
+ }
+
+ private int[] getSupportedCaptureFormats() {
+ // no capture formats yet
+ return null;
+ }
+
+ private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
+ mBatteryScale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
+ int newLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
+ if (newLevel != mBatteryLevel) {
+ mBatteryLevel = newLevel;
+ if (mServer != null) {
+ // send device property changed event
+ mServer.sendDevicePropertyChanged(
+ MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL);
+ }
+ }
+ }
+ }
+ };
+
+ public MtpDatabase(Context context, Context userContext, String volumeName,
+ String[] subDirectories) {
+ native_setup();
+ mContext = context;
+ mMediaProvider = userContext.getContentResolver()
+ .acquireContentProviderClient(MediaStore.AUTHORITY);
+ mVolumeName = volumeName;
+ mObjectsUri = Files.getMtpObjectsUri(volumeName);
+ mMediaScanner = new MediaScanner(context, mVolumeName);
+ mManager = new MtpStorageManager(new MtpStorageManager.MtpNotifier() {
+ @Override
+ public void sendObjectAdded(int id) {
+ if (MtpDatabase.this.mServer != null)
+ MtpDatabase.this.mServer.sendObjectAdded(id);
+ }
+
+ @Override
+ public void sendObjectRemoved(int id) {
+ if (MtpDatabase.this.mServer != null)
+ MtpDatabase.this.mServer.sendObjectRemoved(id);
+ }
+ }, subDirectories == null ? null : Sets.newHashSet(subDirectories));
+
+ initDeviceProperties(context);
+ mDeviceType = SystemProperties.getInt("sys.usb.mtp.device_type", 0);
+ mCloseGuard.open("close");
+ }
+
+ public void setServer(MtpServer server) {
+ mServer = server;
+ // always unregister before registering
+ try {
+ mContext.unregisterReceiver(mBatteryReceiver);
+ } catch (IllegalArgumentException e) {
+ // wasn't previously registered, ignore
+ }
+ // register for battery notifications when we are connected
+ if (server != null) {
+ mContext.registerReceiver(mBatteryReceiver,
+ new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+ }
+ }
+
+ @Override
+ public void close() {
+ mManager.close();
+ mCloseGuard.close();
+ if (mClosed.compareAndSet(false, true)) {
+ mMediaScanner.close();
+ mMediaProvider.close();
+ native_finalize();
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ public void addStorage(StorageVolume storage) {
+ MtpStorage mtpStorage = mManager.addMtpStorage(storage);
+ mStorageMap.put(storage.getPath(), mtpStorage);
+ mServer.addStorage(mtpStorage);
+ }
+
+ public void removeStorage(StorageVolume storage) {
+ MtpStorage mtpStorage = mStorageMap.get(storage.getPath());
+ if (mtpStorage == null) {
+ return;
+ }
+ mServer.removeStorage(mtpStorage);
+ mManager.removeMtpStorage(mtpStorage);
+ mStorageMap.remove(storage.getPath());
+ }
+
+ private void initDeviceProperties(Context context) {
+ final String devicePropertiesName = "device-properties";
+ mDeviceProperties = context.getSharedPreferences(devicePropertiesName,
+ Context.MODE_PRIVATE);
+ File databaseFile = context.getDatabasePath(devicePropertiesName);
+
+ if (databaseFile.exists()) {
+ // for backward compatibility - read device properties from sqlite database
+ // and migrate them to shared prefs
+ SQLiteDatabase db = null;
+ Cursor c = null;
+ try {
+ db = context.openOrCreateDatabase("device-properties", Context.MODE_PRIVATE, null);
+ if (db != null) {
+ c = db.query("properties", new String[]{"_id", "code", "value"},
+ null, null, null, null, null);
+ if (c != null) {
+ SharedPreferences.Editor e = mDeviceProperties.edit();
+ while (c.moveToNext()) {
+ String name = c.getString(1);
+ String value = c.getString(2);
+ e.putString(name, value);
+ }
+ e.commit();
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "failed to migrate device properties", e);
+ } finally {
+ if (c != null) c.close();
+ if (db != null) db.close();
+ }
+ context.deleteDatabase(devicePropertiesName);
+ }
+ }
+
+ private int beginSendObject(String path, int format, int parent, int storageId) {
+ MtpStorageManager.MtpObject parentObj =
+ parent == 0 ? mManager.getStorageRoot(storageId) : mManager.getObject(parent);
+ if (parentObj == null) {
+ return -1;
+ }
+
+ Path objPath = Paths.get(path);
+ return mManager.beginSendObject(parentObj, objPath.getFileName().toString(), format);
+ }
+
+ private void endSendObject(int handle, boolean succeeded) {
+ MtpStorageManager.MtpObject obj = mManager.getObject(handle);
+ if (obj == null || !mManager.endSendObject(obj, succeeded)) {
+ Log.e(TAG, "Failed to successfully end send object");
+ return;
+ }
+ // Add the new file to MediaProvider
+ if (succeeded) {
+ String path = obj.getPath().toString();
+ int format = obj.getFormat();
+ // Get parent info from MediaProvider, since the id is different from MTP's
+ ContentValues values = new ContentValues();
+ values.put(Files.FileColumns.DATA, path);
+ values.put(Files.FileColumns.FORMAT, format);
+ values.put(Files.FileColumns.SIZE, obj.getSize());
+ values.put(Files.FileColumns.DATE_MODIFIED, obj.getModifiedTime());
+ try {
+ if (obj.getParent().isRoot()) {
+ values.put(Files.FileColumns.PARENT, 0);
+ } else {
+ int parentId = findInMedia(obj.getParent().getPath());
+ if (parentId != -1) {
+ values.put(Files.FileColumns.PARENT, parentId);
+ } else {
+ // The parent isn't in MediaProvider. Don't add the new file.
+ return;
+ }
+ }
+
+ Uri uri = mMediaProvider.insert(mObjectsUri, values);
+ if (uri != null) {
+ rescanFile(path, Integer.parseInt(uri.getPathSegments().get(2)), format);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in beginSendObject", e);
+ }
+ }
+ }
+
+ private void rescanFile(String path, int handle, int format) {
+ // handle abstract playlists separately
+ // they do not exist in the file system so don't use the media scanner here
+ if (format == MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST) {
+ // extract name from path
+ String name = path;
+ int lastSlash = name.lastIndexOf('/');
+ if (lastSlash >= 0) {
+ name = name.substring(lastSlash + 1);
+ }
+ // strip trailing ".pla" from the name
+ if (name.endsWith(".pla")) {
+ name = name.substring(0, name.length() - 4);
+ }
+
+ ContentValues values = new ContentValues(1);
+ values.put(Audio.Playlists.DATA, path);
+ values.put(Audio.Playlists.NAME, name);
+ values.put(Files.FileColumns.FORMAT, format);
+ values.put(Files.FileColumns.DATE_MODIFIED, System.currentTimeMillis() / 1000);
+ values.put(MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, handle);
+ try {
+ mMediaProvider.insert(
+ Audio.Playlists.EXTERNAL_CONTENT_URI, values);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in endSendObject", e);
+ }
+ } else {
+ mMediaScanner.scanMtpFile(path, handle, format);
+ }
+ }
+
+ private int[] getObjectList(int storageID, int format, int parent) {
+ Stream<MtpStorageManager.MtpObject> objectStream = mManager.getObjects(parent,
+ format, storageID);
+ if (objectStream == null) {
+ return null;
+ }
+ return objectStream.mapToInt(MtpStorageManager.MtpObject::getId).toArray();
+ }
+
+ private int getNumObjects(int storageID, int format, int parent) {
+ Stream<MtpStorageManager.MtpObject> objectStream = mManager.getObjects(parent,
+ format, storageID);
+ if (objectStream == null) {
+ return -1;
+ }
+ return (int) objectStream.count();
}
private MtpPropertyList getObjectPropertyList(int handle, int format, int property,
- int groupCode, int depth) {
+ int groupCode, int depth) {
// FIXME - implement group support
- if (groupCode != 0) {
- return new MtpPropertyList(0, MtpConstants.RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED);
+ if (property == 0) {
+ if (groupCode == 0) {
+ return new MtpPropertyList(MtpConstants.RESPONSE_PARAMETER_NOT_SUPPORTED);
+ }
+ return new MtpPropertyList(MtpConstants.RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED);
+ }
+ if (depth == 0xFFFFFFFF && (handle == 0 || handle == 0xFFFFFFFF)) {
+ // request all objects starting at root
+ handle = 0xFFFFFFFF;
+ depth = 0;
+ }
+ if (!(depth == 0 || depth == 1)) {
+ // we only support depth 0 and 1
+ // depth 0: single object, depth 1: immediate children
+ return new MtpPropertyList(MtpConstants.RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED);
+ }
+ Stream<MtpStorageManager.MtpObject> objectStream = Stream.of();
+ if (handle == 0xFFFFFFFF) {
+ // All objects are requested
+ objectStream = mManager.getObjects(0, format, 0xFFFFFFFF);
+ if (objectStream == null) {
+ return new MtpPropertyList(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
+ }
+ } else if (handle != 0) {
+ // Add the requested object if format matches
+ MtpStorageManager.MtpObject obj = mManager.getObject(handle);
+ if (obj == null) {
+ return new MtpPropertyList(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
+ }
+ if (obj.getFormat() == format || format == 0) {
+ objectStream = Stream.of(obj);
+ }
+ }
+ if (handle == 0 || depth == 1) {
+ if (handle == 0) {
+ handle = 0xFFFFFFFF;
+ }
+ // Get the direct children of root or this object.
+ Stream<MtpStorageManager.MtpObject> childStream = mManager.getObjects(handle, format,
+ 0xFFFFFFFF);
+ if (childStream == null) {
+ return new MtpPropertyList(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
+ }
+ objectStream = Stream.concat(objectStream, childStream);
}
+ MtpPropertyList ret = new MtpPropertyList(MtpConstants.RESPONSE_OK);
MtpPropertyGroup propertyGroup;
- if (property == 0xffffffff) {
- if (format == 0 && handle != 0 && handle != 0xffffffff) {
- // return properties based on the object's format
- format = getObjectFormat(handle);
+ Iterator<MtpStorageManager.MtpObject> iter = objectStream.iterator();
+ while (iter.hasNext()) {
+ MtpStorageManager.MtpObject obj = iter.next();
+ if (property == 0xffffffff) {
+ // Get all properties supported by this object
+ propertyGroup = mPropertyGroupsByFormat.get(obj.getFormat());
+ if (propertyGroup == null) {
+ int[] propertyList = getSupportedObjectProperties(format);
+ propertyGroup = new MtpPropertyGroup(mMediaProvider, mVolumeName,
+ propertyList);
+ mPropertyGroupsByFormat.put(format, propertyGroup);
+ }
+ } else {
+ // Get this property value
+ final int[] propertyList = new int[]{property};
+ propertyGroup = mPropertyGroupsByProperty.get(property);
+ if (propertyGroup == null) {
+ propertyGroup = new MtpPropertyGroup(mMediaProvider, mVolumeName,
+ propertyList);
+ mPropertyGroupsByProperty.put(property, propertyGroup);
+ }
}
- propertyGroup = mPropertyGroupsByFormat.get(format);
- if (propertyGroup == null) {
- int[] propertyList = getSupportedObjectProperties(format);
- propertyGroup = new MtpPropertyGroup(this, mMediaProvider,
- mVolumeName, propertyList);
- mPropertyGroupsByFormat.put(format, propertyGroup);
- }
- } else {
- propertyGroup = mPropertyGroupsByProperty.get(property);
- if (propertyGroup == null) {
- final int[] propertyList = new int[] { property };
- propertyGroup = new MtpPropertyGroup(
- this, mMediaProvider, mVolumeName, propertyList);
- mPropertyGroupsByProperty.put(property, propertyGroup);
+ int err = propertyGroup.getPropertyList(obj, ret);
+ if (err != MtpConstants.RESPONSE_OK) {
+ return new MtpPropertyList(err);
}
}
-
- return propertyGroup.getPropertyList(handle, format, depth);
+ return ret;
}
private int renameFile(int handle, String newName) {
- Cursor c = null;
-
- // first compute current path
- String path = null;
- String[] whereArgs = new String[] { Integer.toString(handle) };
- try {
- c = mMediaProvider.query(mObjectsUri, PATH_PROJECTION, ID_WHERE,
- whereArgs, null, null);
- if (c != null && c.moveToNext()) {
- path = c.getString(1);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in getObjectFilePath", e);
- return MtpConstants.RESPONSE_GENERAL_ERROR;
- } finally {
- if (c != null) {
- c.close();
- }
- }
- if (path == null) {
+ MtpStorageManager.MtpObject obj = mManager.getObject(handle);
+ if (obj == null) {
return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
}
-
- // do not allow renaming any of the special subdirectories
- if (isStorageSubDirectory(path)) {
- return MtpConstants.RESPONSE_OBJECT_WRITE_PROTECTED;
- }
+ Path oldPath = obj.getPath();
// now rename the file. make sure this succeeds before updating database
- File oldFile = new File(path);
- int lastSlash = path.lastIndexOf('/');
- if (lastSlash <= 1) {
+ if (!mManager.beginRenameObject(obj, newName))
return MtpConstants.RESPONSE_GENERAL_ERROR;
+ Path newPath = obj.getPath();
+ boolean success = oldPath.toFile().renameTo(newPath.toFile());
+ if (!mManager.endRenameObject(obj, oldPath.getFileName().toString(), success)) {
+ Log.e(TAG, "Failed to end rename object");
}
- String newPath = path.substring(0, lastSlash + 1) + newName;
- File newFile = new File(newPath);
- boolean success = oldFile.renameTo(newFile);
if (!success) {
- Log.w(TAG, "renaming "+ path + " to " + newPath + " failed");
return MtpConstants.RESPONSE_GENERAL_ERROR;
}
- // finally update database
+ // finally update MediaProvider
ContentValues values = new ContentValues();
- values.put(Files.FileColumns.DATA, newPath);
- int updated = 0;
+ values.put(Files.FileColumns.DATA, newPath.toString());
+ String[] whereArgs = new String[]{oldPath.toString()};
try {
// note - we are relying on a special case in MediaProvider.update() to update
// the paths for all children in the case where this is a directory.
- updated = mMediaProvider.update(mObjectsUri, values, ID_WHERE, whereArgs);
+ mMediaProvider.update(mObjectsUri, values, PATH_WHERE, whereArgs);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in mMediaProvider.update", e);
}
- if (updated == 0) {
- Log.e(TAG, "Unable to update path for " + path + " to " + newPath);
- // this shouldn't happen, but if it does we need to rename the file to its original name
- newFile.renameTo(oldFile);
- return MtpConstants.RESPONSE_GENERAL_ERROR;
- }
// check if nomedia status changed
- if (newFile.isDirectory()) {
+ if (obj.isDir()) {
// for directories, check if renamed from something hidden to something non-hidden
- if (oldFile.getName().startsWith(".") && !newPath.startsWith(".")) {
+ if (oldPath.getFileName().startsWith(".") && !newPath.startsWith(".")) {
// directory was unhidden
try {
- mMediaProvider.call(MediaStore.UNHIDE_CALL, newPath, null);
+ mMediaProvider.call(MediaStore.UNHIDE_CALL, newPath.toString(), null);
} catch (RemoteException e) {
Log.e(TAG, "failed to unhide/rescan for " + newPath);
}
}
} else {
// for files, check if renamed from .nomedia to something else
- if (oldFile.getName().toLowerCase(Locale.US).equals(".nomedia")
- && !newPath.toLowerCase(Locale.US).equals(".nomedia")) {
+ if (oldPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA)
+ && !newPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA)) {
try {
- mMediaProvider.call(MediaStore.UNHIDE_CALL, oldFile.getParent(), null);
+ mMediaProvider.call(MediaStore.UNHIDE_CALL,
+ oldPath.getParent().toString(), null);
} catch (RemoteException e) {
Log.e(TAG, "failed to unhide/rescan for " + newPath);
}
}
}
-
return MtpConstants.RESPONSE_OK;
}
- private int moveObject(int handle, int newParent, int newStorage, String newPath) {
- String[] whereArgs = new String[] { Integer.toString(handle) };
+ private int beginMoveObject(int handle, int newParent, int newStorage) {
+ MtpStorageManager.MtpObject obj = mManager.getObject(handle);
+ MtpStorageManager.MtpObject parent = newParent == 0 ?
+ mManager.getStorageRoot(newStorage) : mManager.getObject(newParent);
+ if (obj == null || parent == null)
+ return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
- // do not allow renaming any of the special subdirectories
- if (isStorageSubDirectory(newPath)) {
- return MtpConstants.RESPONSE_OBJECT_WRITE_PROTECTED;
+ boolean allowed = mManager.beginMoveObject(obj, parent);
+ return allowed ? MtpConstants.RESPONSE_OK : MtpConstants.RESPONSE_GENERAL_ERROR;
+ }
+
+ private void endMoveObject(int oldParent, int newParent, int oldStorage, int newStorage,
+ int objId, boolean success) {
+ MtpStorageManager.MtpObject oldParentObj = oldParent == 0 ?
+ mManager.getStorageRoot(oldStorage) : mManager.getObject(oldParent);
+ MtpStorageManager.MtpObject newParentObj = newParent == 0 ?
+ mManager.getStorageRoot(newStorage) : mManager.getObject(newParent);
+ MtpStorageManager.MtpObject obj = mManager.getObject(objId);
+ String name = obj.getName();
+ if (newParentObj == null || oldParentObj == null
+ ||!mManager.endMoveObject(oldParentObj, newParentObj, name, success)) {
+ Log.e(TAG, "Failed to end move object");
+ return;
}
- // update database
+ obj = mManager.getObject(objId);
+ if (!success || obj == null)
+ return;
+ // Get parent info from MediaProvider, since the id is different from MTP's
ContentValues values = new ContentValues();
- values.put(Files.FileColumns.DATA, newPath);
- values.put(Files.FileColumns.PARENT, newParent);
- values.put(Files.FileColumns.STORAGE_ID, newStorage);
- int updated = 0;
+ Path path = newParentObj.getPath().resolve(name);
+ Path oldPath = oldParentObj.getPath().resolve(name);
+ values.put(Files.FileColumns.DATA, path.toString());
+ if (obj.getParent().isRoot()) {
+ values.put(Files.FileColumns.PARENT, 0);
+ } else {
+ int parentId = findInMedia(path.getParent());
+ if (parentId != -1) {
+ values.put(Files.FileColumns.PARENT, parentId);
+ } else {
+ // The new parent isn't in MediaProvider, so delete the object instead
+ deleteFromMedia(oldPath, obj.isDir());
+ return;
+ }
+ }
+ // update MediaProvider
+ Cursor c = null;
+ String[] whereArgs = new String[]{oldPath.toString()};
try {
- // note - we are relying on a special case in MediaProvider.update() to update
- // the paths for all children in the case where this is a directory.
- updated = mMediaProvider.update(mObjectsUri, values, ID_WHERE, whereArgs);
+ int parentId = -1;
+ if (!oldParentObj.isRoot()) {
+ parentId = findInMedia(oldPath.getParent());
+ }
+ if (oldParentObj.isRoot() || parentId != -1) {
+ // Old parent exists in MediaProvider - perform a move
+ // note - we are relying on a special case in MediaProvider.update() to update
+ // the paths for all children in the case where this is a directory.
+ mMediaProvider.update(mObjectsUri, values, PATH_WHERE, whereArgs);
+ } else {
+ // Old parent doesn't exist - add the object
+ values.put(Files.FileColumns.FORMAT, obj.getFormat());
+ values.put(Files.FileColumns.SIZE, obj.getSize());
+ values.put(Files.FileColumns.DATE_MODIFIED, obj.getModifiedTime());
+ Uri uri = mMediaProvider.insert(mObjectsUri, values);
+ if (uri != null) {
+ rescanFile(path.toString(),
+ Integer.parseInt(uri.getPathSegments().get(2)), obj.getFormat());
+ }
+ }
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in mMediaProvider.update", e);
}
- if (updated == 0) {
- Log.e(TAG, "Unable to update path for " + handle + " to " + newPath);
- return MtpConstants.RESPONSE_GENERAL_ERROR;
+ }
+
+ private int beginCopyObject(int handle, int newParent, int newStorage) {
+ MtpStorageManager.MtpObject obj = mManager.getObject(handle);
+ MtpStorageManager.MtpObject parent = newParent == 0 ?
+ mManager.getStorageRoot(newStorage) : mManager.getObject(newParent);
+ if (obj == null || parent == null)
+ return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+ return mManager.beginCopyObject(obj, parent);
+ }
+
+ private void endCopyObject(int handle, boolean success) {
+ MtpStorageManager.MtpObject obj = mManager.getObject(handle);
+ if (obj == null || !mManager.endCopyObject(obj, success)) {
+ Log.e(TAG, "Failed to end copy object");
+ return;
}
- return MtpConstants.RESPONSE_OK;
+ if (!success) {
+ return;
+ }
+ String path = obj.getPath().toString();
+ int format = obj.getFormat();
+ // Get parent info from MediaProvider, since the id is different from MTP's
+ ContentValues values = new ContentValues();
+ values.put(Files.FileColumns.DATA, path);
+ values.put(Files.FileColumns.FORMAT, format);
+ values.put(Files.FileColumns.SIZE, obj.getSize());
+ values.put(Files.FileColumns.DATE_MODIFIED, obj.getModifiedTime());
+ try {
+ if (obj.getParent().isRoot()) {
+ values.put(Files.FileColumns.PARENT, 0);
+ } else {
+ int parentId = findInMedia(obj.getParent().getPath());
+ if (parentId != -1) {
+ values.put(Files.FileColumns.PARENT, parentId);
+ } else {
+ // The parent isn't in MediaProvider. Don't add the new file.
+ return;
+ }
+ }
+ if (obj.isDir()) {
+ mMediaScanner.scanDirectories(new String[]{path});
+ } else {
+ Uri uri = mMediaProvider.insert(mObjectsUri, values);
+ if (uri != null) {
+ rescanFile(path, Integer.parseInt(uri.getPathSegments().get(2)), format);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in beginSendObject", e);
+ }
}
private int setObjectProperty(int handle, int property,
- long intValue, String stringValue) {
+ long intValue, String stringValue) {
switch (property) {
case MtpConstants.PROPERTY_OBJECT_FILE_NAME:
return renameFile(handle, stringValue);
@@ -912,24 +748,23 @@
value.getChars(0, length, outStringValue, 0);
outStringValue[length] = 0;
return MtpConstants.RESPONSE_OK;
-
case MtpConstants.DEVICE_PROPERTY_IMAGE_SIZE:
// use screen size as max image size
- Display display = ((WindowManager)mContext.getSystemService(
+ Display display = ((WindowManager) mContext.getSystemService(
Context.WINDOW_SERVICE)).getDefaultDisplay();
int width = display.getMaximumSizeDimension();
int height = display.getMaximumSizeDimension();
- String imageSize = Integer.toString(width) + "x" + Integer.toString(height);
+ String imageSize = Integer.toString(width) + "x" + Integer.toString(height);
imageSize.getChars(0, imageSize.length(), outStringValue, 0);
outStringValue[imageSize.length()] = 0;
return MtpConstants.RESPONSE_OK;
-
case MtpConstants.DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE:
outIntValue[0] = mDeviceType;
return MtpConstants.RESPONSE_OK;
-
- // DEVICE_PROPERTY_BATTERY_LEVEL is implemented in the JNI code
-
+ case MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL:
+ outIntValue[0] = mBatteryLevel;
+ outIntValue[1] = mBatteryScale;
+ return MtpConstants.RESPONSE_OK;
default:
return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
}
@@ -950,179 +785,144 @@
}
private boolean getObjectInfo(int handle, int[] outStorageFormatParent,
- char[] outName, long[] outCreatedModified) {
- Cursor c = null;
- try {
- c = mMediaProvider.query(mObjectsUri, OBJECT_INFO_PROJECTION,
- ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
- if (c != null && c.moveToNext()) {
- outStorageFormatParent[0] = c.getInt(1);
- outStorageFormatParent[1] = c.getInt(2);
- outStorageFormatParent[2] = c.getInt(3);
-
- // extract name from path
- String path = c.getString(4);
- int lastSlash = path.lastIndexOf('/');
- int start = (lastSlash >= 0 ? lastSlash + 1 : 0);
- int end = path.length();
- if (end - start > 255) {
- end = start + 255;
- }
- path.getChars(start, end, outName, 0);
- outName[end - start] = 0;
-
- outCreatedModified[0] = c.getLong(5);
- outCreatedModified[1] = c.getLong(6);
- // use modification date as creation date if date added is not set
- if (outCreatedModified[0] == 0) {
- outCreatedModified[0] = outCreatedModified[1];
- }
- return true;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in getObjectInfo", e);
- } finally {
- if (c != null) {
- c.close();
- }
+ char[] outName, long[] outCreatedModified) {
+ MtpStorageManager.MtpObject obj = mManager.getObject(handle);
+ if (obj == null) {
+ return false;
}
- return false;
+ outStorageFormatParent[0] = obj.getStorageId();
+ outStorageFormatParent[1] = obj.getFormat();
+ outStorageFormatParent[2] = obj.getParent().isRoot() ? 0 : obj.getParent().getId();
+
+ int nameLen = Integer.min(obj.getName().length(), 255);
+ obj.getName().getChars(0, nameLen, outName, 0);
+ outName[nameLen] = 0;
+
+ outCreatedModified[0] = obj.getModifiedTime();
+ outCreatedModified[1] = obj.getModifiedTime();
+ return true;
}
private int getObjectFilePath(int handle, char[] outFilePath, long[] outFileLengthFormat) {
- if (handle == 0) {
- // special case root directory
- mMediaStoragePath.getChars(0, mMediaStoragePath.length(), outFilePath, 0);
- outFilePath[mMediaStoragePath.length()] = 0;
- outFileLengthFormat[0] = 0;
- outFileLengthFormat[1] = MtpConstants.FORMAT_ASSOCIATION;
- return MtpConstants.RESPONSE_OK;
+ MtpStorageManager.MtpObject obj = mManager.getObject(handle);
+ if (obj == null) {
+ return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
}
- Cursor c = null;
- try {
- c = mMediaProvider.query(mObjectsUri, PATH_FORMAT_PROJECTION,
- ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
- if (c != null && c.moveToNext()) {
- String path = c.getString(1);
- path.getChars(0, path.length(), outFilePath, 0);
- outFilePath[path.length()] = 0;
- // File transfers from device to host will likely fail if the size is incorrect.
- // So to be safe, use the actual file size here.
- outFileLengthFormat[0] = new File(path).length();
- outFileLengthFormat[1] = c.getLong(2);
- return MtpConstants.RESPONSE_OK;
- } else {
- return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in getObjectFilePath", e);
- return MtpConstants.RESPONSE_GENERAL_ERROR;
- } finally {
- if (c != null) {
- c.close();
- }
- }
+
+ String path = obj.getPath().toString();
+ int pathLen = Integer.min(path.length(), 4096);
+ path.getChars(0, pathLen, outFilePath, 0);
+ outFilePath[pathLen] = 0;
+
+ outFileLengthFormat[0] = obj.getSize();
+ outFileLengthFormat[1] = obj.getFormat();
+ return MtpConstants.RESPONSE_OK;
}
private int getObjectFormat(int handle) {
- Cursor c = null;
- try {
- c = mMediaProvider.query(mObjectsUri, FORMAT_PROJECTION,
- ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
- if (c != null && c.moveToNext()) {
- return c.getInt(1);
- } else {
- return -1;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in getObjectFilePath", e);
+ MtpStorageManager.MtpObject obj = mManager.getObject(handle);
+ if (obj == null) {
return -1;
- } finally {
- if (c != null) {
- c.close();
- }
}
+ return obj.getFormat();
}
- private int deleteFile(int handle) {
- mDatabaseModified = true;
- String path = null;
- int format = 0;
+ private int beginDeleteObject(int handle) {
+ MtpStorageManager.MtpObject obj = mManager.getObject(handle);
+ if (obj == null) {
+ return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+ }
+ if (!mManager.beginRemoveObject(obj)) {
+ return MtpConstants.RESPONSE_GENERAL_ERROR;
+ }
+ return MtpConstants.RESPONSE_OK;
+ }
+ private void endDeleteObject(int handle, boolean success) {
+ MtpStorageManager.MtpObject obj = mManager.getObject(handle);
+ if (obj == null) {
+ return;
+ }
+ if (!mManager.endRemoveObject(obj, success))
+ Log.e(TAG, "Failed to end remove object");
+ if (success)
+ deleteFromMedia(obj.getPath(), obj.isDir());
+ }
+
+ private int findInMedia(Path path) {
+ int ret = -1;
Cursor c = null;
try {
- c = mMediaProvider.query(mObjectsUri, PATH_FORMAT_PROJECTION,
- ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
+ c = mMediaProvider.query(mObjectsUri, ID_PROJECTION, PATH_WHERE,
+ new String[]{path.toString()}, null, null);
if (c != null && c.moveToNext()) {
- // don't convert to media path here, since we will be matching
- // against paths in the database matching /data/media
- path = c.getString(1);
- format = c.getInt(2);
- } else {
- return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+ ret = c.getInt(0);
}
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error finding " + path + " in MediaProvider");
+ } finally {
+ if (c != null)
+ c.close();
+ }
+ return ret;
+ }
- if (path == null || format == 0) {
- return MtpConstants.RESPONSE_GENERAL_ERROR;
- }
-
- // do not allow deleting any of the special subdirectories
- if (isStorageSubDirectory(path)) {
- return MtpConstants.RESPONSE_OBJECT_WRITE_PROTECTED;
- }
-
- if (format == MtpConstants.FORMAT_ASSOCIATION) {
+ private void deleteFromMedia(Path path, boolean isDir) {
+ try {
+ // Delete the object(s) from MediaProvider, but ignore errors.
+ if (isDir) {
// recursive case - delete all children first
- Uri uri = Files.getMtpObjectsUri(mVolumeName);
- int count = mMediaProvider.delete(uri,
- // the 'like' makes it use the index, the 'lower()' makes it correct
- // when the path contains sqlite wildcard characters
- "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
- new String[] { path + "/%",Integer.toString(path.length() + 1), path + "/"});
+ mMediaProvider.delete(mObjectsUri,
+ // the 'like' makes it use the index, the 'lower()' makes it correct
+ // when the path contains sqlite wildcard characters
+ "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
+ new String[]{path + "/%", Integer.toString(path.toString().length() + 1),
+ path.toString() + "/"});
}
- Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
- if (mMediaProvider.delete(uri, null, null) > 0) {
- if (format != MtpConstants.FORMAT_ASSOCIATION
- && path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
+ String[] whereArgs = new String[]{path.toString()};
+ if (mMediaProvider.delete(mObjectsUri, PATH_WHERE, whereArgs) > 0) {
+ if (!isDir && path.toString().toLowerCase(Locale.US).endsWith(NO_MEDIA)) {
try {
- String parentPath = path.substring(0, path.lastIndexOf("/"));
+ String parentPath = path.getParent().toString();
mMediaProvider.call(MediaStore.UNHIDE_CALL, parentPath, null);
} catch (RemoteException e) {
Log.e(TAG, "failed to unhide/rescan for " + path);
}
}
- return MtpConstants.RESPONSE_OK;
} else {
- return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+ Log.i(TAG, "Mediaprovider didn't delete " + path);
}
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in deleteFile", e);
- return MtpConstants.RESPONSE_GENERAL_ERROR;
- } finally {
- if (c != null) {
- c.close();
- }
+ } catch (Exception e) {
+ Log.d(TAG, "Failed to delete " + path + " from MediaProvider");
}
}
private int[] getObjectReferences(int handle) {
+ MtpStorageManager.MtpObject obj = mManager.getObject(handle);
+ if (obj == null)
+ return null;
+ // Translate this handle to the MediaProvider Handle
+ handle = findInMedia(obj.getPath());
+ if (handle == -1)
+ return null;
Uri uri = Files.getMtpReferencesUri(mVolumeName, handle);
Cursor c = null;
try {
- c = mMediaProvider.query(uri, ID_PROJECTION, null, null, null, null);
+ c = mMediaProvider.query(uri, PATH_PROJECTION, null, null, null, null);
if (c == null) {
return null;
}
- int count = c.getCount();
- if (count > 0) {
- int[] result = new int[count];
- for (int i = 0; i < count; i++) {
- c.moveToNext();
- result[i] = c.getInt(0);
+ ArrayList<Integer> result = new ArrayList<>();
+ while (c.moveToNext()) {
+ // Translate result handles back into handles for this session.
+ String refPath = c.getString(0);
+ MtpStorageManager.MtpObject refObj = mManager.getByPath(refPath);
+ if (refObj != null) {
+ result.add(refObj.getId());
+ }
}
- return result;
- }
+ return result.stream().mapToInt(Integer::intValue).toArray();
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in getObjectList", e);
} finally {
@@ -1134,17 +934,29 @@
}
private int setObjectReferences(int handle, int[] references) {
- mDatabaseModified = true;
+ MtpStorageManager.MtpObject obj = mManager.getObject(handle);
+ if (obj == null)
+ return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+ // Translate this handle to the MediaProvider Handle
+ handle = findInMedia(obj.getPath());
+ if (handle == -1)
+ return MtpConstants.RESPONSE_GENERAL_ERROR;
Uri uri = Files.getMtpReferencesUri(mVolumeName, handle);
- int count = references.length;
- ContentValues[] valuesList = new ContentValues[count];
- for (int i = 0; i < count; i++) {
+ ArrayList<ContentValues> valuesList = new ArrayList<>();
+ for (int id : references) {
+ // Translate each reference id to the MediaProvider Id
+ MtpStorageManager.MtpObject refObj = mManager.getObject(id);
+ if (refObj == null)
+ continue;
+ int refHandle = findInMedia(refObj.getPath());
+ if (refHandle == -1)
+ continue;
ContentValues values = new ContentValues();
- values.put(Files.FileColumns._ID, references[i]);
- valuesList[i] = values;
+ values.put(Files.FileColumns._ID, refHandle);
+ valuesList.add(values);
}
try {
- if (mMediaProvider.bulkInsert(uri, valuesList) > 0) {
+ if (mMediaProvider.bulkInsert(uri, valuesList.toArray(new ContentValues[0])) > 0) {
return MtpConstants.RESPONSE_OK;
}
} catch (RemoteException e) {
@@ -1153,17 +965,6 @@
return MtpConstants.RESPONSE_GENERAL_ERROR;
}
- private void sessionStarted() {
- mDatabaseModified = false;
- }
-
- private void sessionEnded() {
- if (mDatabaseModified) {
- mUserContext.sendBroadcast(new Intent(MediaStore.ACTION_MTP_SESSION_END));
- mDatabaseModified = false;
- }
- }
-
// used by the JNI code
private long mNativeContext;
diff --git a/media/java/android/mtp/MtpPropertyGroup.java b/media/java/android/mtp/MtpPropertyGroup.java
index dea3008..77d0f34f 100644
--- a/media/java/android/mtp/MtpPropertyGroup.java
+++ b/media/java/android/mtp/MtpPropertyGroup.java
@@ -23,22 +23,21 @@
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Files;
import android.provider.MediaStore.Images;
-import android.provider.MediaStore.MediaColumns;
import android.util.Log;
import java.util.ArrayList;
+/**
+ * MtpPropertyGroup represents a list of MTP properties.
+ * {@hide}
+ */
class MtpPropertyGroup {
-
- private static final String TAG = "MtpPropertyGroup";
+ private static final String TAG = MtpPropertyGroup.class.getSimpleName();
private class Property {
- // MTP property code
- int code;
- // MTP data type
- int type;
- // column index for our query
- int column;
+ int code;
+ int type;
+ int column;
Property(int code, int type, int column) {
this.code = code;
@@ -47,32 +46,26 @@
}
}
- private final MtpDatabase mDatabase;
private final ContentProviderClient mProvider;
private final String mVolumeName;
private final Uri mUri;
// list of all properties in this group
- private final Property[] mProperties;
+ private final Property[] mProperties;
// list of columns for database query
- private String[] mColumns;
+ private String[] mColumns;
- private static final String ID_WHERE = Files.FileColumns._ID + "=?";
- private static final String FORMAT_WHERE = Files.FileColumns.FORMAT + "=?";
- private static final String ID_FORMAT_WHERE = ID_WHERE + " AND " + FORMAT_WHERE;
- private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
- private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND " + FORMAT_WHERE;
+ private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
+
// constructs a property group for a list of properties
- public MtpPropertyGroup(MtpDatabase database, ContentProviderClient provider, String volumeName,
- int[] properties) {
- mDatabase = database;
+ public MtpPropertyGroup(ContentProviderClient provider, String volumeName, int[] properties) {
mProvider = provider;
mVolumeName = volumeName;
mUri = Files.getMtpObjectsUri(volumeName);
int count = properties.length;
- ArrayList<String> columns = new ArrayList<String>(count);
+ ArrayList<String> columns = new ArrayList<>(count);
columns.add(Files.FileColumns._ID);
mProperties = new Property[count];
@@ -90,37 +83,29 @@
String column = null;
int type;
- switch (code) {
+ switch (code) {
case MtpConstants.PROPERTY_STORAGE_ID:
- column = Files.FileColumns.STORAGE_ID;
type = MtpConstants.TYPE_UINT32;
break;
- case MtpConstants.PROPERTY_OBJECT_FORMAT:
- column = Files.FileColumns.FORMAT;
+ case MtpConstants.PROPERTY_OBJECT_FORMAT:
type = MtpConstants.TYPE_UINT16;
break;
case MtpConstants.PROPERTY_PROTECTION_STATUS:
- // protection status is always 0
type = MtpConstants.TYPE_UINT16;
break;
case MtpConstants.PROPERTY_OBJECT_SIZE:
- column = Files.FileColumns.SIZE;
type = MtpConstants.TYPE_UINT64;
break;
case MtpConstants.PROPERTY_OBJECT_FILE_NAME:
- column = Files.FileColumns.DATA;
type = MtpConstants.TYPE_STR;
break;
case MtpConstants.PROPERTY_NAME:
- column = MediaColumns.TITLE;
type = MtpConstants.TYPE_STR;
break;
case MtpConstants.PROPERTY_DATE_MODIFIED:
- column = Files.FileColumns.DATE_MODIFIED;
type = MtpConstants.TYPE_STR;
break;
case MtpConstants.PROPERTY_DATE_ADDED:
- column = Files.FileColumns.DATE_ADDED;
type = MtpConstants.TYPE_STR;
break;
case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE:
@@ -128,12 +113,9 @@
type = MtpConstants.TYPE_STR;
break;
case MtpConstants.PROPERTY_PARENT_OBJECT:
- column = Files.FileColumns.PARENT;
type = MtpConstants.TYPE_UINT32;
break;
case MtpConstants.PROPERTY_PERSISTENT_UID:
- // PUID is concatenation of storageID and object handle
- column = Files.FileColumns.STORAGE_ID;
type = MtpConstants.TYPE_UINT128;
break;
case MtpConstants.PROPERTY_DURATION:
@@ -145,7 +127,6 @@
type = MtpConstants.TYPE_UINT16;
break;
case MtpConstants.PROPERTY_DISPLAY_NAME:
- column = MediaColumns.DISPLAY_NAME;
type = MtpConstants.TYPE_STR;
break;
case MtpConstants.PROPERTY_ARTIST:
@@ -195,40 +176,19 @@
}
}
- private String queryString(int id, String column) {
- Cursor c = null;
- try {
- // for now we are only reading properties from the "objects" table
- c = mProvider.query(mUri,
- new String [] { Files.FileColumns._ID, column },
- ID_WHERE, new String[] { Integer.toString(id) }, null, null);
- if (c != null && c.moveToNext()) {
- return c.getString(1);
- } else {
- return "";
- }
- } catch (Exception e) {
- return null;
- } finally {
- if (c != null) {
- c.close();
- }
- }
- }
-
- private String queryAudio(int id, String column) {
+ private String queryAudio(String path, String column) {
Cursor c = null;
try {
c = mProvider.query(Audio.Media.getContentUri(mVolumeName),
- new String [] { Files.FileColumns._ID, column },
- ID_WHERE, new String[] { Integer.toString(id) }, null, null);
+ new String [] { column },
+ PATH_WHERE, new String[] {path}, null, null);
if (c != null && c.moveToNext()) {
- return c.getString(1);
+ return c.getString(0);
} else {
return "";
}
} catch (Exception e) {
- return null;
+ return "";
} finally {
if (c != null) {
c.close();
@@ -236,21 +196,19 @@
}
}
- private String queryGenre(int id) {
+ private String queryGenre(String path) {
Cursor c = null;
try {
- Uri uri = Audio.Genres.getContentUriForAudioId(mVolumeName, id);
- c = mProvider.query(uri,
- new String [] { Files.FileColumns._ID, Audio.GenresColumns.NAME },
- null, null, null, null);
+ c = mProvider.query(Audio.Genres.getContentUri(mVolumeName),
+ new String [] { Audio.GenresColumns.NAME },
+ PATH_WHERE, new String[] {path}, null, null);
if (c != null && c.moveToNext()) {
- return c.getString(1);
+ return c.getString(0);
} else {
return "";
}
} catch (Exception e) {
- Log.e(TAG, "queryGenre exception", e);
- return null;
+ return "";
} finally {
if (c != null) {
c.close();
@@ -258,211 +216,127 @@
}
}
- private Long queryLong(int id, String column) {
+ /**
+ * Gets the values of the properties represented by this property group for the given
+ * object and adds them to the given property list.
+ * @return Response_OK if the operation succeeded.
+ */
+ public int getPropertyList(MtpStorageManager.MtpObject object, MtpPropertyList list) {
Cursor c = null;
- try {
- // for now we are only reading properties from the "objects" table
- c = mProvider.query(mUri,
- new String [] { Files.FileColumns._ID, column },
- ID_WHERE, new String[] { Integer.toString(id) }, null, null);
- if (c != null && c.moveToNext()) {
- return new Long(c.getLong(1));
- }
- } catch (Exception e) {
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return null;
- }
-
- private static String nameFromPath(String path) {
- // extract name from full path
- int start = 0;
- int lastSlash = path.lastIndexOf('/');
- if (lastSlash >= 0) {
- start = lastSlash + 1;
- }
- int end = path.length();
- if (end - start > 255) {
- end = start + 255;
- }
- return path.substring(start, end);
- }
-
- MtpPropertyList getPropertyList(int handle, int format, int depth) {
- //Log.d(TAG, "getPropertyList handle: " + handle + " format: " + format + " depth: " + depth);
- if (depth > 1) {
- // we only support depth 0 and 1
- // depth 0: single object, depth 1: immediate children
- return new MtpPropertyList(0, MtpConstants.RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED);
- }
-
- String where;
- String[] whereArgs;
- if (format == 0) {
- if (handle == 0xFFFFFFFF) {
- // select all objects
- where = null;
- whereArgs = null;
- } else {
- whereArgs = new String[] { Integer.toString(handle) };
- if (depth == 1) {
- where = PARENT_WHERE;
- } else {
- where = ID_WHERE;
+ int id = object.getId();
+ String path = object.getPath().toString();
+ for (Property property : mProperties) {
+ if (property.column != -1 && c == null) {
+ try {
+ // Look up the entry in MediaProvider only if one of those properties is needed.
+ c = mProvider.query(mUri, mColumns,
+ PATH_WHERE, new String[] {path}, null, null);
+ if (c != null && !c.moveToNext()) {
+ c.close();
+ c = null;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Mediaprovider lookup failed");
}
}
- } else {
- if (handle == 0xFFFFFFFF) {
- // select all objects with given format
- where = FORMAT_WHERE;
- whereArgs = new String[] { Integer.toString(format) };
- } else {
- whereArgs = new String[] { Integer.toString(handle), Integer.toString(format) };
- if (depth == 1) {
- where = PARENT_FORMAT_WHERE;
- } else {
- where = ID_FORMAT_WHERE;
- }
- }
- }
-
- Cursor c = null;
- try {
- // don't query if not necessary
- if (depth > 0 || handle == 0xFFFFFFFF || mColumns.length > 1) {
- c = mProvider.query(mUri, mColumns, where, whereArgs, null, null);
- if (c == null) {
- return new MtpPropertyList(0, MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
- }
- }
-
- int count = (c == null ? 1 : c.getCount());
- MtpPropertyList result = new MtpPropertyList(count * mProperties.length,
- MtpConstants.RESPONSE_OK);
-
- // iterate over all objects in the query
- for (int objectIndex = 0; objectIndex < count; objectIndex++) {
- if (c != null) {
- c.moveToNext();
- handle = (int)c.getLong(0);
- }
-
- // iterate over all properties in the query for the given object
- for (int propertyIndex = 0; propertyIndex < mProperties.length; propertyIndex++) {
- Property property = mProperties[propertyIndex];
- int propertyCode = property.code;
- int column = property.column;
-
- // handle some special cases
- switch (propertyCode) {
- case MtpConstants.PROPERTY_PROTECTION_STATUS:
- // protection status is always 0
- result.append(handle, propertyCode, MtpConstants.TYPE_UINT16, 0);
+ switch (property.code) {
+ case MtpConstants.PROPERTY_PROTECTION_STATUS:
+ // protection status is always 0
+ list.append(id, property.code, property.type, 0);
+ break;
+ case MtpConstants.PROPERTY_NAME:
+ case MtpConstants.PROPERTY_OBJECT_FILE_NAME:
+ case MtpConstants.PROPERTY_DISPLAY_NAME:
+ list.append(id, property.code, object.getName());
+ break;
+ case MtpConstants.PROPERTY_DATE_MODIFIED:
+ case MtpConstants.PROPERTY_DATE_ADDED:
+ // convert from seconds to DateTime
+ list.append(id, property.code,
+ format_date_time(object.getModifiedTime()));
+ break;
+ case MtpConstants.PROPERTY_STORAGE_ID:
+ list.append(id, property.code, property.type, object.getStorageId());
+ break;
+ case MtpConstants.PROPERTY_OBJECT_FORMAT:
+ list.append(id, property.code, property.type, object.getFormat());
+ break;
+ case MtpConstants.PROPERTY_OBJECT_SIZE:
+ list.append(id, property.code, property.type, object.getSize());
+ break;
+ case MtpConstants.PROPERTY_PARENT_OBJECT:
+ list.append(id, property.code, property.type,
+ object.getParent().isRoot() ? 0 : object.getParent().getId());
+ break;
+ case MtpConstants.PROPERTY_PERSISTENT_UID:
+ // The persistent uid must be unique and never reused among all objects,
+ // and remain the same between sessions.
+ long puid = (object.getPath().toString().hashCode() << 32)
+ + object.getModifiedTime();
+ list.append(id, property.code, property.type, puid);
+ break;
+ case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE:
+ // release date is stored internally as just the year
+ int year = 0;
+ if (c != null)
+ year = c.getInt(property.column);
+ String dateTime = Integer.toString(year) + "0101T000000";
+ list.append(id, property.code, dateTime);
+ break;
+ case MtpConstants.PROPERTY_TRACK:
+ int track = 0;
+ if (c != null)
+ track = c.getInt(property.column);
+ list.append(id, property.code, MtpConstants.TYPE_UINT16,
+ track % 1000);
+ break;
+ case MtpConstants.PROPERTY_ARTIST:
+ list.append(id, property.code,
+ queryAudio(path, Audio.AudioColumns.ARTIST));
+ break;
+ case MtpConstants.PROPERTY_ALBUM_NAME:
+ list.append(id, property.code,
+ queryAudio(path, Audio.AudioColumns.ALBUM));
+ break;
+ case MtpConstants.PROPERTY_GENRE:
+ String genre = queryGenre(path);
+ if (genre != null) {
+ list.append(id, property.code, genre);
+ }
+ break;
+ case MtpConstants.PROPERTY_AUDIO_WAVE_CODEC:
+ case MtpConstants.PROPERTY_AUDIO_BITRATE:
+ case MtpConstants.PROPERTY_SAMPLE_RATE:
+ // we don't have these in our database, so return 0
+ list.append(id, property.code, MtpConstants.TYPE_UINT32, 0);
+ break;
+ case MtpConstants.PROPERTY_BITRATE_TYPE:
+ case MtpConstants.PROPERTY_NUMBER_OF_CHANNELS:
+ // we don't have these in our database, so return 0
+ list.append(id, property.code, MtpConstants.TYPE_UINT16, 0);
+ break;
+ default:
+ switch(property.type) {
+ case MtpConstants.TYPE_UNDEFINED:
+ list.append(id, property.code, property.type, 0);
break;
- case MtpConstants.PROPERTY_OBJECT_FILE_NAME:
- // special case - need to extract file name from full path
- String value = c.getString(column);
- if (value != null) {
- result.append(handle, propertyCode, nameFromPath(value));
- } else {
- result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
- }
- break;
- case MtpConstants.PROPERTY_NAME:
- // first try title
- String name = c.getString(column);
- // then try name
- if (name == null) {
- name = queryString(handle, Audio.PlaylistsColumns.NAME);
- }
- // if title and name fail, extract name from full path
- if (name == null) {
- name = queryString(handle, Files.FileColumns.DATA);
- if (name != null) {
- name = nameFromPath(name);
- }
- }
- if (name != null) {
- result.append(handle, propertyCode, name);
- } else {
- result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
- }
- break;
- case MtpConstants.PROPERTY_DATE_MODIFIED:
- case MtpConstants.PROPERTY_DATE_ADDED:
- // convert from seconds to DateTime
- result.append(handle, propertyCode, format_date_time(c.getInt(column)));
- break;
- case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE:
- // release date is stored internally as just the year
- int year = c.getInt(column);
- String dateTime = Integer.toString(year) + "0101T000000";
- result.append(handle, propertyCode, dateTime);
- break;
- case MtpConstants.PROPERTY_PERSISTENT_UID:
- // PUID is concatenation of storageID and object handle
- long puid = c.getLong(column);
- puid <<= 32;
- puid += handle;
- result.append(handle, propertyCode, MtpConstants.TYPE_UINT128, puid);
- break;
- case MtpConstants.PROPERTY_TRACK:
- result.append(handle, propertyCode, MtpConstants.TYPE_UINT16,
- c.getInt(column) % 1000);
- break;
- case MtpConstants.PROPERTY_ARTIST:
- result.append(handle, propertyCode,
- queryAudio(handle, Audio.AudioColumns.ARTIST));
- break;
- case MtpConstants.PROPERTY_ALBUM_NAME:
- result.append(handle, propertyCode,
- queryAudio(handle, Audio.AudioColumns.ALBUM));
- break;
- case MtpConstants.PROPERTY_GENRE:
- String genre = queryGenre(handle);
- if (genre != null) {
- result.append(handle, propertyCode, genre);
- } else {
- result.setResult(MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE);
- }
- break;
- case MtpConstants.PROPERTY_AUDIO_WAVE_CODEC:
- case MtpConstants.PROPERTY_AUDIO_BITRATE:
- case MtpConstants.PROPERTY_SAMPLE_RATE:
- // we don't have these in our database, so return 0
- result.append(handle, propertyCode, MtpConstants.TYPE_UINT32, 0);
- break;
- case MtpConstants.PROPERTY_BITRATE_TYPE:
- case MtpConstants.PROPERTY_NUMBER_OF_CHANNELS:
- // we don't have these in our database, so return 0
- result.append(handle, propertyCode, MtpConstants.TYPE_UINT16, 0);
+ case MtpConstants.TYPE_STR:
+ String value = "";
+ if (c != null)
+ value = c.getString(property.column);
+ list.append(id, property.code, value);
break;
default:
- if (property.type == MtpConstants.TYPE_STR) {
- result.append(handle, propertyCode, c.getString(column));
- } else if (property.type == MtpConstants.TYPE_UNDEFINED) {
- result.append(handle, propertyCode, property.type, 0);
- } else {
- result.append(handle, propertyCode, property.type,
- c.getLong(column));
- }
- break;
+ long longValue = 0L;
+ if (c != null)
+ longValue = c.getLong(property.column);
+ list.append(id, property.code, property.type, longValue);
}
- }
- }
-
- return result;
- } catch (RemoteException e) {
- return new MtpPropertyList(0, MtpConstants.RESPONSE_GENERAL_ERROR);
- } finally {
- if (c != null) {
- c.close();
}
}
- // impossible to get here, so no return statement
+ if (c != null)
+ c.close();
+ return MtpConstants.RESPONSE_OK;
}
private native String format_date_time(long seconds);
diff --git a/media/java/android/mtp/MtpPropertyList.java b/media/java/android/mtp/MtpPropertyList.java
index f9bc603..ede90da 100644
--- a/media/java/android/mtp/MtpPropertyList.java
+++ b/media/java/android/mtp/MtpPropertyList.java
@@ -16,6 +16,9 @@
package android.mtp;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Encapsulates the ObjectPropList dataset used by the GetObjectPropList command.
* The fields of this class are read by JNI code in android_media_MtpDatabase.cpp
@@ -23,56 +26,70 @@
class MtpPropertyList {
- // number of results returned
- private int mCount;
- // maximum number of results
- private final int mMaxCount;
- // result code for GetObjectPropList
- public int mResult;
// list of object handles (first field in quadruplet)
- public final int[] mObjectHandles;
- // list of object propery codes (second field in quadruplet)
- public final int[] mPropertyCodes;
+ private List<Integer> mObjectHandles;
+ // list of object property codes (second field in quadruplet)
+ private List<Integer> mPropertyCodes;
// list of data type codes (third field in quadruplet)
- public final int[] mDataTypes;
+ private List<Integer> mDataTypes;
// list of long int property values (fourth field in quadruplet, when value is integer type)
- public long[] mLongValues;
+ private List<Long> mLongValues;
// list of long int property values (fourth field in quadruplet, when value is string type)
- public String[] mStringValues;
+ private List<String> mStringValues;
- // constructor only called from MtpDatabase
- public MtpPropertyList(int maxCount, int result) {
- mMaxCount = maxCount;
- mResult = result;
- mObjectHandles = new int[maxCount];
- mPropertyCodes = new int[maxCount];
- mDataTypes = new int[maxCount];
- // mLongValues and mStringValues are created lazily since both might not be necessary
+ // Return value of this operation
+ private int mCode;
+
+ public MtpPropertyList(int code) {
+ mCode = code;
+ mObjectHandles = new ArrayList<>();
+ mPropertyCodes = new ArrayList<>();
+ mDataTypes = new ArrayList<>();
+ mLongValues = new ArrayList<>();
+ mStringValues = new ArrayList<>();
}
public void append(int handle, int property, int type, long value) {
- int index = mCount++;
- if (mLongValues == null) {
- mLongValues = new long[mMaxCount];
- }
- mObjectHandles[index] = handle;
- mPropertyCodes[index] = property;
- mDataTypes[index] = type;
- mLongValues[index] = value;
+ mObjectHandles.add(handle);
+ mPropertyCodes.add(property);
+ mDataTypes.add(type);
+ mLongValues.add(value);
+ mStringValues.add(null);
}
public void append(int handle, int property, String value) {
- int index = mCount++;
- if (mStringValues == null) {
- mStringValues = new String[mMaxCount];
- }
- mObjectHandles[index] = handle;
- mPropertyCodes[index] = property;
- mDataTypes[index] = MtpConstants.TYPE_STR;
- mStringValues[index] = value;
+ mObjectHandles.add(handle);
+ mPropertyCodes.add(property);
+ mDataTypes.add(MtpConstants.TYPE_STR);
+ mStringValues.add(value);
+ mLongValues.add(0L);
}
- public void setResult(int result) {
- mResult = result;
+ public int getCode() {
+ return mCode;
+ }
+
+ public int getCount() {
+ return mObjectHandles.size();
+ }
+
+ public int[] getObjectHandles() {
+ return mObjectHandles.stream().mapToInt(Integer::intValue).toArray();
+ }
+
+ public int[] getPropertyCodes() {
+ return mPropertyCodes.stream().mapToInt(Integer::intValue).toArray();
+ }
+
+ public int[] getDataTypes() {
+ return mDataTypes.stream().mapToInt(Integer::intValue).toArray();
+ }
+
+ public long[] getLongValues() {
+ return mLongValues.stream().mapToLong(Long::longValue).toArray();
+ }
+
+ public String[] getStringValues() {
+ return mStringValues.toArray(new String[0]);
}
}
diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java
index 6ca442c..c72b827 100644
--- a/media/java/android/mtp/MtpStorage.java
+++ b/media/java/android/mtp/MtpStorage.java
@@ -31,15 +31,13 @@
private final int mStorageId;
private final String mPath;
private final String mDescription;
- private final long mReserveSpace;
private final boolean mRemovable;
private final long mMaxFileSize;
- public MtpStorage(StorageVolume volume, Context context) {
- mStorageId = volume.getStorageId();
+ public MtpStorage(StorageVolume volume, int storageId) {
+ mStorageId = storageId;
mPath = volume.getPath();
- mDescription = volume.getDescription(context);
- mReserveSpace = volume.getMtpReserveSpace() * 1024L * 1024L;
+ mDescription = volume.getDescription(null);
mRemovable = volume.isRemovable();
mMaxFileSize = volume.getMaxFileSize();
}
@@ -72,16 +70,6 @@
}
/**
- * Returns the amount of space to reserve on the storage file system.
- * This can be set to a non-zero value to prevent MTP from filling up the entire storage.
- *
- * @return reserved space in bytes.
- */
- public final long getReserveSpace() {
- return mReserveSpace;
- }
-
- /**
* Returns true if the storage is removable.
*
* @return is removable
diff --git a/media/java/android/mtp/MtpStorageManager.java b/media/java/android/mtp/MtpStorageManager.java
new file mode 100644
index 0000000..bdc8741
--- /dev/null
+++ b/media/java/android/mtp/MtpStorageManager.java
@@ -0,0 +1,1210 @@
+/*
+ * 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.mtp;
+
+import android.media.MediaFile;
+import android.os.FileObserver;
+import android.os.storage.StorageVolume;
+import android.util.Log;
+
+import java.io.IOException;
+import java.nio.file.DirectoryIteratorException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Stream;
+
+/**
+ * MtpStorageManager provides functionality for listing, tracking, and notifying MtpServer of
+ * filesystem changes. As directories are listed, this class will cache the results,
+ * and send events when objects are added/removed from cached directories.
+ * {@hide}
+ */
+public class MtpStorageManager {
+ private static final String TAG = MtpStorageManager.class.getSimpleName();
+ public static boolean sDebug = false;
+
+ // Inotify flags not provided by FileObserver
+ private static final int IN_ONLYDIR = 0x01000000;
+ private static final int IN_Q_OVERFLOW = 0x00004000;
+ private static final int IN_IGNORED = 0x00008000;
+ private static final int IN_ISDIR = 0x40000000;
+
+ private class MtpObjectObserver extends FileObserver {
+ MtpObject mObject;
+
+ MtpObjectObserver(MtpObject object) {
+ super(object.getPath().toString(),
+ MOVED_FROM | MOVED_TO | DELETE | CREATE | IN_ONLYDIR);
+ mObject = object;
+ }
+
+ @Override
+ public void onEvent(int event, String path) {
+ synchronized (MtpStorageManager.this) {
+ if ((event & IN_Q_OVERFLOW) != 0) {
+ // We are out of space in the inotify queue.
+ Log.e(TAG, "Received Inotify overflow event!");
+ }
+ MtpObject obj = mObject.getChild(path);
+ if ((event & MOVED_TO) != 0 || (event & CREATE) != 0) {
+ if (sDebug)
+ Log.i(TAG, "Got inotify added event for " + path + " " + event);
+ handleAddedObject(mObject, path, (event & IN_ISDIR) != 0);
+ } else if ((event & MOVED_FROM) != 0 || (event & DELETE) != 0) {
+ if (obj == null) {
+ Log.w(TAG, "Object was null in event " + path);
+ return;
+ }
+ if (sDebug)
+ Log.i(TAG, "Got inotify removed event for " + path + " " + event);
+ handleRemovedObject(obj);
+ } else if ((event & IN_IGNORED) != 0) {
+ if (sDebug)
+ Log.i(TAG, "inotify for " + mObject.getPath() + " deleted");
+ if (mObject.mObserver != null)
+ mObject.mObserver.stopWatching();
+ mObject.mObserver = null;
+ } else {
+ Log.w(TAG, "Got unrecognized event " + path + " " + event);
+ }
+ }
+ }
+
+ @Override
+ public void finalize() {
+ // If the server shuts down and starts up again, the new server's observers can be
+ // invalidated by the finalize() calls of the previous server's observers.
+ // Hence, disable the automatic stopWatching() call in FileObserver#finalize, and
+ // always call stopWatching() manually whenever an observer should be shut down.
+ }
+ }
+
+ /**
+ * Describes how the object is being acted on, to determine how events are handled.
+ */
+ private enum MtpObjectState {
+ NORMAL,
+ FROZEN, // Object is going to be modified in this session.
+ FROZEN_ADDED, // Object was frozen, and has been added.
+ FROZEN_REMOVED, // Object was frozen, and has been removed.
+ FROZEN_ONESHOT_ADD, // Object is waiting for single add event before being unfrozen.
+ FROZEN_ONESHOT_DEL, // Object is waiting for single remove event and will then be removed.
+ }
+
+ /**
+ * Describes the current operation being done on an object. Determines whether observers are
+ * created on new folders.
+ */
+ private enum MtpOperation {
+ NONE, // Any new folders not added as part of the session are immediately observed.
+ ADD, // New folders added as part of the session are immediately observed.
+ RENAME, // Renamed or moved folders are not immediately observed.
+ COPY, // Copied folders are immediately observed iff the original was.
+ DELETE, // Exists for debugging purposes only.
+ }
+
+ /** MtpObject represents either a file or directory in an associated storage. **/
+ public static class MtpObject {
+ // null for root objects
+ private MtpObject mParent;
+
+ private String mName;
+ private int mId;
+ private MtpObjectState mState;
+ private MtpOperation mOp;
+
+ private boolean mVisited;
+ private boolean mIsDir;
+
+ // null if not a directory
+ private HashMap<String, MtpObject> mChildren;
+ // null if not both a directory and visited
+ private FileObserver mObserver;
+
+ MtpObject(String name, int id, MtpObject parent, boolean isDir) {
+ mId = id;
+ mName = name;
+ mParent = parent;
+ mObserver = null;
+ mVisited = false;
+ mState = MtpObjectState.NORMAL;
+ mIsDir = isDir;
+ mOp = MtpOperation.NONE;
+
+ mChildren = mIsDir ? new HashMap<>() : null;
+ }
+
+ /** Public methods for getting object info **/
+
+ public String getName() {
+ return mName;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public boolean isDir() {
+ return mIsDir;
+ }
+
+ public int getFormat() {
+ return mIsDir ? MtpConstants.FORMAT_ASSOCIATION : MediaFile.getFormatCode(mName, null);
+ }
+
+ public int getStorageId() {
+ return getRoot().getId();
+ }
+
+ public long getModifiedTime() {
+ return getPath().toFile().lastModified() / 1000;
+ }
+
+ public MtpObject getParent() {
+ return mParent;
+ }
+
+ public MtpObject getRoot() {
+ return isRoot() ? this : mParent.getRoot();
+ }
+
+ public long getSize() {
+ return mIsDir ? 0 : getPath().toFile().length();
+ }
+
+ public Path getPath() {
+ return isRoot() ? Paths.get(mName) : mParent.getPath().resolve(mName);
+ }
+
+ public boolean isRoot() {
+ return mParent == null;
+ }
+
+ /** For MtpStorageManager only **/
+
+ private void setName(String name) {
+ mName = name;
+ }
+
+ private void setId(int id) {
+ mId = id;
+ }
+
+ private boolean isVisited() {
+ return mVisited;
+ }
+
+ private void setParent(MtpObject parent) {
+ mParent = parent;
+ }
+
+ private void setDir(boolean dir) {
+ if (dir != mIsDir) {
+ mIsDir = dir;
+ mChildren = mIsDir ? new HashMap<>() : null;
+ }
+ }
+
+ private void setVisited(boolean visited) {
+ mVisited = visited;
+ }
+
+ private MtpObjectState getState() {
+ return mState;
+ }
+
+ private void setState(MtpObjectState state) {
+ mState = state;
+ if (mState == MtpObjectState.NORMAL)
+ mOp = MtpOperation.NONE;
+ }
+
+ private MtpOperation getOperation() {
+ return mOp;
+ }
+
+ private void setOperation(MtpOperation op) {
+ mOp = op;
+ }
+
+ private FileObserver getObserver() {
+ return mObserver;
+ }
+
+ private void setObserver(FileObserver observer) {
+ mObserver = observer;
+ }
+
+ private void addChild(MtpObject child) {
+ mChildren.put(child.getName(), child);
+ }
+
+ private MtpObject getChild(String name) {
+ return mChildren.get(name);
+ }
+
+ private Collection<MtpObject> getChildren() {
+ return mChildren.values();
+ }
+
+ private boolean exists() {
+ return getPath().toFile().exists();
+ }
+
+ private MtpObject copy(boolean recursive) {
+ MtpObject copy = new MtpObject(mName, mId, mParent, mIsDir);
+ copy.mIsDir = mIsDir;
+ copy.mVisited = mVisited;
+ copy.mState = mState;
+ copy.mChildren = mIsDir ? new HashMap<>() : null;
+ if (recursive && mIsDir) {
+ for (MtpObject child : mChildren.values()) {
+ MtpObject childCopy = child.copy(true);
+ childCopy.setParent(copy);
+ copy.addChild(childCopy);
+ }
+ }
+ return copy;
+ }
+ }
+
+ /**
+ * A class that processes generated filesystem events.
+ */
+ public static abstract class MtpNotifier {
+ /**
+ * Called when an object is added.
+ */
+ public abstract void sendObjectAdded(int id);
+
+ /**
+ * Called when an object is deleted.
+ */
+ public abstract void sendObjectRemoved(int id);
+ }
+
+ private MtpNotifier mMtpNotifier;
+
+ // A cache of MtpObjects. The objects in the cache are keyed by object id.
+ // The root object of each storage isn't in this map since they all have ObjectId 0.
+ // Instead, they can be found in mRoots keyed by storageId.
+ private HashMap<Integer, MtpObject> mObjects;
+
+ // A cache of the root MtpObject for each storage, keyed by storage id.
+ private HashMap<Integer, MtpObject> mRoots;
+
+ // Object and Storage ids are allocated incrementally and not to be reused.
+ private int mNextObjectId;
+ private int mNextStorageId;
+
+ // Special subdirectories. When set, only return objects rooted in these directories, and do
+ // not allow them to be modified.
+ private Set<String> mSubdirectories;
+
+ private volatile boolean mCheckConsistency;
+ private Thread mConsistencyThread;
+
+ public MtpStorageManager(MtpNotifier notifier, Set<String> subdirectories) {
+ mMtpNotifier = notifier;
+ mSubdirectories = subdirectories;
+ mObjects = new HashMap<>();
+ mRoots = new HashMap<>();
+ mNextObjectId = 1;
+ mNextStorageId = 1;
+
+ mCheckConsistency = false; // Set to true to turn on automatic consistency checking
+ mConsistencyThread = new Thread(() -> {
+ while (mCheckConsistency) {
+ try {
+ Thread.sleep(15 * 1000);
+ } catch (InterruptedException e) {
+ return;
+ }
+ if (MtpStorageManager.this.checkConsistency()) {
+ Log.v(TAG, "Cache is consistent");
+ } else {
+ Log.w(TAG, "Cache is not consistent");
+ }
+ }
+ });
+ if (mCheckConsistency)
+ mConsistencyThread.start();
+ }
+
+ /**
+ * Clean up resources used by the storage manager.
+ */
+ public synchronized void close() {
+ Stream<MtpObject> objs = Stream.concat(mRoots.values().stream(),
+ mObjects.values().stream());
+
+ Iterator<MtpObject> iter = objs.iterator();
+ while (iter.hasNext()) {
+ // Close all FileObservers.
+ MtpObject obj = iter.next();
+ if (obj.getObserver() != null) {
+ obj.getObserver().stopWatching();
+ obj.setObserver(null);
+ }
+ }
+
+ // Shut down the consistency checking thread
+ if (mCheckConsistency) {
+ mCheckConsistency = false;
+ mConsistencyThread.interrupt();
+ try {
+ mConsistencyThread.join();
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * Sets the special subdirectories, which are the subdirectories of root storage that queries
+ * are restricted to. Must be done before any root storages are accessed.
+ * @param subDirs Subdirectories to set, or null to reset.
+ */
+ public synchronized void setSubdirectories(Set<String> subDirs) {
+ mSubdirectories = subDirs;
+ }
+
+ /**
+ * Allocates an MTP storage id for the given volume and add it to current roots.
+ * @param volume Storage to add.
+ * @return the associated MtpStorage
+ */
+ public synchronized MtpStorage addMtpStorage(StorageVolume volume) {
+ int storageId = ((getNextStorageId() & 0x0000FFFF) << 16) + 1;
+ MtpObject root = new MtpObject(volume.getPath(), storageId, null, true);
+ MtpStorage storage = new MtpStorage(volume, storageId);
+ mRoots.put(storageId, root);
+ return storage;
+ }
+
+ /**
+ * Removes the given storage and all associated items from the cache.
+ * @param storage Storage to remove.
+ */
+ public synchronized void removeMtpStorage(MtpStorage storage) {
+ removeObjectFromCache(getStorageRoot(storage.getStorageId()), true, true);
+ }
+
+ /**
+ * Checks if the given object can be renamed, moved, or deleted.
+ * If there are special subdirectories, they cannot be modified.
+ * @param obj Object to check.
+ * @return Whether object can be modified.
+ */
+ private synchronized boolean isSpecialSubDir(MtpObject obj) {
+ return obj.getParent().isRoot() && mSubdirectories != null
+ && !mSubdirectories.contains(obj.getName());
+ }
+
+ /**
+ * Get the object with the specified path. Visit any necessary directories on the way.
+ * @param path Full path of the object to find.
+ * @return The desired object, or null if it cannot be found.
+ */
+ public synchronized MtpObject getByPath(String path) {
+ MtpObject obj = null;
+ for (MtpObject root : mRoots.values()) {
+ if (path.startsWith(root.getName())) {
+ obj = root;
+ path = path.substring(root.getName().length());
+ }
+ }
+ for (String name : path.split("/")) {
+ if (obj == null || !obj.isDir())
+ return null;
+ if ("".equals(name))
+ continue;
+ if (!obj.isVisited())
+ getChildren(obj);
+ obj = obj.getChild(name);
+ }
+ return obj;
+ }
+
+ /**
+ * Get the object with specified id.
+ * @param id Id of object. must not be 0 or 0xFFFFFFFF
+ * @return Object, or null if error.
+ */
+ public synchronized MtpObject getObject(int id) {
+ if (id == 0 || id == 0xFFFFFFFF) {
+ Log.w(TAG, "Can't get root storages with getObject()");
+ return null;
+ }
+ if (!mObjects.containsKey(id)) {
+ Log.w(TAG, "Id " + id + " doesn't exist");
+ return null;
+ }
+ return mObjects.get(id);
+ }
+
+ /**
+ * Get the storage with specified id.
+ * @param id Storage id.
+ * @return Object that is the root of the storage, or null if error.
+ */
+ public MtpObject getStorageRoot(int id) {
+ if (!mRoots.containsKey(id)) {
+ Log.w(TAG, "StorageId " + id + " doesn't exist");
+ return null;
+ }
+ return mRoots.get(id);
+ }
+
+ private int getNextObjectId() {
+ int ret = mNextObjectId;
+ // Treat the id as unsigned int
+ mNextObjectId = (int) ((long) mNextObjectId + 1);
+ return ret;
+ }
+
+ private int getNextStorageId() {
+ return mNextStorageId++;
+ }
+
+ /**
+ * Get all objects matching the given parent, format, and storage
+ * @param parent object id of the parent. 0 for all objects, 0xFFFFFFFF for all object in root
+ * @param format format of returned objects. 0 for any format
+ * @param storageId storage id to look in. 0xFFFFFFFF for all storages
+ * @return A stream of matched objects, or null if error
+ */
+ public synchronized Stream<MtpObject> getObjects(int parent, int format, int storageId) {
+ boolean recursive = parent == 0;
+ if (parent == 0xFFFFFFFF)
+ parent = 0;
+ if (storageId == 0xFFFFFFFF) {
+ // query all stores
+ if (parent == 0) {
+ // Get the objects of this format and parent in each store.
+ ArrayList<Stream<MtpObject>> streamList = new ArrayList<>();
+ for (MtpObject root : mRoots.values()) {
+ streamList.add(getObjects(root, format, recursive));
+ }
+ return Stream.of(streamList).flatMap(Collection::stream).reduce(Stream::concat)
+ .orElseGet(Stream::empty);
+ }
+ }
+ MtpObject obj = parent == 0 ? getStorageRoot(storageId) : getObject(parent);
+ if (obj == null)
+ return null;
+ return getObjects(obj, format, recursive);
+ }
+
+ private synchronized Stream<MtpObject> getObjects(MtpObject parent, int format, boolean rec) {
+ Collection<MtpObject> children = getChildren(parent);
+ if (children == null)
+ return null;
+ Stream<MtpObject> ret = Stream.of(children).flatMap(Collection::stream);
+
+ if (format != 0) {
+ ret = ret.filter(o -> o.getFormat() == format);
+ }
+ if (rec) {
+ // Get all objects recursively.
+ ArrayList<Stream<MtpObject>> streamList = new ArrayList<>();
+ streamList.add(ret);
+ for (MtpObject o : children) {
+ if (o.isDir())
+ streamList.add(getObjects(o, format, true));
+ }
+ ret = Stream.of(streamList).filter(Objects::nonNull).flatMap(Collection::stream)
+ .reduce(Stream::concat).orElseGet(Stream::empty);
+ }
+ return ret;
+ }
+
+ /**
+ * Return the children of the given object. If the object hasn't been visited yet, add
+ * its children to the cache and start observing it.
+ * @param object the parent object
+ * @return The collection of child objects or null if error
+ */
+ private synchronized Collection<MtpObject> getChildren(MtpObject object) {
+ if (object == null || !object.isDir()) {
+ Log.w(TAG, "Can't find children of " + (object == null ? "null" : object.getId()));
+ return null;
+ }
+ if (!object.isVisited()) {
+ Path dir = object.getPath();
+ /*
+ * If a file is added after the observer starts watching the directory, but before
+ * the contents are listed, it will generate an event that will get processed
+ * after this synchronized function returns. We handle this by ignoring object
+ * added events if an object at that path already exists.
+ */
+ if (object.getObserver() != null)
+ Log.e(TAG, "Observer is not null!");
+ object.setObserver(new MtpObjectObserver(object));
+ object.getObserver().startWatching();
+ try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
+ for (Path file : stream) {
+ addObjectToCache(object, file.getFileName().toString(),
+ file.toFile().isDirectory());
+ }
+ } catch (IOException | DirectoryIteratorException e) {
+ Log.e(TAG, e.toString());
+ object.getObserver().stopWatching();
+ object.setObserver(null);
+ return null;
+ }
+ object.setVisited(true);
+ }
+ return object.getChildren();
+ }
+
+ /**
+ * Create a new object from the given path and add it to the cache.
+ * @param parent The parent object
+ * @param newName Path of the new object
+ * @return the new object if success, else null
+ */
+ private synchronized MtpObject addObjectToCache(MtpObject parent, String newName,
+ boolean isDir) {
+ if (!parent.isRoot() && getObject(parent.getId()) != parent)
+ // parent object has been removed
+ return null;
+ if (parent.getChild(newName) != null) {
+ // Object already exists
+ return null;
+ }
+ if (mSubdirectories != null && parent.isRoot() && !mSubdirectories.contains(newName)) {
+ // Not one of the restricted subdirectories.
+ return null;
+ }
+
+ MtpObject obj = new MtpObject(newName, getNextObjectId(), parent, isDir);
+ mObjects.put(obj.getId(), obj);
+ parent.addChild(obj);
+ return obj;
+ }
+
+ /**
+ * Remove the given path from the cache.
+ * @param removed The removed object
+ * @param removeGlobal Whether to remove the object from the global id map
+ * @param recursive Whether to also remove its children recursively.
+ * @return true if successfully removed
+ */
+ private synchronized boolean removeObjectFromCache(MtpObject removed, boolean removeGlobal,
+ boolean recursive) {
+ boolean ret = removed.isRoot()
+ || removed.getParent().mChildren.remove(removed.getName(), removed);
+ if (!ret && sDebug)
+ Log.w(TAG, "Failed to remove from parent " + removed.getPath());
+ if (removed.isRoot()) {
+ ret = mRoots.remove(removed.getId(), removed) && ret;
+ } else if (removeGlobal) {
+ ret = mObjects.remove(removed.getId(), removed) && ret;
+ }
+ if (!ret && sDebug)
+ Log.w(TAG, "Failed to remove from global cache " + removed.getPath());
+ if (removed.getObserver() != null) {
+ removed.getObserver().stopWatching();
+ removed.setObserver(null);
+ }
+ if (removed.isDir() && recursive) {
+ // Remove all descendants from cache recursively
+ Collection<MtpObject> children = new ArrayList<>(removed.getChildren());
+ for (MtpObject child : children) {
+ ret = removeObjectFromCache(child, removeGlobal, true) && ret;
+ }
+ }
+ return ret;
+ }
+
+ private synchronized void handleAddedObject(MtpObject parent, String path, boolean isDir) {
+ MtpOperation op = MtpOperation.NONE;
+ MtpObject obj = parent.getChild(path);
+ if (obj != null) {
+ MtpObjectState state = obj.getState();
+ op = obj.getOperation();
+ if (obj.isDir() != isDir && state != MtpObjectState.FROZEN_REMOVED)
+ Log.d(TAG, "Inconsistent directory info! " + obj.getPath());
+ obj.setDir(isDir);
+ switch (state) {
+ case FROZEN:
+ case FROZEN_REMOVED:
+ obj.setState(MtpObjectState.FROZEN_ADDED);
+ break;
+ case FROZEN_ONESHOT_ADD:
+ obj.setState(MtpObjectState.NORMAL);
+ break;
+ case NORMAL:
+ case FROZEN_ADDED:
+ // This can happen when handling listed object in a new directory.
+ return;
+ default:
+ Log.w(TAG, "Unexpected state in add " + path + " " + state);
+ }
+ if (sDebug)
+ Log.i(TAG, state + " transitioned to " + obj.getState() + " in op " + op);
+ } else {
+ obj = MtpStorageManager.this.addObjectToCache(parent, path, isDir);
+ if (obj != null) {
+ MtpStorageManager.this.mMtpNotifier.sendObjectAdded(obj.getId());
+ } else {
+ if (sDebug)
+ Log.w(TAG, "object " + path + " already exists");
+ return;
+ }
+ }
+ if (isDir) {
+ // If this was added as part of a rename do not visit or send events.
+ if (op == MtpOperation.RENAME)
+ return;
+
+ // If it was part of a copy operation, then only add observer if it was visited before.
+ if (op == MtpOperation.COPY && !obj.isVisited())
+ return;
+
+ if (obj.getObserver() != null) {
+ Log.e(TAG, "Observer is not null!");
+ return;
+ }
+ obj.setObserver(new MtpObjectObserver(obj));
+ obj.getObserver().startWatching();
+ obj.setVisited(true);
+
+ // It's possible that objects were added to a watched directory before the watch can be
+ // created, so manually handle those.
+ try (DirectoryStream<Path> stream = Files.newDirectoryStream(obj.getPath())) {
+ for (Path file : stream) {
+ if (sDebug)
+ Log.i(TAG, "Manually handling event for " + file.getFileName().toString());
+ handleAddedObject(obj, file.getFileName().toString(),
+ file.toFile().isDirectory());
+ }
+ } catch (IOException | DirectoryIteratorException e) {
+ Log.e(TAG, e.toString());
+ obj.getObserver().stopWatching();
+ obj.setObserver(null);
+ }
+ }
+ }
+
+ private synchronized void handleRemovedObject(MtpObject obj) {
+ MtpObjectState state = obj.getState();
+ MtpOperation op = obj.getOperation();
+ switch (state) {
+ case FROZEN_ADDED:
+ obj.setState(MtpObjectState.FROZEN_REMOVED);
+ break;
+ case FROZEN_ONESHOT_DEL:
+ removeObjectFromCache(obj, op != MtpOperation.RENAME, false);
+ break;
+ case FROZEN:
+ obj.setState(MtpObjectState.FROZEN_REMOVED);
+ break;
+ case NORMAL:
+ if (MtpStorageManager.this.removeObjectFromCache(obj, true, true))
+ MtpStorageManager.this.mMtpNotifier.sendObjectRemoved(obj.getId());
+ break;
+ default:
+ // This shouldn't happen; states correspond to objects that don't exist
+ Log.e(TAG, "Got unexpected object remove for " + obj.getName());
+ }
+ if (sDebug)
+ Log.i(TAG, state + " transitioned to " + obj.getState() + " in op " + op);
+ }
+
+ /**
+ * Block the caller until all events currently in the event queue have been
+ * read and processed. Used for testing purposes.
+ */
+ public void flushEvents() {
+ try {
+ // TODO make this smarter
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+
+ }
+ }
+
+ /**
+ * Dumps a representation of the cache to log.
+ */
+ public synchronized void dump() {
+ for (int key : mObjects.keySet()) {
+ MtpObject obj = mObjects.get(key);
+ Log.i(TAG, key + " | " + (obj.getParent() == null ? obj.getParent().getId() : "null")
+ + " | " + obj.getName() + " | " + (obj.isDir() ? "dir" : "obj")
+ + " | " + (obj.isVisited() ? "v" : "nv") + " | " + obj.getState());
+ }
+ }
+
+ /**
+ * Checks consistency of the cache. This checks whether all objects have correct links
+ * to their parent, and whether directories are missing or have extraneous objects.
+ * @return true iff cache is consistent
+ */
+ public synchronized boolean checkConsistency() {
+ Stream<MtpObject> objs = Stream.concat(mRoots.values().stream(),
+ mObjects.values().stream());
+ Iterator<MtpObject> iter = objs.iterator();
+ boolean ret = true;
+ while (iter.hasNext()) {
+ MtpObject obj = iter.next();
+ if (!obj.exists()) {
+ Log.w(TAG, "Object doesn't exist " + obj.getPath() + " " + obj.getId());
+ ret = false;
+ }
+ if (obj.getState() != MtpObjectState.NORMAL) {
+ Log.w(TAG, "Object " + obj.getPath() + " in state " + obj.getState());
+ ret = false;
+ }
+ if (obj.getOperation() != MtpOperation.NONE) {
+ Log.w(TAG, "Object " + obj.getPath() + " in operation " + obj.getOperation());
+ ret = false;
+ }
+ if (!obj.isRoot() && mObjects.get(obj.getId()) != obj) {
+ Log.w(TAG, "Object " + obj.getPath() + " is not in map correctly");
+ ret = false;
+ }
+ if (obj.getParent() != null) {
+ if (obj.getParent().isRoot() && obj.getParent()
+ != mRoots.get(obj.getParent().getId())) {
+ Log.w(TAG, "Root parent is not in root mapping " + obj.getPath());
+ ret = false;
+ }
+ if (!obj.getParent().isRoot() && obj.getParent()
+ != mObjects.get(obj.getParent().getId())) {
+ Log.w(TAG, "Parent is not in object mapping " + obj.getPath());
+ ret = false;
+ }
+ if (obj.getParent().getChild(obj.getName()) != obj) {
+ Log.w(TAG, "Child does not exist in parent " + obj.getPath());
+ ret = false;
+ }
+ }
+ if (obj.isDir()) {
+ if (obj.isVisited() == (obj.getObserver() == null)) {
+ Log.w(TAG, obj.getPath() + " is " + (obj.isVisited() ? "" : "not ")
+ + " visited but observer is " + obj.getObserver());
+ ret = false;
+ }
+ if (!obj.isVisited() && obj.getChildren().size() > 0) {
+ Log.w(TAG, obj.getPath() + " is not visited but has children");
+ ret = false;
+ }
+ try (DirectoryStream<Path> stream = Files.newDirectoryStream(obj.getPath())) {
+ Set<String> files = new HashSet<>();
+ for (Path file : stream) {
+ if (obj.isVisited() &&
+ obj.getChild(file.getFileName().toString()) == null &&
+ (mSubdirectories == null || !obj.isRoot() ||
+ mSubdirectories.contains(file.getFileName().toString()))) {
+ Log.w(TAG, "File exists in fs but not in children " + file);
+ ret = false;
+ }
+ files.add(file.toString());
+ }
+ for (MtpObject child : obj.getChildren()) {
+ if (!files.contains(child.getPath().toString())) {
+ Log.w(TAG, "File in children doesn't exist in fs " + child.getPath());
+ ret = false;
+ }
+ if (child != mObjects.get(child.getId())) {
+ Log.w(TAG, "Child is not in object map " + child.getPath());
+ ret = false;
+ }
+ }
+ } catch (IOException | DirectoryIteratorException e) {
+ Log.w(TAG, e.toString());
+ ret = false;
+ }
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Informs MtpStorageManager that an object with the given path is about to be added.
+ * @param parent The parent object of the object to be added.
+ * @param name Filename of object to add.
+ * @return Object id of the added object, or -1 if it cannot be added.
+ */
+ public synchronized int beginSendObject(MtpObject parent, String name, int format) {
+ if (sDebug)
+ Log.v(TAG, "beginSendObject " + name);
+ if (!parent.isDir())
+ return -1;
+ if (parent.isRoot() && mSubdirectories != null && !mSubdirectories.contains(name))
+ return -1;
+ getChildren(parent); // Ensure parent is visited
+ MtpObject obj = addObjectToCache(parent, name, format == MtpConstants.FORMAT_ASSOCIATION);
+ if (obj == null)
+ return -1;
+ obj.setState(MtpObjectState.FROZEN);
+ obj.setOperation(MtpOperation.ADD);
+ return obj.getId();
+ }
+
+ /**
+ * Clean up the object state after a sendObject operation.
+ * @param obj The object, returned from beginAddObject().
+ * @param succeeded Whether the file was successfully created.
+ * @return Whether cache state was successfully cleaned up.
+ */
+ public synchronized boolean endSendObject(MtpObject obj, boolean succeeded) {
+ if (sDebug)
+ Log.v(TAG, "endSendObject " + succeeded);
+ return generalEndAddObject(obj, succeeded, true);
+ }
+
+ /**
+ * Informs MtpStorageManager that the given object is about to be renamed.
+ * If this returns true, it must be followed with an endRenameObject()
+ * @param obj Object to be renamed.
+ * @param newName New name of the object.
+ * @return Whether renaming is allowed.
+ */
+ public synchronized boolean beginRenameObject(MtpObject obj, String newName) {
+ if (sDebug)
+ Log.v(TAG, "beginRenameObject " + obj.getName() + " " + newName);
+ if (obj.isRoot())
+ return false;
+ if (isSpecialSubDir(obj))
+ return false;
+ if (obj.getParent().getChild(newName) != null)
+ // Object already exists in parent with that name.
+ return false;
+
+ MtpObject oldObj = obj.copy(false);
+ obj.setName(newName);
+ obj.getParent().addChild(obj);
+ oldObj.getParent().addChild(oldObj);
+ return generalBeginRenameObject(oldObj, obj);
+ }
+
+ /**
+ * Cleans up cache state after a rename operation and sends any events that were missed.
+ * @param obj The object being renamed, the same one that was passed in beginRenameObject().
+ * @param oldName The previous name of the object.
+ * @param success Whether the rename operation succeeded.
+ * @return Whether state was successfully cleaned up.
+ */
+ public synchronized boolean endRenameObject(MtpObject obj, String oldName, boolean success) {
+ if (sDebug)
+ Log.v(TAG, "endRenameObject " + success);
+ MtpObject parent = obj.getParent();
+ MtpObject oldObj = parent.getChild(oldName);
+ if (!success) {
+ // If the rename failed, we want oldObj to be the original and obj to be the dummy.
+ // Switch the objects, except for their name and state.
+ MtpObject temp = oldObj;
+ MtpObjectState oldState = oldObj.getState();
+ temp.setName(obj.getName());
+ temp.setState(obj.getState());
+ oldObj = obj;
+ oldObj.setName(oldName);
+ oldObj.setState(oldState);
+ obj = temp;
+ parent.addChild(obj);
+ parent.addChild(oldObj);
+ }
+ return generalEndRenameObject(oldObj, obj, success);
+ }
+
+ /**
+ * Informs MtpStorageManager that the given object is about to be deleted by the initiator,
+ * so don't send an event.
+ * @param obj Object to be deleted.
+ * @return Whether cache deletion is allowed.
+ */
+ public synchronized boolean beginRemoveObject(MtpObject obj) {
+ if (sDebug)
+ Log.v(TAG, "beginRemoveObject " + obj.getName());
+ return !obj.isRoot() && !isSpecialSubDir(obj)
+ && generalBeginRemoveObject(obj, MtpOperation.DELETE);
+ }
+
+ /**
+ * Clean up cache state after a delete operation and send any events that were missed.
+ * @param obj Object to be deleted, same one passed in beginRemoveObject().
+ * @param success Whether operation was completed successfully.
+ * @return Whether cache state is correct.
+ */
+ public synchronized boolean endRemoveObject(MtpObject obj, boolean success) {
+ if (sDebug)
+ Log.v(TAG, "endRemoveObject " + success);
+ boolean ret = true;
+ if (obj.isDir()) {
+ for (MtpObject child : new ArrayList<>(obj.getChildren()))
+ if (child.getOperation() == MtpOperation.DELETE)
+ ret = endRemoveObject(child, success) && ret;
+ }
+ return generalEndRemoveObject(obj, success, true) && ret;
+ }
+
+ /**
+ * Informs MtpStorageManager that the given object is about to be moved to a new parent.
+ * @param obj Object to be moved.
+ * @param newParent The new parent object.
+ * @return Whether the move is allowed.
+ */
+ public synchronized boolean beginMoveObject(MtpObject obj, MtpObject newParent) {
+ if (sDebug)
+ Log.v(TAG, "beginMoveObject " + newParent.getPath());
+ if (obj.isRoot())
+ return false;
+ if (isSpecialSubDir(obj))
+ return false;
+ getChildren(newParent); // Ensure parent is visited
+ if (newParent.getChild(obj.getName()) != null)
+ // Object already exists in parent with that name.
+ return false;
+ if (obj.getStorageId() != newParent.getStorageId()) {
+ /*
+ * The move is occurring across storages. The observers will not remain functional
+ * after the move, and the move will not be atomic. We have to copy the file tree
+ * to the destination and recreate the observers once copy is complete.
+ */
+ MtpObject newObj = obj.copy(true);
+ newObj.setParent(newParent);
+ newParent.addChild(newObj);
+ return generalBeginRemoveObject(obj, MtpOperation.RENAME)
+ && generalBeginCopyObject(newObj, false);
+ }
+ // Move obj to new parent, create a dummy object in the old parent.
+ MtpObject oldObj = obj.copy(false);
+ obj.setParent(newParent);
+ oldObj.getParent().addChild(oldObj);
+ obj.getParent().addChild(obj);
+ return generalBeginRenameObject(oldObj, obj);
+ }
+
+ /**
+ * Clean up cache state after a move operation and send any events that were missed.
+ * @param oldParent The old parent object.
+ * @param newParent The new parent object.
+ * @param name The name of the object being moved.
+ * @param success Whether operation was completed successfully.
+ * @return Whether cache state is correct.
+ */
+ public synchronized boolean endMoveObject(MtpObject oldParent, MtpObject newParent, String name,
+ boolean success) {
+ if (sDebug)
+ Log.v(TAG, "endMoveObject " + success);
+ MtpObject oldObj = oldParent.getChild(name);
+ MtpObject newObj = newParent.getChild(name);
+ if (oldObj == null || newObj == null)
+ return false;
+ if (oldParent.getStorageId() != newObj.getStorageId()) {
+ boolean ret = endRemoveObject(oldObj, success);
+ return generalEndCopyObject(newObj, success, true) && ret;
+ }
+ if (!success) {
+ // If the rename failed, we want oldObj to be the original and obj to be the dummy.
+ // Switch the objects, except for their parent and state.
+ MtpObject temp = oldObj;
+ MtpObjectState oldState = oldObj.getState();
+ temp.setParent(newObj.getParent());
+ temp.setState(newObj.getState());
+ oldObj = newObj;
+ oldObj.setParent(oldParent);
+ oldObj.setState(oldState);
+ newObj = temp;
+ newObj.getParent().addChild(newObj);
+ oldParent.addChild(oldObj);
+ }
+ return generalEndRenameObject(oldObj, newObj, success);
+ }
+
+ /**
+ * Informs MtpStorageManager that the given object is about to be copied recursively.
+ * @param object Object to be copied
+ * @param newParent New parent for the object.
+ * @return The object id for the new copy, or -1 if error.
+ */
+ public synchronized int beginCopyObject(MtpObject object, MtpObject newParent) {
+ if (sDebug)
+ Log.v(TAG, "beginCopyObject " + object.getName() + " to " + newParent.getPath());
+ String name = object.getName();
+ if (!newParent.isDir())
+ return -1;
+ if (newParent.isRoot() && mSubdirectories != null && !mSubdirectories.contains(name))
+ return -1;
+ getChildren(newParent); // Ensure parent is visited
+ if (newParent.getChild(name) != null)
+ return -1;
+ MtpObject newObj = object.copy(object.isDir());
+ newParent.addChild(newObj);
+ newObj.setParent(newParent);
+ if (!generalBeginCopyObject(newObj, true))
+ return -1;
+ return newObj.getId();
+ }
+
+ /**
+ * Cleans up cache state after a copy operation.
+ * @param object Object that was copied.
+ * @param success Whether the operation was successful.
+ * @return Whether cache state is consistent.
+ */
+ public synchronized boolean endCopyObject(MtpObject object, boolean success) {
+ if (sDebug)
+ Log.v(TAG, "endCopyObject " + object.getName() + " " + success);
+ return generalEndCopyObject(object, success, false);
+ }
+
+ private synchronized boolean generalEndAddObject(MtpObject obj, boolean succeeded,
+ boolean removeGlobal) {
+ switch (obj.getState()) {
+ case FROZEN:
+ // Object was never created.
+ if (succeeded) {
+ // The operation was successful so the event must still be in the queue.
+ obj.setState(MtpObjectState.FROZEN_ONESHOT_ADD);
+ } else {
+ // The operation failed and never created the file.
+ if (!removeObjectFromCache(obj, removeGlobal, false)) {
+ return false;
+ }
+ }
+ break;
+ case FROZEN_ADDED:
+ obj.setState(MtpObjectState.NORMAL);
+ if (!succeeded) {
+ MtpObject parent = obj.getParent();
+ // The operation failed but some other process created the file. Send an event.
+ if (!removeObjectFromCache(obj, removeGlobal, false))
+ return false;
+ handleAddedObject(parent, obj.getName(), obj.isDir());
+ }
+ // else: The operation successfully created the object.
+ break;
+ case FROZEN_REMOVED:
+ if (!removeObjectFromCache(obj, removeGlobal, false))
+ return false;
+ if (succeeded) {
+ // Some other process deleted the object. Send an event.
+ mMtpNotifier.sendObjectRemoved(obj.getId());
+ }
+ // else: Mtp deleted the object as part of cleanup. Don't send an event.
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ private synchronized boolean generalEndRemoveObject(MtpObject obj, boolean success,
+ boolean removeGlobal) {
+ switch (obj.getState()) {
+ case FROZEN:
+ if (success) {
+ // Object was deleted successfully, and event is still in the queue.
+ obj.setState(MtpObjectState.FROZEN_ONESHOT_DEL);
+ } else {
+ // Object was not deleted.
+ obj.setState(MtpObjectState.NORMAL);
+ }
+ break;
+ case FROZEN_ADDED:
+ // Object was deleted, and then readded.
+ obj.setState(MtpObjectState.NORMAL);
+ if (success) {
+ // Some other process readded the object.
+ MtpObject parent = obj.getParent();
+ if (!removeObjectFromCache(obj, removeGlobal, false))
+ return false;
+ handleAddedObject(parent, obj.getName(), obj.isDir());
+ }
+ // else : Object still exists after failure.
+ break;
+ case FROZEN_REMOVED:
+ if (!removeObjectFromCache(obj, removeGlobal, false))
+ return false;
+ if (!success) {
+ // Some other process deleted the object.
+ mMtpNotifier.sendObjectRemoved(obj.getId());
+ }
+ // else : This process deleted the object as part of the operation.
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ private synchronized boolean generalBeginRenameObject(MtpObject fromObj, MtpObject toObj) {
+ fromObj.setState(MtpObjectState.FROZEN);
+ toObj.setState(MtpObjectState.FROZEN);
+ fromObj.setOperation(MtpOperation.RENAME);
+ toObj.setOperation(MtpOperation.RENAME);
+ return true;
+ }
+
+ private synchronized boolean generalEndRenameObject(MtpObject fromObj, MtpObject toObj,
+ boolean success) {
+ boolean ret = generalEndRemoveObject(fromObj, success, !success);
+ return generalEndAddObject(toObj, success, success) && ret;
+ }
+
+ private synchronized boolean generalBeginRemoveObject(MtpObject obj, MtpOperation op) {
+ obj.setState(MtpObjectState.FROZEN);
+ obj.setOperation(op);
+ if (obj.isDir()) {
+ for (MtpObject child : obj.getChildren())
+ generalBeginRemoveObject(child, op);
+ }
+ return true;
+ }
+
+ private synchronized boolean generalBeginCopyObject(MtpObject obj, boolean newId) {
+ obj.setState(MtpObjectState.FROZEN);
+ obj.setOperation(MtpOperation.COPY);
+ if (newId) {
+ obj.setId(getNextObjectId());
+ mObjects.put(obj.getId(), obj);
+ }
+ if (obj.isDir())
+ for (MtpObject child : obj.getChildren())
+ if (!generalBeginCopyObject(child, newId))
+ return false;
+ return true;
+ }
+
+ private synchronized boolean generalEndCopyObject(MtpObject obj, boolean success, boolean addGlobal) {
+ if (success && addGlobal)
+ mObjects.put(obj.getId(), obj);
+ boolean ret = true;
+ if (obj.isDir()) {
+ for (MtpObject child : new ArrayList<>(obj.getChildren())) {
+ if (child.getOperation() == MtpOperation.COPY)
+ ret = generalEndCopyObject(child, success, addGlobal) && ret;
+ }
+ }
+ ret = generalEndAddObject(obj, success, success || !addGlobal) && ret;
+ return ret;
+ }
+}
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 4e8c72b..23ef84f6 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -19,7 +19,7 @@
#include "android_media_Utils.h"
#include "mtp.h"
-#include "MtpDatabase.h"
+#include "IMtpDatabase.h"
#include "MtpDataPacket.h"
#include "MtpObjectInfo.h"
#include "MtpProperty.h"
@@ -55,7 +55,7 @@
static jmethodID method_beginSendObject;
static jmethodID method_endSendObject;
-static jmethodID method_doScanDirectory;
+static jmethodID method_rescanFile;
static jmethodID method_getObjectList;
static jmethodID method_getNumObjects;
static jmethodID method_getSupportedPlaybackFormats;
@@ -68,35 +68,34 @@
static jmethodID method_getObjectPropertyList;
static jmethodID method_getObjectInfo;
static jmethodID method_getObjectFilePath;
-static jmethodID method_deleteFile;
-static jmethodID method_moveObject;
+static jmethodID method_beginDeleteObject;
+static jmethodID method_endDeleteObject;
+static jmethodID method_beginMoveObject;
+static jmethodID method_endMoveObject;
+static jmethodID method_beginCopyObject;
+static jmethodID method_endCopyObject;
static jmethodID method_getObjectReferences;
static jmethodID method_setObjectReferences;
-static jmethodID method_sessionStarted;
-static jmethodID method_sessionEnded;
static jfieldID field_context;
-static jfieldID field_batteryLevel;
-static jfieldID field_batteryScale;
-static jfieldID field_deviceType;
-// MtpPropertyList fields
-static jfieldID field_mCount;
-static jfieldID field_mResult;
-static jfieldID field_mObjectHandles;
-static jfieldID field_mPropertyCodes;
-static jfieldID field_mDataTypes;
-static jfieldID field_mLongValues;
-static jfieldID field_mStringValues;
+// MtpPropertyList methods
+static jmethodID method_getCode;
+static jmethodID method_getCount;
+static jmethodID method_getObjectHandles;
+static jmethodID method_getPropertyCodes;
+static jmethodID method_getDataTypes;
+static jmethodID method_getLongValues;
+static jmethodID method_getStringValues;
-MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) {
- return (MtpDatabase *)env->GetLongField(database, field_context);
+IMtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) {
+ return (IMtpDatabase *)env->GetLongField(database, field_context);
}
// ----------------------------------------------------------------------------
-class MyMtpDatabase : public MtpDatabase {
+class MtpDatabase : public IMtpDatabase {
private:
jobject mDatabase;
jintArray mIntBuffer;
@@ -104,23 +103,20 @@
jcharArray mStringBuffer;
public:
- MyMtpDatabase(JNIEnv *env, jobject client);
- virtual ~MyMtpDatabase();
+ MtpDatabase(JNIEnv *env, jobject client);
+ virtual ~MtpDatabase();
void cleanup(JNIEnv *env);
virtual MtpObjectHandle beginSendObject(const char* path,
MtpObjectFormat format,
MtpObjectHandle parent,
- MtpStorageID storage,
- uint64_t size,
- time_t modified);
+ MtpStorageID storage);
- virtual void endSendObject(const char* path,
+ virtual void endSendObject(MtpObjectHandle handle, bool succeeded);
+
+ virtual void rescanFile(const char* path,
MtpObjectHandle handle,
- MtpObjectFormat format,
- bool succeeded);
-
- virtual void doScanDirectory(const char* path);
+ MtpObjectFormat format);
virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID,
MtpObjectFormat format,
@@ -167,7 +163,8 @@
MtpString& outFilePath,
int64_t& outFileLength,
MtpObjectFormat& outFormat);
- virtual MtpResponseCode deleteFile(MtpObjectHandle handle);
+ virtual MtpResponseCode beginDeleteObject(MtpObjectHandle handle);
+ virtual void endDeleteObject(MtpObjectHandle handle, bool succeeded);
bool getObjectPropertyInfo(MtpObjectProperty property, int& type);
bool getDevicePropertyInfo(MtpDeviceProperty property, int& type);
@@ -182,12 +179,17 @@
virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty property);
- virtual MtpResponseCode moveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
- MtpStorageID newStorage, MtpString& newPath);
+ virtual MtpResponseCode beginMoveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+ MtpStorageID newStorage);
- virtual void sessionStarted();
+ virtual void endMoveObject(MtpObjectHandle oldParent, MtpObjectHandle newParent,
+ MtpStorageID oldStorage, MtpStorageID newStorage,
+ MtpObjectHandle handle, bool succeeded);
- virtual void sessionEnded();
+ virtual MtpResponseCode beginCopyObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+ MtpStorageID newStorage);
+ virtual void endCopyObject(MtpObjectHandle handle, bool succeeded);
+
};
// ----------------------------------------------------------------------------
@@ -202,7 +204,7 @@
// ----------------------------------------------------------------------------
-MyMtpDatabase::MyMtpDatabase(JNIEnv *env, jobject client)
+MtpDatabase::MtpDatabase(JNIEnv *env, jobject client)
: mDatabase(env->NewGlobalRef(client)),
mIntBuffer(NULL),
mLongBuffer(NULL),
@@ -228,27 +230,24 @@
mStringBuffer = (jcharArray)env->NewGlobalRef(charArray);
}
-void MyMtpDatabase::cleanup(JNIEnv *env) {
+void MtpDatabase::cleanup(JNIEnv *env) {
env->DeleteGlobalRef(mDatabase);
env->DeleteGlobalRef(mIntBuffer);
env->DeleteGlobalRef(mLongBuffer);
env->DeleteGlobalRef(mStringBuffer);
}
-MyMtpDatabase::~MyMtpDatabase() {
+MtpDatabase::~MtpDatabase() {
}
-MtpObjectHandle MyMtpDatabase::beginSendObject(const char* path,
+MtpObjectHandle MtpDatabase::beginSendObject(const char* path,
MtpObjectFormat format,
MtpObjectHandle parent,
- MtpStorageID storage,
- uint64_t size,
- time_t modified) {
+ MtpStorageID storage) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
jstring pathStr = env->NewStringUTF(path);
MtpObjectHandle result = env->CallIntMethod(mDatabase, method_beginSendObject,
- pathStr, (jint)format, (jint)parent, (jint)storage,
- (jlong)size, (jlong)modified);
+ pathStr, (jint)format, (jint)parent, (jint)storage);
if (pathStr)
env->DeleteLocalRef(pathStr);
@@ -256,29 +255,26 @@
return result;
}
-void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle,
- MtpObjectFormat format, bool succeeded) {
+void MtpDatabase::endSendObject(MtpObjectHandle handle, bool succeeded) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(mDatabase, method_endSendObject, (jint)handle, (jboolean)succeeded);
+
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void MtpDatabase::rescanFile(const char* path, MtpObjectHandle handle,
+ MtpObjectFormat format) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
jstring pathStr = env->NewStringUTF(path);
- env->CallVoidMethod(mDatabase, method_endSendObject, pathStr,
- (jint)handle, (jint)format, (jboolean)succeeded);
+ env->CallVoidMethod(mDatabase, method_rescanFile, pathStr,
+ (jint)handle, (jint)format);
if (pathStr)
env->DeleteLocalRef(pathStr);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
-void MyMtpDatabase::doScanDirectory(const char* path) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- jstring pathStr = env->NewStringUTF(path);
- env->CallVoidMethod(mDatabase, method_doScanDirectory, pathStr);
-
- if (pathStr)
- env->DeleteLocalRef(pathStr);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-}
-
-MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID,
+MtpObjectHandleList* MtpDatabase::getObjectList(MtpStorageID storageID,
MtpObjectFormat format,
MtpObjectHandle parent) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -298,7 +294,7 @@
return list;
}
-int MyMtpDatabase::getNumObjects(MtpStorageID storageID,
+int MtpDatabase::getNumObjects(MtpStorageID storageID,
MtpObjectFormat format,
MtpObjectHandle parent) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -309,7 +305,7 @@
return result;
}
-MtpObjectFormatList* MyMtpDatabase::getSupportedPlaybackFormats() {
+MtpObjectFormatList* MtpDatabase::getSupportedPlaybackFormats() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
method_getSupportedPlaybackFormats);
@@ -327,7 +323,7 @@
return list;
}
-MtpObjectFormatList* MyMtpDatabase::getSupportedCaptureFormats() {
+MtpObjectFormatList* MtpDatabase::getSupportedCaptureFormats() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
method_getSupportedCaptureFormats);
@@ -345,7 +341,7 @@
return list;
}
-MtpObjectPropertyList* MyMtpDatabase::getSupportedObjectProperties(MtpObjectFormat format) {
+MtpObjectPropertyList* MtpDatabase::getSupportedObjectProperties(MtpObjectFormat format) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
method_getSupportedObjectProperties, (jint)format);
@@ -363,7 +359,7 @@
return list;
}
-MtpDevicePropertyList* MyMtpDatabase::getSupportedDeviceProperties() {
+MtpDevicePropertyList* MtpDatabase::getSupportedDeviceProperties() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
method_getSupportedDeviceProperties);
@@ -381,7 +377,7 @@
return list;
}
-MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle,
+MtpResponseCode MtpDatabase::getObjectPropertyValue(MtpObjectHandle handle,
MtpObjectProperty property,
MtpDataPacket& packet) {
static_assert(sizeof(jint) >= sizeof(MtpObjectHandle),
@@ -397,42 +393,26 @@
static_cast<jint>(property),
0,
0);
- MtpResponseCode result = env->GetIntField(list, field_mResult);
- int count = env->GetIntField(list, field_mCount);
- if (result == MTP_RESPONSE_OK && count != 1)
+ MtpResponseCode result = env->CallIntMethod(list, method_getCode);
+ jint count = env->CallIntMethod(list, method_getCount);
+ if (count != 1)
result = MTP_RESPONSE_GENERAL_ERROR;
if (result == MTP_RESPONSE_OK) {
- jintArray objectHandlesArray = (jintArray)env->GetObjectField(list, field_mObjectHandles);
- jintArray propertyCodesArray = (jintArray)env->GetObjectField(list, field_mPropertyCodes);
- jintArray dataTypesArray = (jintArray)env->GetObjectField(list, field_mDataTypes);
- jlongArray longValuesArray = (jlongArray)env->GetObjectField(list, field_mLongValues);
- jobjectArray stringValuesArray = (jobjectArray)env->GetObjectField(list, field_mStringValues);
+ jintArray objectHandlesArray = (jintArray)env->CallObjectMethod(list, method_getObjectHandles);
+ jintArray propertyCodesArray = (jintArray)env->CallObjectMethod(list, method_getPropertyCodes);
+ jintArray dataTypesArray = (jintArray)env->CallObjectMethod(list, method_getDataTypes);
+ jlongArray longValuesArray = (jlongArray)env->CallObjectMethod(list, method_getLongValues);
+ jobjectArray stringValuesArray = (jobjectArray)env->CallObjectMethod(list, method_getStringValues);
jint* objectHandles = env->GetIntArrayElements(objectHandlesArray, 0);
jint* propertyCodes = env->GetIntArrayElements(propertyCodesArray, 0);
jint* dataTypes = env->GetIntArrayElements(dataTypesArray, 0);
- jlong* longValues = (longValuesArray ? env->GetLongArrayElements(longValuesArray, 0) : NULL);
+ jlong* longValues = env->GetLongArrayElements(longValuesArray, 0);
int type = dataTypes[0];
jlong longValue = (longValues ? longValues[0] : 0);
- // special case date properties, which are strings to MTP
- // but stored internally as a uint64
- if (property == MTP_PROPERTY_DATE_MODIFIED || property == MTP_PROPERTY_DATE_ADDED) {
- char date[20];
- formatDateTime(longValue, date, sizeof(date));
- packet.putString(date);
- goto out;
- }
- // release date is stored internally as just the year
- if (property == MTP_PROPERTY_ORIGINAL_RELEASE_DATE) {
- char date[20];
- snprintf(date, sizeof(date), "%04" PRId64 "0101T000000", longValue);
- packet.putString(date);
- goto out;
- }
-
switch (type) {
case MTP_TYPE_INT8:
packet.putInt8(longValue);
@@ -481,20 +461,16 @@
ALOGE("unsupported type in getObjectPropertyValue\n");
result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
}
-out:
env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0);
env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0);
env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0);
- if (longValues)
- env->ReleaseLongArrayElements(longValuesArray, longValues, 0);
+ env->ReleaseLongArrayElements(longValuesArray, longValues, 0);
env->DeleteLocalRef(objectHandlesArray);
env->DeleteLocalRef(propertyCodesArray);
env->DeleteLocalRef(dataTypesArray);
- if (longValuesArray)
- env->DeleteLocalRef(longValuesArray);
- if (stringValuesArray)
- env->DeleteLocalRef(stringValuesArray);
+ env->DeleteLocalRef(longValuesArray);
+ env->DeleteLocalRef(stringValuesArray);
}
env->DeleteLocalRef(list);
@@ -559,7 +535,7 @@
return true;
}
-MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle,
+MtpResponseCode MtpDatabase::setObjectPropertyValue(MtpObjectHandle handle,
MtpObjectProperty property,
MtpDataPacket& packet) {
int type;
@@ -590,80 +566,73 @@
return result;
}
-MtpResponseCode MyMtpDatabase::getDevicePropertyValue(MtpDeviceProperty property,
+MtpResponseCode MtpDatabase::getDevicePropertyValue(MtpDeviceProperty property,
MtpDataPacket& packet) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
+ int type;
- if (property == MTP_DEVICE_PROPERTY_BATTERY_LEVEL) {
- // special case - implemented here instead of Java
- packet.putUInt8((uint8_t)env->GetIntField(mDatabase, field_batteryLevel));
- return MTP_RESPONSE_OK;
- } else {
- int type;
+ if (!getDevicePropertyInfo(property, type))
+ return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
- if (!getDevicePropertyInfo(property, type))
- return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
-
- jint result = env->CallIntMethod(mDatabase, method_getDeviceProperty,
- (jint)property, mLongBuffer, mStringBuffer);
- if (result != MTP_RESPONSE_OK) {
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return result;
- }
-
- jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
- jlong longValue = longValues[0];
- env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
-
- switch (type) {
- case MTP_TYPE_INT8:
- packet.putInt8(longValue);
- break;
- case MTP_TYPE_UINT8:
- packet.putUInt8(longValue);
- break;
- case MTP_TYPE_INT16:
- packet.putInt16(longValue);
- break;
- case MTP_TYPE_UINT16:
- packet.putUInt16(longValue);
- break;
- case MTP_TYPE_INT32:
- packet.putInt32(longValue);
- break;
- case MTP_TYPE_UINT32:
- packet.putUInt32(longValue);
- break;
- case MTP_TYPE_INT64:
- packet.putInt64(longValue);
- break;
- case MTP_TYPE_UINT64:
- packet.putUInt64(longValue);
- break;
- case MTP_TYPE_INT128:
- packet.putInt128(longValue);
- break;
- case MTP_TYPE_UINT128:
- packet.putInt128(longValue);
- break;
- case MTP_TYPE_STR:
- {
- jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
- packet.putString(str);
- env->ReleaseCharArrayElements(mStringBuffer, str, 0);
- break;
- }
- default:
- ALOGE("unsupported type in getDevicePropertyValue\n");
- return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT;
- }
-
+ jint result = env->CallIntMethod(mDatabase, method_getDeviceProperty,
+ (jint)property, mLongBuffer, mStringBuffer);
+ if (result != MTP_RESPONSE_OK) {
checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return MTP_RESPONSE_OK;
+ return result;
}
+
+ jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
+ jlong longValue = longValues[0];
+ env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
+
+ switch (type) {
+ case MTP_TYPE_INT8:
+ packet.putInt8(longValue);
+ break;
+ case MTP_TYPE_UINT8:
+ packet.putUInt8(longValue);
+ break;
+ case MTP_TYPE_INT16:
+ packet.putInt16(longValue);
+ break;
+ case MTP_TYPE_UINT16:
+ packet.putUInt16(longValue);
+ break;
+ case MTP_TYPE_INT32:
+ packet.putInt32(longValue);
+ break;
+ case MTP_TYPE_UINT32:
+ packet.putUInt32(longValue);
+ break;
+ case MTP_TYPE_INT64:
+ packet.putInt64(longValue);
+ break;
+ case MTP_TYPE_UINT64:
+ packet.putUInt64(longValue);
+ break;
+ case MTP_TYPE_INT128:
+ packet.putInt128(longValue);
+ break;
+ case MTP_TYPE_UINT128:
+ packet.putInt128(longValue);
+ break;
+ case MTP_TYPE_STR:
+ {
+ jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
+ packet.putString(str);
+ env->ReleaseCharArrayElements(mStringBuffer, str, 0);
+ break;
+ }
+ default:
+ ALOGE("unsupported type in getDevicePropertyValue\n");
+ return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT;
+ }
+
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return MTP_RESPONSE_OK;
}
-MtpResponseCode MyMtpDatabase::setDevicePropertyValue(MtpDeviceProperty property,
+MtpResponseCode MtpDatabase::setDevicePropertyValue(MtpDeviceProperty property,
MtpDataPacket& packet) {
int type;
@@ -693,11 +662,11 @@
return result;
}
-MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty /*property*/) {
+MtpResponseCode MtpDatabase::resetDeviceProperty(MtpDeviceProperty /*property*/) {
return -1;
}
-MtpResponseCode MyMtpDatabase::getObjectPropertyList(MtpObjectHandle handle,
+MtpResponseCode MtpDatabase::getObjectPropertyList(MtpObjectHandle handle,
uint32_t format, uint32_t property,
int groupCode, int depth,
MtpDataPacket& packet) {
@@ -715,16 +684,16 @@
checkAndClearExceptionFromCallback(env, __FUNCTION__);
if (!list)
return MTP_RESPONSE_GENERAL_ERROR;
- int count = env->GetIntField(list, field_mCount);
- MtpResponseCode result = env->GetIntField(list, field_mResult);
+ int count = env->CallIntMethod(list, method_getCount);
+ MtpResponseCode result = env->CallIntMethod(list, method_getCode);
packet.putUInt32(count);
if (count > 0) {
- jintArray objectHandlesArray = (jintArray)env->GetObjectField(list, field_mObjectHandles);
- jintArray propertyCodesArray = (jintArray)env->GetObjectField(list, field_mPropertyCodes);
- jintArray dataTypesArray = (jintArray)env->GetObjectField(list, field_mDataTypes);
- jlongArray longValuesArray = (jlongArray)env->GetObjectField(list, field_mLongValues);
- jobjectArray stringValuesArray = (jobjectArray)env->GetObjectField(list, field_mStringValues);
+ jintArray objectHandlesArray = (jintArray)env->CallObjectMethod(list, method_getObjectHandles);
+ jintArray propertyCodesArray = (jintArray)env->CallObjectMethod(list, method_getPropertyCodes);
+ jintArray dataTypesArray = (jintArray)env->CallObjectMethod(list, method_getDataTypes);
+ jlongArray longValuesArray = (jlongArray)env->CallObjectMethod(list, method_getLongValues);
+ jobjectArray stringValuesArray = (jobjectArray)env->CallObjectMethod(list, method_getStringValues);
jint* objectHandles = env->GetIntArrayElements(objectHandlesArray, 0);
jint* propertyCodes = env->GetIntArrayElements(propertyCodesArray, 0);
@@ -781,7 +750,7 @@
break;
}
default:
- ALOGE("bad or unsupported data type in MyMtpDatabase::getObjectPropertyList");
+ ALOGE("bad or unsupported data type in MtpDatabase::getObjectPropertyList");
break;
}
}
@@ -789,16 +758,13 @@
env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0);
env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0);
env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0);
- if (longValues)
- env->ReleaseLongArrayElements(longValuesArray, longValues, 0);
+ env->ReleaseLongArrayElements(longValuesArray, longValues, 0);
env->DeleteLocalRef(objectHandlesArray);
env->DeleteLocalRef(propertyCodesArray);
env->DeleteLocalRef(dataTypesArray);
- if (longValuesArray)
- env->DeleteLocalRef(longValuesArray);
- if (stringValuesArray)
- env->DeleteLocalRef(stringValuesArray);
+ env->DeleteLocalRef(longValuesArray);
+ env->DeleteLocalRef(stringValuesArray);
}
env->DeleteLocalRef(list);
@@ -822,7 +788,7 @@
return exif_get_long(e->data, o);
}
-MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle,
+MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle,
MtpObjectInfo& info) {
MtpString path;
int64_t length;
@@ -914,7 +880,7 @@
return MTP_RESPONSE_OK;
}
-void* MyMtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) {
+void* MtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) {
MtpString path;
int64_t length;
MtpObjectFormat format;
@@ -979,7 +945,7 @@
return result;
}
-MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle,
+MtpResponseCode MtpDatabase::getObjectFilePath(MtpObjectHandle handle,
MtpString& outFilePath,
int64_t& outFileLength,
MtpObjectFormat& outFormat) {
@@ -1005,26 +971,60 @@
return result;
}
-MtpResponseCode MyMtpDatabase::deleteFile(MtpObjectHandle handle) {
+MtpResponseCode MtpDatabase::beginDeleteObject(MtpObjectHandle handle) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
- MtpResponseCode result = env->CallIntMethod(mDatabase, method_deleteFile, (jint)handle);
+ MtpResponseCode result = env->CallIntMethod(mDatabase, method_beginDeleteObject, (jint)handle);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return result;
}
-MtpResponseCode MyMtpDatabase::moveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
- MtpStorageID newStorage, MtpString &newPath) {
+void MtpDatabase::endDeleteObject(MtpObjectHandle handle, bool succeeded) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
- jstring stringValue = env->NewStringUTF((const char *) newPath);
- MtpResponseCode result = env->CallIntMethod(mDatabase, method_moveObject,
- (jint)handle, (jint)newParent, (jint) newStorage, stringValue);
+ env->CallVoidMethod(mDatabase, method_endDeleteObject, (jint)handle, (jboolean) succeeded);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
- env->DeleteLocalRef(stringValue);
+}
+
+MtpResponseCode MtpDatabase::beginMoveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+ MtpStorageID newStorage) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ MtpResponseCode result = env->CallIntMethod(mDatabase, method_beginMoveObject,
+ (jint)handle, (jint)newParent, (jint) newStorage);
+
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
return result;
}
+void MtpDatabase::endMoveObject(MtpObjectHandle oldParent, MtpObjectHandle newParent,
+ MtpStorageID oldStorage, MtpStorageID newStorage,
+ MtpObjectHandle handle, bool succeeded) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(mDatabase, method_endMoveObject,
+ (jint)oldParent, (jint) newParent, (jint) oldStorage, (jint) newStorage,
+ (jint) handle, (jboolean) succeeded);
+
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+MtpResponseCode MtpDatabase::beginCopyObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+ MtpStorageID newStorage) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ MtpResponseCode result = env->CallIntMethod(mDatabase, method_beginCopyObject,
+ (jint)handle, (jint)newParent, (jint) newStorage);
+
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return result;
+}
+
+void MtpDatabase::endCopyObject(MtpObjectHandle handle, bool succeeded) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(mDatabase, method_endCopyObject, (jint)handle, (jboolean)succeeded);
+
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+
struct PropertyTableEntry {
MtpObjectProperty property;
int type;
@@ -1066,7 +1066,7 @@
{ MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE, MTP_TYPE_UINT32 },
};
-bool MyMtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
+bool MtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]);
const PropertyTableEntry* entry = kObjectPropertyTable;
for (int i = 0; i < count; i++, entry++) {
@@ -1078,7 +1078,7 @@
return false;
}
-bool MyMtpDatabase::getDevicePropertyInfo(MtpDeviceProperty property, int& type) {
+bool MtpDatabase::getDevicePropertyInfo(MtpDeviceProperty property, int& type) {
int count = sizeof(kDevicePropertyTable) / sizeof(kDevicePropertyTable[0]);
const PropertyTableEntry* entry = kDevicePropertyTable;
for (int i = 0; i < count; i++, entry++) {
@@ -1090,7 +1090,7 @@
return false;
}
-MtpObjectHandleList* MyMtpDatabase::getObjectReferences(MtpObjectHandle handle) {
+MtpObjectHandleList* MtpDatabase::getObjectReferences(MtpObjectHandle handle) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectReferences,
(jint)handle);
@@ -1108,7 +1108,7 @@
return list;
}
-MtpResponseCode MyMtpDatabase::setObjectReferences(MtpObjectHandle handle,
+MtpResponseCode MtpDatabase::setObjectReferences(MtpObjectHandle handle,
MtpObjectHandleList* references) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
int count = references->size();
@@ -1129,7 +1129,7 @@
return result;
}
-MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property,
+MtpProperty* MtpDatabase::getObjectPropertyDesc(MtpObjectProperty property,
MtpObjectFormat format) {
static const int channelEnum[] = {
1, // mono
@@ -1210,67 +1210,65 @@
return result;
}
-MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
+MtpProperty* MtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
MtpProperty* result = NULL;
bool writable = false;
- switch (property) {
- case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
- case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
- writable = true;
- // fall through
- case MTP_DEVICE_PROPERTY_IMAGE_SIZE: {
- result = new MtpProperty(property, MTP_TYPE_STR, writable);
-
- // get current value
- jint ret = env->CallIntMethod(mDatabase, method_getDeviceProperty,
- (jint)property, mLongBuffer, mStringBuffer);
- if (ret == MTP_RESPONSE_OK) {
+ // get current value
+ jint ret = env->CallIntMethod(mDatabase, method_getDeviceProperty,
+ (jint)property, mLongBuffer, mStringBuffer);
+ if (ret == MTP_RESPONSE_OK) {
+ switch (property) {
+ case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
+ case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
+ writable = true;
+ // fall through
+ case MTP_DEVICE_PROPERTY_IMAGE_SIZE:
+ {
+ result = new MtpProperty(property, MTP_TYPE_STR, writable);
jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
result->setCurrentValue(str);
// for read-only properties it is safe to assume current value is default value
if (!writable)
result->setDefaultValue(str);
env->ReleaseCharArrayElements(mStringBuffer, str, 0);
- } else {
- ALOGE("unable to read device property, response: %04X", ret);
+ break;
}
- break;
+ case MTP_DEVICE_PROPERTY_BATTERY_LEVEL:
+ {
+ result = new MtpProperty(property, MTP_TYPE_UINT8);
+ jlong* arr = env->GetLongArrayElements(mLongBuffer, 0);
+ result->setFormRange(0, arr[1], 1);
+ result->mCurrentValue.u.u8 = (uint8_t) arr[0];
+ env->ReleaseLongArrayElements(mLongBuffer, arr, 0);
+ break;
+ }
+ case MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE:
+ {
+ jlong* arr = env->GetLongArrayElements(mLongBuffer, 0);
+ result = new MtpProperty(property, MTP_TYPE_UINT32);
+ result->mCurrentValue.u.u32 = (uint32_t) arr[0];
+ env->ReleaseLongArrayElements(mLongBuffer, arr, 0);
+ break;
+ }
+ default:
+ ALOGE("Unrecognized property %x", property);
}
- case MTP_DEVICE_PROPERTY_BATTERY_LEVEL:
- result = new MtpProperty(property, MTP_TYPE_UINT8);
- result->setFormRange(0, env->GetIntField(mDatabase, field_batteryScale), 1);
- result->mCurrentValue.u.u8 = (uint8_t)env->GetIntField(mDatabase, field_batteryLevel);
- break;
- case MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE:
- result = new MtpProperty(property, MTP_TYPE_UINT32);
- result->mCurrentValue.u.u32 = (uint32_t)env->GetIntField(mDatabase, field_deviceType);
- break;
+ } else {
+ ALOGE("unable to read device property, response: %04X", ret);
}
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return result;
}
-void MyMtpDatabase::sessionStarted() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- env->CallVoidMethod(mDatabase, method_sessionStarted);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-}
-
-void MyMtpDatabase::sessionEnded() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- env->CallVoidMethod(mDatabase, method_sessionEnded);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-}
-
// ----------------------------------------------------------------------------
static void
android_mtp_MtpDatabase_setup(JNIEnv *env, jobject thiz)
{
- MyMtpDatabase* database = new MyMtpDatabase(env, thiz);
+ MtpDatabase* database = new MtpDatabase(env, thiz);
env->SetLongField(thiz, field_context, (jlong)database);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
}
@@ -1278,7 +1276,7 @@
static void
android_mtp_MtpDatabase_finalize(JNIEnv *env, jobject thiz)
{
- MyMtpDatabase* database = (MyMtpDatabase *)env->GetLongField(thiz, field_context);
+ MtpDatabase* database = (MtpDatabase *)env->GetLongField(thiz, field_context);
database->cleanup(env);
delete database;
env->SetLongField(thiz, field_context, 0);
@@ -1305,6 +1303,13 @@
(void *)android_mtp_MtpPropertyGroup_format_date_time},
};
+#define GET_METHOD_ID(name, jclass, signature) \
+ method_##name = env->GetMethodID(jclass, #name, signature); \
+ if (method_##name == NULL) { \
+ ALOGE("Can't find " #name); \
+ return -1; \
+ } \
+
int register_android_mtp_MtpDatabase(JNIEnv *env)
{
jclass clazz;
@@ -1314,175 +1319,48 @@
ALOGE("Can't find android/mtp/MtpDatabase");
return -1;
}
- method_beginSendObject = env->GetMethodID(clazz, "beginSendObject", "(Ljava/lang/String;IIIJJ)I");
- if (method_beginSendObject == NULL) {
- ALOGE("Can't find beginSendObject");
- return -1;
- }
- method_endSendObject = env->GetMethodID(clazz, "endSendObject", "(Ljava/lang/String;IIZ)V");
- if (method_endSendObject == NULL) {
- ALOGE("Can't find endSendObject");
- return -1;
- }
- method_doScanDirectory = env->GetMethodID(clazz, "doScanDirectory", "(Ljava/lang/String;)V");
- if (method_doScanDirectory == NULL) {
- ALOGE("Can't find doScanDirectory");
- return -1;
- }
- method_getObjectList = env->GetMethodID(clazz, "getObjectList", "(III)[I");
- if (method_getObjectList == NULL) {
- ALOGE("Can't find getObjectList");
- return -1;
- }
- method_getNumObjects = env->GetMethodID(clazz, "getNumObjects", "(III)I");
- if (method_getNumObjects == NULL) {
- ALOGE("Can't find getNumObjects");
- return -1;
- }
- method_getSupportedPlaybackFormats = env->GetMethodID(clazz, "getSupportedPlaybackFormats", "()[I");
- if (method_getSupportedPlaybackFormats == NULL) {
- ALOGE("Can't find getSupportedPlaybackFormats");
- return -1;
- }
- method_getSupportedCaptureFormats = env->GetMethodID(clazz, "getSupportedCaptureFormats", "()[I");
- if (method_getSupportedCaptureFormats == NULL) {
- ALOGE("Can't find getSupportedCaptureFormats");
- return -1;
- }
- method_getSupportedObjectProperties = env->GetMethodID(clazz, "getSupportedObjectProperties", "(I)[I");
- if (method_getSupportedObjectProperties == NULL) {
- ALOGE("Can't find getSupportedObjectProperties");
- return -1;
- }
- method_getSupportedDeviceProperties = env->GetMethodID(clazz, "getSupportedDeviceProperties", "()[I");
- if (method_getSupportedDeviceProperties == NULL) {
- ALOGE("Can't find getSupportedDeviceProperties");
- return -1;
- }
- method_setObjectProperty = env->GetMethodID(clazz, "setObjectProperty", "(IIJLjava/lang/String;)I");
- if (method_setObjectProperty == NULL) {
- ALOGE("Can't find setObjectProperty");
- return -1;
- }
- method_getDeviceProperty = env->GetMethodID(clazz, "getDeviceProperty", "(I[J[C)I");
- if (method_getDeviceProperty == NULL) {
- ALOGE("Can't find getDeviceProperty");
- return -1;
- }
- method_setDeviceProperty = env->GetMethodID(clazz, "setDeviceProperty", "(IJLjava/lang/String;)I");
- if (method_setDeviceProperty == NULL) {
- ALOGE("Can't find setDeviceProperty");
- return -1;
- }
- method_getObjectPropertyList = env->GetMethodID(clazz, "getObjectPropertyList",
- "(IIIII)Landroid/mtp/MtpPropertyList;");
- if (method_getObjectPropertyList == NULL) {
- ALOGE("Can't find getObjectPropertyList");
- return -1;
- }
- method_getObjectInfo = env->GetMethodID(clazz, "getObjectInfo", "(I[I[C[J)Z");
- if (method_getObjectInfo == NULL) {
- ALOGE("Can't find getObjectInfo");
- return -1;
- }
- method_getObjectFilePath = env->GetMethodID(clazz, "getObjectFilePath", "(I[C[J)I");
- if (method_getObjectFilePath == NULL) {
- ALOGE("Can't find getObjectFilePath");
- return -1;
- }
- method_deleteFile = env->GetMethodID(clazz, "deleteFile", "(I)I");
- if (method_deleteFile == NULL) {
- ALOGE("Can't find deleteFile");
- return -1;
- }
- method_moveObject = env->GetMethodID(clazz, "moveObject", "(IIILjava/lang/String;)I");
- if (method_moveObject == NULL) {
- ALOGE("Can't find moveObject");
- return -1;
- }
- method_getObjectReferences = env->GetMethodID(clazz, "getObjectReferences", "(I)[I");
- if (method_getObjectReferences == NULL) {
- ALOGE("Can't find getObjectReferences");
- return -1;
- }
- method_setObjectReferences = env->GetMethodID(clazz, "setObjectReferences", "(I[I)I");
- if (method_setObjectReferences == NULL) {
- ALOGE("Can't find setObjectReferences");
- return -1;
- }
- method_sessionStarted = env->GetMethodID(clazz, "sessionStarted", "()V");
- if (method_sessionStarted == NULL) {
- ALOGE("Can't find sessionStarted");
- return -1;
- }
- method_sessionEnded = env->GetMethodID(clazz, "sessionEnded", "()V");
- if (method_sessionEnded == NULL) {
- ALOGE("Can't find sessionEnded");
- return -1;
- }
+ GET_METHOD_ID(beginSendObject, clazz, "(Ljava/lang/String;III)I");
+ GET_METHOD_ID(endSendObject, clazz, "(IZ)V");
+ GET_METHOD_ID(rescanFile, clazz, "(Ljava/lang/String;II)V");
+ GET_METHOD_ID(getObjectList, clazz, "(III)[I");
+ GET_METHOD_ID(getNumObjects, clazz, "(III)I");
+ GET_METHOD_ID(getSupportedPlaybackFormats, clazz, "()[I");
+ GET_METHOD_ID(getSupportedCaptureFormats, clazz, "()[I");
+ GET_METHOD_ID(getSupportedObjectProperties, clazz, "(I)[I");
+ GET_METHOD_ID(getSupportedDeviceProperties, clazz, "()[I");
+ GET_METHOD_ID(setObjectProperty, clazz, "(IIJLjava/lang/String;)I");
+ GET_METHOD_ID(getDeviceProperty, clazz, "(I[J[C)I");
+ GET_METHOD_ID(setDeviceProperty, clazz, "(IJLjava/lang/String;)I");
+ GET_METHOD_ID(getObjectPropertyList, clazz, "(IIIII)Landroid/mtp/MtpPropertyList;");
+ GET_METHOD_ID(getObjectInfo, clazz, "(I[I[C[J)Z");
+ GET_METHOD_ID(getObjectFilePath, clazz, "(I[C[J)I");
+ GET_METHOD_ID(beginDeleteObject, clazz, "(I)I");
+ GET_METHOD_ID(endDeleteObject, clazz, "(IZ)V");
+ GET_METHOD_ID(beginMoveObject, clazz, "(III)I");
+ GET_METHOD_ID(endMoveObject, clazz, "(IIIIIZ)V");
+ GET_METHOD_ID(beginCopyObject, clazz, "(III)I");
+ GET_METHOD_ID(endCopyObject, clazz, "(IZ)V");
+ GET_METHOD_ID(getObjectReferences, clazz, "(I)[I");
+ GET_METHOD_ID(setObjectReferences, clazz, "(I[I)I");
field_context = env->GetFieldID(clazz, "mNativeContext", "J");
if (field_context == NULL) {
ALOGE("Can't find MtpDatabase.mNativeContext");
return -1;
}
- field_batteryLevel = env->GetFieldID(clazz, "mBatteryLevel", "I");
- if (field_batteryLevel == NULL) {
- ALOGE("Can't find MtpDatabase.mBatteryLevel");
- return -1;
- }
- field_batteryScale = env->GetFieldID(clazz, "mBatteryScale", "I");
- if (field_batteryScale == NULL) {
- ALOGE("Can't find MtpDatabase.mBatteryScale");
- return -1;
- }
- field_deviceType = env->GetFieldID(clazz, "mDeviceType", "I");
- if (field_deviceType == NULL) {
- ALOGE("Can't find MtpDatabase.mDeviceType");
- return -1;
- }
- // now set up fields for MtpPropertyList class
clazz = env->FindClass("android/mtp/MtpPropertyList");
if (clazz == NULL) {
ALOGE("Can't find android/mtp/MtpPropertyList");
return -1;
}
- field_mCount = env->GetFieldID(clazz, "mCount", "I");
- if (field_mCount == NULL) {
- ALOGE("Can't find MtpPropertyList.mCount");
- return -1;
- }
- field_mResult = env->GetFieldID(clazz, "mResult", "I");
- if (field_mResult == NULL) {
- ALOGE("Can't find MtpPropertyList.mResult");
- return -1;
- }
- field_mObjectHandles = env->GetFieldID(clazz, "mObjectHandles", "[I");
- if (field_mObjectHandles == NULL) {
- ALOGE("Can't find MtpPropertyList.mObjectHandles");
- return -1;
- }
- field_mPropertyCodes = env->GetFieldID(clazz, "mPropertyCodes", "[I");
- if (field_mPropertyCodes == NULL) {
- ALOGE("Can't find MtpPropertyList.mPropertyCodes");
- return -1;
- }
- field_mDataTypes = env->GetFieldID(clazz, "mDataTypes", "[I");
- if (field_mDataTypes == NULL) {
- ALOGE("Can't find MtpPropertyList.mDataTypes");
- return -1;
- }
- field_mLongValues = env->GetFieldID(clazz, "mLongValues", "[J");
- if (field_mLongValues == NULL) {
- ALOGE("Can't find MtpPropertyList.mLongValues");
- return -1;
- }
- field_mStringValues = env->GetFieldID(clazz, "mStringValues", "[Ljava/lang/String;");
- if (field_mStringValues == NULL) {
- ALOGE("Can't find MtpPropertyList.mStringValues");
- return -1;
- }
+ GET_METHOD_ID(getCode, clazz, "()I");
+ GET_METHOD_ID(getCount, clazz, "()I");
+ GET_METHOD_ID(getObjectHandles, clazz, "()[I");
+ GET_METHOD_ID(getPropertyCodes, clazz, "()[I");
+ GET_METHOD_ID(getDataTypes, clazz, "()[I");
+ GET_METHOD_ID(getLongValues, clazz, "()[J");
+ GET_METHOD_ID(getStringValues, clazz, "()[Ljava/lang/String;");
if (AndroidRuntime::registerNativeMethods(env,
"android/mtp/MtpDatabase", gMtpDatabaseMethods, NELEM(gMtpDatabaseMethods)))
diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp
index 6ce104d..c76cebe 100644
--- a/media/jni/android_mtp_MtpServer.cpp
+++ b/media/jni/android_mtp_MtpServer.cpp
@@ -41,7 +41,6 @@
static jfieldID field_MtpStorage_storageId;
static jfieldID field_MtpStorage_path;
static jfieldID field_MtpStorage_description;
-static jfieldID field_MtpStorage_reserveSpace;
static jfieldID field_MtpStorage_removable;
static jfieldID field_MtpStorage_maxFileSize;
@@ -50,7 +49,7 @@
// ----------------------------------------------------------------------------
// in android_mtp_MtpDatabase.cpp
-extern MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database);
+extern IMtpDatabase* getMtpDatabase(JNIEnv *env, jobject database);
static inline MtpServer* getMtpServer(JNIEnv *env, jobject thiz) {
return (MtpServer*)env->GetLongField(thiz, field_MtpServer_nativeContext);
@@ -162,7 +161,6 @@
jint storageID = env->GetIntField(jstorage, field_MtpStorage_storageId);
jstring path = (jstring)env->GetObjectField(jstorage, field_MtpStorage_path);
jstring description = (jstring)env->GetObjectField(jstorage, field_MtpStorage_description);
- jlong reserveSpace = env->GetLongField(jstorage, field_MtpStorage_reserveSpace);
jboolean removable = env->GetBooleanField(jstorage, field_MtpStorage_removable);
jlong maxFileSize = env->GetLongField(jstorage, field_MtpStorage_maxFileSize);
@@ -171,7 +169,7 @@
const char *descriptionStr = env->GetStringUTFChars(description, NULL);
if (descriptionStr != NULL) {
MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr,
- reserveSpace, removable, maxFileSize);
+ removable, maxFileSize);
server->addStorage(storage);
env->ReleaseStringUTFChars(path, pathStr);
env->ReleaseStringUTFChars(description, descriptionStr);
@@ -241,11 +239,6 @@
ALOGE("Can't find MtpStorage.mDescription");
return -1;
}
- field_MtpStorage_reserveSpace = env->GetFieldID(clazz, "mReserveSpace", "J");
- if (field_MtpStorage_reserveSpace == NULL) {
- ALOGE("Can't find MtpStorage.mReserveSpace");
- return -1;
- }
field_MtpStorage_removable = env->GetFieldID(clazz, "mRemovable", "Z");
if (field_MtpStorage_removable == NULL) {
ALOGE("Can't find MtpStorage.mRemovable");
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 21410ea..b4ca88c 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -24,7 +24,6 @@
#define USE_SHARED_MEM_BUFFER
#include <media/AudioTrack.h>
-#include <media/mediaplayer.h>
#include "SoundPool.h"
#include "SoundPoolThread.h"
#include <media/AudioPolicyHelper.h>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index e628b68..fadb76d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -16,10 +16,18 @@
package com.android.mediaframeworktest.integration;
+import static android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.ICameraService;
-import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
@@ -41,13 +49,10 @@
import android.util.Log;
import android.view.Surface;
-import static android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW;
-
import com.android.mediaframeworktest.MediaFrameworkIntegrationTestRunner;
import org.mockito.ArgumentCaptor;
-import org.mockito.compat.ArgumentMatcher;
-import static org.mockito.Mockito.*;
+import org.mockito.ArgumentMatcher;
public class CameraDeviceBinderTest extends AndroidTestCase {
private static String TAG = "CameraDeviceBinderTest";
@@ -166,10 +171,10 @@
}
}
- class IsMetadataNotEmpty extends ArgumentMatcher<CameraMetadataNative> {
+ class IsMetadataNotEmpty implements ArgumentMatcher<CameraMetadataNative> {
@Override
- public boolean matchesObject(Object obj) {
- return !((CameraMetadataNative) obj).isEmpty();
+ public boolean matches(CameraMetadataNative obj) {
+ return !obj.isEmpty();
}
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
index 712039d..74bf1a2 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java
@@ -16,13 +16,13 @@
package com.android.mediaframeworktest.unit;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
import android.content.ContentProviderClient;
import android.content.ContentValues;
@@ -36,10 +36,9 @@
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.SmallTest;
-import org.hamcrest.Description;
+import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.compat.ArgumentMatcher;
public class MediaInserterTest extends InstrumentationTestCase {
@@ -59,7 +58,7 @@
private static final Uri sImagesUri = Images.Media.getContentUri(sVolumeName);
private static final Uri sFilesUri = Files.getContentUri(sVolumeName);
- private static class MediaUriMatcher extends ArgumentMatcher<Uri> {
+ private static class MediaUriMatcher implements ArgumentMatcher<Uri> {
private final Uri mUri;
private MediaUriMatcher(Uri uri) {
@@ -67,15 +66,8 @@
}
@Override
- public boolean matchesObject(Object argument) {
- if (!(argument instanceof Uri)) {
- return false;
- }
-
- Uri actualUri = (Uri) argument;
- if (actualUri == mUri)
- return true;
- return false;
+ public boolean matches(Uri actualUri) {
+ return actualUri == mUri;
}
@Override
diff --git a/media/tests/MtpTests/Android.mk b/media/tests/MtpTests/Android.mk
new file mode 100644
index 0000000..616e600
--- /dev/null
+++ b/media/tests/MtpTests/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_PACKAGE_NAME := MtpTests
+
+include $(BUILD_PACKAGE)
diff --git a/media/tests/MtpTests/AndroidManifest.xml b/media/tests/MtpTests/AndroidManifest.xml
new file mode 100644
index 0000000..21e2b01
--- /dev/null
+++ b/media/tests/MtpTests/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.mtp" >
+
+ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" />
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.mtp"
+ android:label="MtpTests"/>
+</manifest>
diff --git a/media/tests/MtpTests/AndroidTest.xml b/media/tests/MtpTests/AndroidTest.xml
new file mode 100644
index 0000000..a61a3b4
--- /dev/null
+++ b/media/tests/MtpTests/AndroidTest.xml
@@ -0,0 +1,15 @@
+<configuration description="Runs sample instrumentation test.">
+ <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="MtpTests.apk"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-tag" value="MtpTests"/>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.mtp"/>
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
new file mode 100644
index 0000000..0d7f3fe
--- /dev/null
+++ b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
@@ -0,0 +1,1657 @@
+/*
+ * 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.mtp;
+
+import android.os.FileUtils;
+import android.os.UserHandle;
+import android.os.storage.StorageVolume;
+import android.support.test.filters.SmallTest;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.UUID;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+/**
+ * Tests for MtpStorageManager functionality.
+ */
+@RunWith(JUnit4.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class MtpStorageManagerTest {
+ private static final String TAG = MtpStorageManagerTest.class.getSimpleName();
+
+ private static final String TEMP_DIR = InstrumentationRegistry.getContext().getFilesDir()
+ + "/" + TAG + "/";
+ private static final File TEMP_DIR_FILE = new File(TEMP_DIR);
+
+ private MtpStorageManager manager;
+
+ private ArrayList<Integer> objectsAdded;
+ private ArrayList<Integer> objectsRemoved;
+
+ private File mainStorageDir;
+ private File secondaryStorageDir;
+
+ private MtpStorage mainMtpStorage;
+ private MtpStorage secondaryMtpStorage;
+
+ static {
+ MtpStorageManager.sDebug = true;
+ }
+
+ private static void logMethodName() {
+ Log.d(TAG, Thread.currentThread().getStackTrace()[3].getMethodName());
+ }
+
+ private static File createNewFile(File parent) {
+ return createNewFile(parent, UUID.randomUUID().toString());
+ }
+
+ private static File createNewFile(File parent, String name) {
+ try {
+ File ret = new File(parent, name);
+ if (!ret.createNewFile())
+ throw new AssertionError("Failed to create file");
+ return ret;
+ } catch (IOException e) {
+ throw new AssertionError(e.getMessage());
+ }
+ }
+
+ private static File createNewDir(File parent, String name) {
+ File ret = new File(parent, name);
+ if (!ret.mkdir())
+ throw new AssertionError("Failed to create file");
+ return ret;
+ }
+
+ private static File createNewDir(File parent) {
+ return createNewDir(parent, UUID.randomUUID().toString());
+ }
+
+ @Before
+ public void before() {
+ Assert.assertTrue(TEMP_DIR_FILE.mkdir());
+ mainStorageDir = createNewDir(TEMP_DIR_FILE);
+ secondaryStorageDir = createNewDir(TEMP_DIR_FILE);
+
+ StorageVolume mainStorage = new StorageVolume("1", mainStorageDir, "", true, false, true,
+ false, -1, UserHandle.CURRENT, "", "");
+ StorageVolume secondaryStorage = new StorageVolume("2", secondaryStorageDir, "", false,
+ false, true, false, -1, UserHandle.CURRENT, "", "");
+
+ objectsAdded = new ArrayList<>();
+ objectsRemoved = new ArrayList<>();
+
+ manager = new MtpStorageManager(new MtpStorageManager.MtpNotifier() {
+ @Override
+ public void sendObjectAdded(int id) {
+ objectsAdded.add(id);
+ }
+
+ @Override
+ public void sendObjectRemoved(int id) {
+ objectsRemoved.add(id);
+ }
+ }, null);
+
+ mainMtpStorage = manager.addMtpStorage(mainStorage);
+ secondaryMtpStorage = manager.addMtpStorage(secondaryStorage);
+ }
+
+ @After
+ public void after() {
+ manager.close();
+ FileUtils.deleteContentsAndDir(TEMP_DIR_FILE);
+ }
+
+ /** MtpObject getter tests. **/
+
+ @Test
+ @SmallTest
+ public void testMtpObjectGetNameRoot() {
+ logMethodName();
+ MtpStorageManager.MtpObject obj = manager.getStorageRoot(mainMtpStorage.getStorageId());
+ Assert.assertEquals(obj.getName(), mainStorageDir.getPath());
+ }
+
+ @Test
+ @SmallTest
+ public void testMtpObjectGetNameNonRoot() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.findFirst().get().getName(), newFile.getName());
+ }
+
+ @Test
+ @SmallTest
+ public void testMtpObjectGetIdRoot() {
+ logMethodName();
+ MtpStorageManager.MtpObject obj = manager.getStorageRoot(mainMtpStorage.getStorageId());
+ Assert.assertEquals(obj.getId(), mainMtpStorage.getStorageId());
+ }
+
+ @Test
+ @SmallTest
+ public void testMtpObjectGetIdNonRoot() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.findFirst().get().getId(), 1);
+ }
+
+ @Test
+ @SmallTest
+ public void testMtpObjectIsDirTrue() {
+ logMethodName();
+ MtpStorageManager.MtpObject obj = manager.getStorageRoot(mainMtpStorage.getStorageId());
+ Assert.assertTrue(obj.isDir());
+ }
+
+ @Test
+ @SmallTest
+ public void testMtpObjectIsDirFalse() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir, "TEST123.mp3");
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertFalse(stream.findFirst().get().isDir());
+ }
+
+ @Test
+ @SmallTest
+ public void testMtpObjectGetFormatDir() {
+ logMethodName();
+ File newFile = createNewDir(mainStorageDir);
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.findFirst().get().getFormat(), MtpConstants.FORMAT_ASSOCIATION);
+ }
+
+ @Test
+ @SmallTest
+ public void testMtpObjectGetFormatNonDir() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir, "TEST123.mp3");
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.findFirst().get().getFormat(), MtpConstants.FORMAT_MP3);
+ }
+
+ @Test
+ @SmallTest
+ public void testMtpObjectGetStorageId() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.findFirst().get().getStorageId(), mainMtpStorage.getStorageId());
+ }
+
+ @Test
+ @SmallTest
+ public void testMtpObjectGetLastModified() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.findFirst().get().getModifiedTime(),
+ newFile.lastModified() / 1000);
+ }
+
+ @Test
+ @SmallTest
+ public void testMtpObjectGetParent() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.findFirst().get().getParent(),
+ manager.getStorageRoot(mainMtpStorage.getStorageId()));
+ }
+
+ @Test
+ @SmallTest
+ public void testMtpObjectGetRoot() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.findFirst().get().getRoot(),
+ manager.getStorageRoot(mainMtpStorage.getStorageId()));
+ }
+
+ @Test
+ @SmallTest
+ public void testMtpObjectGetPath() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.findFirst().get().getPath().toString(), newFile.getPath());
+ }
+
+ @Test
+ @SmallTest
+ public void testMtpObjectGetSize() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ try {
+ new FileOutputStream(newFile).write(new byte[] {0, 0, 0, 0, 0, 0, 0, 0});
+ } catch (IOException e) {
+ Assert.fail();
+ }
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.findFirst().get().getSize(), 8);
+ }
+
+ @Test
+ @SmallTest
+ public void testMtpObjectGetSizeDir() {
+ logMethodName();
+ File newDir = createNewDir(mainStorageDir);
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.findFirst().get().getSize(), 0);
+ }
+
+ /** MtpStorageManager cache access tests. **/
+
+ @Test
+ @SmallTest
+ public void testAddMtpStorage() {
+ logMethodName();
+ Assert.assertEquals(mainMtpStorage.getPath(), mainStorageDir.getPath());
+ Assert.assertNotNull(manager.getStorageRoot(mainMtpStorage.getStorageId()));
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRemoveMtpStorage() {
+ logMethodName();
+ File newFile = createNewFile(secondaryStorageDir);
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ secondaryMtpStorage.getStorageId());
+ Assert.assertEquals(stream.count(), 1);
+
+ manager.removeMtpStorage(secondaryMtpStorage);
+ Assert.assertNull(manager.getStorageRoot(secondaryMtpStorage.getStorageId()));
+ Assert.assertNull(manager.getObject(1));
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetByPath() {
+ logMethodName();
+ File newFile = createNewFile(createNewDir(createNewDir(mainStorageDir)));
+
+ MtpStorageManager.MtpObject obj = manager.getByPath(newFile.getPath());
+ Assert.assertNotNull(obj);
+ Assert.assertEquals(obj.getPath().toString(), newFile.getPath());
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetByPathError() {
+ logMethodName();
+ File newFile = createNewFile(createNewDir(createNewDir(mainStorageDir)));
+
+ MtpStorageManager.MtpObject obj = manager.getByPath(newFile.getPath() + "q");
+ Assert.assertNull(obj);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetObject() {
+ logMethodName();
+ File newFile = createNewFile(createNewDir(createNewDir(mainStorageDir)));
+ MtpStorageManager.MtpObject obj = manager.getByPath(newFile.getPath());
+ Assert.assertNotNull(obj);
+
+ Assert.assertEquals(manager.getObject(obj.getId()), obj);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetObjectError() {
+ logMethodName();
+ File newFile = createNewFile(createNewDir(createNewDir(mainStorageDir)));
+
+ Assert.assertNull(manager.getObject(42));
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetStorageRoot() {
+ logMethodName();
+ MtpStorageManager.MtpObject obj = manager.getStorageRoot(mainMtpStorage.getStorageId());
+ Assert.assertEquals(obj.getPath().toString(), mainStorageDir.getPath());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetObjectsParent() {
+ logMethodName();
+ File newDir = createNewDir(createNewDir(mainStorageDir));
+ File newFile = createNewFile(newDir);
+ File newMP3File = createNewFile(newDir, "lalala.mp3");
+ MtpStorageManager.MtpObject parent = manager.getByPath(newDir.getPath());
+ Assert.assertNotNull(parent);
+
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(parent.getId(), 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.count(), 2);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetObjectsFormat() {
+ logMethodName();
+ File newDir = createNewDir(createNewDir(mainStorageDir));
+ File newFile = createNewFile(newDir);
+ File newMP3File = createNewFile(newDir, "lalala.mp3");
+ MtpStorageManager.MtpObject parent = manager.getByPath(newDir.getPath());
+ Assert.assertNotNull(parent);
+
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(parent.getId(),
+ MtpConstants.FORMAT_MP3, mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.findFirst().get().getPath().toString(), newMP3File.toString());
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetObjectsRoot() {
+ logMethodName();
+ File newDir = createNewDir(mainStorageDir);
+ File newFile = createNewFile(mainStorageDir);
+ File newMP3File = createNewFile(newDir, "lalala.mp3");
+
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.count(), 2);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetObjectsAll() {
+ logMethodName();
+ File newDir = createNewDir(mainStorageDir);
+ File newFile = createNewFile(mainStorageDir);
+ File newMP3File = createNewFile(newDir, "lalala.mp3");
+
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.count(), 3);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetObjectsAllStorages() {
+ logMethodName();
+ File newDir = createNewDir(mainStorageDir);
+ createNewFile(mainStorageDir);
+ createNewFile(newDir, "lalala.mp3");
+ File newDir2 = createNewDir(secondaryStorageDir);
+ createNewFile(secondaryStorageDir);
+ createNewFile(newDir2, "lalala.mp3");
+
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0, 0, 0xFFFFFFFF);
+ Assert.assertEquals(stream.count(), 6);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetObjectsAllStoragesRoot() {
+ logMethodName();
+ File newDir = createNewDir(mainStorageDir);
+ createNewFile(mainStorageDir);
+ createNewFile(newDir, "lalala.mp3");
+ File newDir2 = createNewDir(secondaryStorageDir);
+ createNewFile(secondaryStorageDir);
+ createNewFile(newDir2, "lalala.mp3");
+
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0, 0xFFFFFFFF);
+ Assert.assertEquals(stream.count(), 4);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ /** MtpStorageManager event handling tests. **/
+
+ @Test
+ @SmallTest
+ public void testObjectAdded() {
+ logMethodName();
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.count(), 0);
+
+ File newFile = createNewFile(mainStorageDir);
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertEquals(manager.getObject(objectsAdded.get(0)).getPath().toString(),
+ newFile.getPath());
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testObjectAddedDir() {
+ logMethodName();
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.count(), 0);
+
+ File newDir = createNewDir(mainStorageDir);
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertEquals(manager.getObject(objectsAdded.get(0)).getPath().toString(),
+ newDir.getPath());
+ Assert.assertTrue(manager.getObject(objectsAdded.get(0)).isDir());
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testObjectAddedRecursiveDir() {
+ logMethodName();
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.count(), 0);
+
+ File newDir = createNewDir(createNewDir(createNewDir(mainStorageDir)));
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 3);
+ Assert.assertEquals(manager.getObject(objectsAdded.get(2)).getPath().toString(),
+ newDir.getPath());
+ Assert.assertTrue(manager.getObject(objectsAdded.get(2)).isDir());
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testObjectRemoved() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.count(), 1);
+
+ Assert.assertTrue(newFile.delete());
+ manager.flushEvents();
+ Assert.assertEquals(objectsRemoved.size(), 1);
+ Assert.assertNull(manager.getObject(objectsRemoved.get(0)));
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testObjectMoved() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ Assert.assertEquals(stream.count(), 1);
+ File toFile = new File(mainStorageDir, "to" + newFile.getName());
+
+ Assert.assertTrue(newFile.renameTo(toFile));
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertEquals(objectsRemoved.size(), 1);
+ Assert.assertEquals(manager.getObject(objectsAdded.get(0)).getPath().toString(),
+ toFile.getPath());
+ Assert.assertNull(manager.getObject(objectsRemoved.get(0)));
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ /** MtpStorageManager operation tests. Ensure that events are not sent for the main operation,
+ and also test all possible cases of other processes accessing the file at the same time, as
+ well as cases of both failure and success. **/
+
+ @Test
+ @SmallTest
+ public void testSendObjectSuccess() {
+ logMethodName();
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ int id = manager.beginSendObject(manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ "newFile", MtpConstants.FORMAT_UNDEFINED);
+ Assert.assertEquals(id, 1);
+
+ File newFile = createNewFile(mainStorageDir, "newFile");
+ manager.flushEvents();
+ MtpStorageManager.MtpObject obj = manager.getObject(id);
+ Assert.assertTrue(manager.endSendObject(obj, true));
+ Assert.assertEquals(obj.getPath().toString(), newFile.getPath());
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testSendObjectSuccessDir() {
+ logMethodName();
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ int id = manager.beginSendObject(manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ "newDir", MtpConstants.FORMAT_ASSOCIATION);
+ Assert.assertEquals(id, 1);
+
+ File newFile = createNewDir(mainStorageDir, "newDir");
+ manager.flushEvents();
+ MtpStorageManager.MtpObject obj = manager.getObject(id);
+ Assert.assertTrue(manager.endSendObject(obj, true));
+ Assert.assertEquals(obj.getPath().toString(), newFile.getPath());
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(obj.getFormat(), MtpConstants.FORMAT_ASSOCIATION);
+ Assert.assertTrue(manager.checkConsistency());
+
+ // Check that new dir receives events
+ File newerFile = createNewFile(newFile);
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertEquals(manager.getObject(objectsAdded.get(0)).getPath().toString(),
+ newerFile.getPath());
+ }
+
+ @Test
+ @SmallTest
+ public void testSendObjectSuccessDelayed() {
+ logMethodName();
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ int id = manager.beginSendObject(manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ "newFile", MtpConstants.FORMAT_UNDEFINED);
+ Assert.assertEquals(id, 1);
+ MtpStorageManager.MtpObject obj = manager.getObject(id);
+ Assert.assertTrue(manager.endSendObject(obj, true));
+
+ File newFile = createNewFile(mainStorageDir, "newFile");
+ manager.flushEvents();
+ Assert.assertEquals(obj.getPath().toString(), newFile.getPath());
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testSendObjectSuccessDirDelayed() {
+ logMethodName();
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ int id = manager.beginSendObject(manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ "newDir", MtpConstants.FORMAT_ASSOCIATION);
+ Assert.assertEquals(id, 1);
+
+ MtpStorageManager.MtpObject obj = manager.getObject(id);
+ Assert.assertTrue(manager.endSendObject(obj, true));
+ File newFile = createNewDir(mainStorageDir, "newDir");
+ manager.flushEvents();
+ Assert.assertEquals(obj.getPath().toString(), newFile.getPath());
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(obj.getFormat(), MtpConstants.FORMAT_ASSOCIATION);
+ Assert.assertTrue(manager.checkConsistency());
+
+ // Check that new dir receives events
+ File newerFile = createNewFile(newFile);
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertEquals(manager.getObject(objectsAdded.get(0)).getPath().toString(),
+ newerFile.getPath());
+ }
+
+ @Test
+ @SmallTest
+ public void testSendObjectSuccessDeleted() {
+ logMethodName();
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ int id = manager.beginSendObject(manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ "newFile", MtpConstants.FORMAT_UNDEFINED);
+ Assert.assertEquals(id, 1);
+
+ File newFile = createNewFile(mainStorageDir, "newFile");
+ Assert.assertTrue(newFile.delete());
+ manager.flushEvents();
+ MtpStorageManager.MtpObject obj = manager.getObject(id);
+ Assert.assertTrue(manager.endSendObject(obj, true));
+ Assert.assertNull(manager.getObject(obj.getId()));
+ Assert.assertEquals(objectsRemoved.get(0).intValue(), obj.getId());
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testSendObjectFailed() {
+ logMethodName();
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ int id = manager.beginSendObject(manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ "newFile", MtpConstants.FORMAT_UNDEFINED);
+ Assert.assertEquals(id, 1);
+
+ MtpStorageManager.MtpObject obj = manager.getObject(id);
+ Assert.assertTrue(manager.endSendObject(obj, false));
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testSendObjectFailedDeleted() {
+ logMethodName();
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ int id = manager.beginSendObject(manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ "newFile", MtpConstants.FORMAT_UNDEFINED);
+ Assert.assertEquals(id, 1);
+ MtpStorageManager.MtpObject obj = manager.getObject(id);
+
+ File newFile = createNewFile(mainStorageDir, "newFile");
+ Assert.assertTrue(newFile.delete());
+ manager.flushEvents();
+ Assert.assertTrue(manager.endSendObject(obj, false));
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testSendObjectFailedAdded() {
+ logMethodName();
+ Stream<MtpStorageManager.MtpObject> stream = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId());
+ int id = manager.beginSendObject(manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ "newFile", MtpConstants.FORMAT_UNDEFINED);
+ Assert.assertEquals(id, 1);
+ MtpStorageManager.MtpObject obj = manager.getObject(id);
+
+ File newDir = createNewDir(mainStorageDir, "newFile");
+ manager.flushEvents();
+ Assert.assertTrue(manager.endSendObject(obj, false));
+ Assert.assertNotEquals(objectsAdded.get(0).intValue(), id);
+ Assert.assertNull(manager.getObject(id));
+ Assert.assertEquals(manager.getObject(objectsAdded.get(0)).getPath().toString(),
+ newDir.getPath());
+ Assert.assertTrue(manager.checkConsistency());
+
+ // Expect events in new dir
+ createNewFile(newDir);
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 2);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRemoveObjectSuccess() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginRemoveObject(obj));
+
+ Assert.assertTrue(newFile.delete());
+ manager.flushEvents();
+ Assert.assertTrue(manager.endRemoveObject(obj, true));
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertNull(manager.getObject(obj.getId()));
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRemoveObjectDelayed() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginRemoveObject(obj));
+
+ Assert.assertTrue(manager.endRemoveObject(obj, true));
+ Assert.assertTrue(newFile.delete());
+ manager.flushEvents();
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertNull(manager.getObject(obj.getId()));
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRemoveObjectDir() {
+ logMethodName();
+ File newDir = createNewDir(mainStorageDir);
+ createNewFile(createNewDir(newDir));
+ MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ manager.getObjects(obj.getId(), 0, mainMtpStorage.getStorageId());
+ Assert.assertTrue(manager.beginRemoveObject(obj));
+
+ createNewFile(newDir);
+ Assert.assertTrue(FileUtils.deleteContentsAndDir(newDir));
+ manager.flushEvents();
+ Assert.assertTrue(manager.endRemoveObject(obj, true));
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertEquals(objectsRemoved.size(), 1);
+ Assert.assertEquals(manager.getObjects(0, 0, mainMtpStorage.getStorageId()).count(), 0);
+ Assert.assertNull(manager.getObject(obj.getId()));
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRemoveObjectDirDelayed() {
+ logMethodName();
+ File newDir = createNewDir(mainStorageDir);
+ createNewFile(createNewDir(newDir));
+ MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginRemoveObject(obj));
+
+ Assert.assertTrue(manager.endRemoveObject(obj, true));
+ Assert.assertTrue(FileUtils.deleteContentsAndDir(newDir));
+ manager.flushEvents();
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(manager.getObjects(0, 0, mainMtpStorage.getStorageId()).count(), 0);
+ Assert.assertNull(manager.getObject(obj.getId()));
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRemoveObjectSuccessAdded() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ int id = obj.getId();
+ Assert.assertTrue(manager.beginRemoveObject(obj));
+
+ Assert.assertTrue(newFile.delete());
+ createNewFile(mainStorageDir, newFile.getName());
+ manager.flushEvents();
+ Assert.assertTrue(manager.endRemoveObject(obj, true));
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertNull(manager.getObject(id));
+ Assert.assertNotEquals(objectsAdded.get(0).intValue(), id);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRemoveObjectFailed() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginRemoveObject(obj));
+
+ Assert.assertTrue(manager.endRemoveObject(obj, false));
+ Assert.assertEquals(manager.getObject(obj.getId()), obj);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRemoveObjectFailedDir() {
+ logMethodName();
+ File newDir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ manager.getObjects(obj.getId(), 0, mainMtpStorage.getStorageId());
+ Assert.assertTrue(manager.beginRemoveObject(obj));
+
+ createNewFile(newDir);
+ manager.flushEvents();
+ Assert.assertTrue(manager.endRemoveObject(obj, false));
+ Assert.assertEquals(manager.getObject(obj.getId()), obj);
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRemoveObjectFailedRemoved() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginRemoveObject(obj));
+
+ Assert.assertTrue(newFile.delete());
+ manager.flushEvents();
+ Assert.assertTrue(manager.endRemoveObject(obj, false));
+ Assert.assertEquals(objectsRemoved.size(), 1);
+ Assert.assertEquals(objectsRemoved.get(0).intValue(), obj.getId());
+ Assert.assertNull(manager.getObject(obj.getId()));
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testCopyObjectSuccess() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ File newDir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
+ MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> !o.isDir()).findFirst().get();
+
+ int id = manager.beginCopyObject(fileObj, dirObj);
+ Assert.assertNotEquals(id, -1);
+ createNewFile(newDir, newFile.getName());
+ manager.flushEvents();
+ MtpStorageManager.MtpObject obj = manager.getObject(id);
+ Assert.assertTrue(manager.endCopyObject(obj, true));
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testCopyObjectSuccessRecursive() {
+ logMethodName();
+ File newDirFrom = createNewDir(mainStorageDir);
+ File newDirFrom1 = createNewDir(newDirFrom);
+ File newDirFrom2 = createNewFile(newDirFrom1);
+ File delayedFile = createNewFile(newDirFrom);
+ File deletedFile = createNewFile(newDirFrom);
+ File newDirTo = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject toObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> o.getName().equals(newDirTo.getName())).findFirst().get();
+ MtpStorageManager.MtpObject fromObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> o.getName().equals(newDirFrom.getName())).findFirst().get();
+
+ manager.getObjects(fromObj.getId(), 0, mainMtpStorage.getStorageId());
+ int id = manager.beginCopyObject(fromObj, toObj);
+ Assert.assertNotEquals(id, -1);
+ File copiedDir = createNewDir(newDirTo, newDirFrom.getName());
+ File copiedDir1 = createNewDir(copiedDir, newDirFrom1.getName());
+ createNewFile(copiedDir1, newDirFrom2.getName());
+ createNewFile(copiedDir, "extraFile");
+ File toDelete = createNewFile(copiedDir, deletedFile.getName());
+ manager.flushEvents();
+ Assert.assertTrue(toDelete.delete());
+ manager.flushEvents();
+ MtpStorageManager.MtpObject obj = manager.getObject(id);
+ Assert.assertTrue(manager.endCopyObject(obj, true));
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertEquals(objectsRemoved.size(), 1);
+
+ createNewFile(copiedDir, delayedFile.getName());
+ manager.flushEvents();
+ Assert.assertTrue(manager.checkConsistency());
+
+ // Expect events in the visited dir, but not the unvisited dir.
+ createNewFile(copiedDir);
+ createNewFile(copiedDir1);
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 2);
+ Assert.assertEquals(objectsAdded.size(), 2);
+
+ // Number of files/dirs created, minus the one that was deleted.
+ Assert.assertEquals(manager.getObjects(0, 0, mainMtpStorage.getStorageId()).count(), 13);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testCopyObjectFailed() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ File newDir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
+ MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> !o.isDir()).findFirst().get();
+
+ int id = manager.beginCopyObject(fileObj, dirObj);
+ Assert.assertNotEquals(id, -1);
+ manager.flushEvents();
+ MtpStorageManager.MtpObject obj = manager.getObject(id);
+ Assert.assertTrue(manager.endCopyObject(obj, false));
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testCopyObjectFailedAdded() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ File newDir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
+ MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> !o.isDir()).findFirst().get();
+
+ int id = manager.beginCopyObject(fileObj, dirObj);
+ Assert.assertNotEquals(id, -1);
+ File addedDir = createNewDir(newDir, newFile.getName());
+ manager.flushEvents();
+ MtpStorageManager.MtpObject obj = manager.getObject(id);
+ Assert.assertTrue(manager.endCopyObject(obj, false));
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertNotEquals(objectsAdded.get(0).intValue(), id);
+ Assert.assertTrue(manager.checkConsistency());
+
+ // Expect events in new dir
+ createNewFile(addedDir);
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 2);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testCopyObjectFailedDeleted() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ File newDir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
+ MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> !o.isDir()).findFirst().get();
+
+ int id = manager.beginCopyObject(fileObj, dirObj);
+ Assert.assertNotEquals(id, -1);
+ Assert.assertTrue(createNewFile(newDir, newFile.getName()).delete());
+ manager.flushEvents();
+ MtpStorageManager.MtpObject obj = manager.getObject(id);
+ Assert.assertTrue(manager.endCopyObject(obj, false));
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRenameObjectSuccess() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginRenameObject(obj, "renamed"));
+
+ File renamed = new File(mainStorageDir, "renamed");
+ Assert.assertTrue(newFile.renameTo(renamed));
+ manager.flushEvents();
+ Assert.assertTrue(manager.endRenameObject(obj, newFile.getName(), true));
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(obj.getPath().toString(), renamed.getPath());
+
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRenameObjectDirSuccess() {
+ logMethodName();
+ File newDir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginRenameObject(obj, "renamed"));
+
+ File renamed = new File(mainStorageDir, "renamed");
+ Assert.assertTrue(newDir.renameTo(renamed));
+ manager.flushEvents();
+ Assert.assertTrue(manager.endRenameObject(obj, newDir.getName(), true));
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(obj.getPath().toString(), renamed.getPath());
+
+ Assert.assertTrue(manager.checkConsistency());
+
+ // Don't expect events
+ createNewFile(renamed);
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRenameObjectDirVisitedSuccess() {
+ logMethodName();
+ File newDir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ manager.getObjects(obj.getId(), 0, mainMtpStorage.getStorageId());
+ Assert.assertTrue(manager.beginRenameObject(obj, "renamed"));
+
+ File renamed = new File(mainStorageDir, "renamed");
+ Assert.assertTrue(newDir.renameTo(renamed));
+ manager.flushEvents();
+ Assert.assertTrue(manager.endRenameObject(obj, newDir.getName(), true));
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(obj.getPath().toString(), renamed.getPath());
+
+ Assert.assertTrue(manager.checkConsistency());
+
+ // Expect events since the dir was visited
+ createNewFile(renamed);
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRenameObjectDelayed() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginRenameObject(obj, "renamed"));
+
+ Assert.assertTrue(manager.endRenameObject(obj, newFile.getName(), true));
+ File renamed = new File(mainStorageDir, "renamed");
+ Assert.assertTrue(newFile.renameTo(renamed));
+ manager.flushEvents();
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(obj.getPath().toString(), renamed.getPath());
+
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRenameObjectDirVisitedDelayed() {
+ logMethodName();
+ File newDir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ manager.getObjects(obj.getId(), 0, mainMtpStorage.getStorageId());
+ Assert.assertTrue(manager.beginRenameObject(obj, "renamed"));
+
+ Assert.assertTrue(manager.endRenameObject(obj, newDir.getName(), true));
+ File renamed = new File(mainStorageDir, "renamed");
+ Assert.assertTrue(newDir.renameTo(renamed));
+ manager.flushEvents();
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(obj.getPath().toString(), renamed.getPath());
+
+ Assert.assertTrue(manager.checkConsistency());
+
+ // Expect events since the dir was visited
+ createNewFile(renamed);
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRenameObjectFailed() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginRenameObject(obj, "renamed"));
+
+ Assert.assertTrue(manager.endRenameObject(obj, newFile.getName(), false));
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRenameObjectFailedOldRemoved() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginRenameObject(obj, "renamed"));
+
+ Assert.assertTrue(newFile.delete());
+ manager.flushEvents();
+ Assert.assertTrue(manager.endRenameObject(obj, newFile.getName(), false));
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 1);
+
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testRenameObjectFailedNewAdded() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ MtpStorageManager.MtpObject obj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginRenameObject(obj, "renamed"));
+
+ createNewFile(mainStorageDir, "renamed");
+ manager.flushEvents();
+ Assert.assertTrue(manager.endRenameObject(obj, newFile.getName(), false));
+
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testMoveObjectSuccess() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ File dir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
+ MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> !o.isDir()).findFirst().get();
+ Assert.assertTrue(manager.beginMoveObject(fileObj, dirObj));
+
+ File moved = new File(dir, newFile.getName());
+ Assert.assertTrue(newFile.renameTo(moved));
+ manager.flushEvents();
+ Assert.assertTrue(manager.endMoveObject(
+ manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ dirObj, newFile.getName(), true));
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(fileObj.getPath().toString(), moved.getPath());
+
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testMoveObjectDirSuccess() {
+ logMethodName();
+ File newDir = createNewDir(mainStorageDir);
+ File movedDir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> o.getName().equals(newDir.getName())).findFirst().get();
+ MtpStorageManager.MtpObject movedObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> o.getName().equals(movedDir.getName())).findFirst().get();
+ Assert.assertTrue(manager.beginMoveObject(movedObj, dirObj));
+
+ File renamed = new File(newDir, movedDir.getName());
+ Assert.assertTrue(movedDir.renameTo(renamed));
+ manager.flushEvents();
+ Assert.assertTrue(manager.endMoveObject(
+ manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ dirObj, movedDir.getName(), true));
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(movedObj.getPath().toString(), renamed.getPath());
+
+ Assert.assertTrue(manager.checkConsistency());
+
+ // Don't expect events
+ createNewFile(renamed);
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testMoveObjectDirVisitedSuccess() {
+ logMethodName();
+ File newDir = createNewDir(mainStorageDir);
+ File movedDir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> o.getName().equals(newDir.getName())).findFirst().get();
+ MtpStorageManager.MtpObject movedObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> o.getName().equals(movedDir.getName())).findFirst().get();
+ manager.getObjects(movedObj.getId(), 0, mainMtpStorage.getStorageId());
+ Assert.assertTrue(manager.beginMoveObject(movedObj, dirObj));
+
+ File renamed = new File(newDir, movedDir.getName());
+ Assert.assertTrue(movedDir.renameTo(renamed));
+ manager.flushEvents();
+ Assert.assertTrue(manager.endMoveObject(
+ manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ dirObj, movedDir.getName(), true));
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(movedObj.getPath().toString(), renamed.getPath());
+
+ Assert.assertTrue(manager.checkConsistency());
+
+ // Expect events since the dir was visited
+ createNewFile(renamed);
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testMoveObjectDelayed() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ File dir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
+ MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> !o.isDir()).findFirst().get();
+ Assert.assertTrue(manager.beginMoveObject(fileObj, dirObj));
+
+ Assert.assertTrue(manager.endMoveObject(
+ manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ dirObj, newFile.getName(), true));
+
+ File moved = new File(dir, newFile.getName());
+ Assert.assertTrue(newFile.renameTo(moved));
+ manager.flushEvents();
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(fileObj.getPath().toString(), moved.getPath());
+
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testMoveObjectDirVisitedDelayed() {
+ logMethodName();
+ File newDir = createNewDir(mainStorageDir);
+ File movedDir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> o.getName().equals(newDir.getName())).findFirst().get();
+ MtpStorageManager.MtpObject movedObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> o.getName().equals(movedDir.getName())).findFirst().get();
+ manager.getObjects(movedObj.getId(), 0, mainMtpStorage.getStorageId());
+ Assert.assertTrue(manager.beginMoveObject(movedObj, dirObj));
+
+ Assert.assertTrue(manager.endMoveObject(
+ manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ dirObj, movedDir.getName(), true));
+
+ File renamed = new File(newDir, movedDir.getName());
+ Assert.assertTrue(movedDir.renameTo(renamed));
+ manager.flushEvents();
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(movedObj.getPath().toString(), renamed.getPath());
+
+ Assert.assertTrue(manager.checkConsistency());
+
+ // Expect events since the dir was visited
+ createNewFile(renamed);
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testMoveObjectFailed() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ File dir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
+ MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> !o.isDir()).findFirst().get();
+ Assert.assertTrue(manager.beginMoveObject(fileObj, dirObj));
+
+ Assert.assertTrue(manager.endMoveObject(
+ manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ dirObj, newFile.getName(), false));
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testMoveObjectFailedOldRemoved() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ File dir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
+ MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> !o.isDir()).findFirst().get();
+ Assert.assertTrue(manager.beginMoveObject(fileObj, dirObj));
+
+ Assert.assertTrue(newFile.delete());
+ manager.flushEvents();
+ Assert.assertTrue(manager.endMoveObject(
+ manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ dirObj, newFile.getName(), false));
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 1);
+
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testMoveObjectFailedNewAdded() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ File dir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject dirObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(MtpStorageManager.MtpObject::isDir).findFirst().get();
+ MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId())
+ .filter(o -> !o.isDir()).findFirst().get();
+ Assert.assertTrue(manager.beginMoveObject(fileObj, dirObj));
+
+ createNewFile(dir, newFile.getName());
+ manager.flushEvents();
+ Assert.assertTrue(manager.endMoveObject(
+ manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ dirObj, newFile.getName(), false));
+
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testMoveObjectXStorageSuccess() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginMoveObject(fileObj,
+ manager.getStorageRoot(secondaryMtpStorage.getStorageId())));
+
+ Assert.assertTrue(newFile.delete());
+ File moved = createNewFile(secondaryStorageDir, newFile.getName());
+ manager.flushEvents();
+ Assert.assertTrue(manager.endMoveObject(
+ manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ manager.getStorageRoot(secondaryMtpStorage.getStorageId()),
+ newFile.getName(), true));
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(manager.getObject(fileObj.getId()).getPath().toString(),
+ moved.getPath());
+
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testMoveObjectXStorageDirSuccess() {
+ logMethodName();
+ File movedDir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject movedObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginMoveObject(movedObj,
+ manager.getStorageRoot(secondaryMtpStorage.getStorageId())));
+
+ Assert.assertTrue(movedDir.delete());
+ File moved = createNewDir(secondaryStorageDir, movedDir.getName());
+ manager.flushEvents();
+ Assert.assertTrue(manager.endMoveObject(
+ manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ manager.getStorageRoot(secondaryMtpStorage.getStorageId()),
+ movedDir.getName(), true));
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(manager.getObject(movedObj.getId()).getPath().toString(),
+ moved.getPath());
+
+ Assert.assertTrue(manager.checkConsistency());
+
+ // Don't expect events
+ createNewFile(moved);
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testMoveObjectXStorageDirVisitedSuccess() {
+ logMethodName();
+ File movedDir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject movedObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ manager.getObjects(movedObj.getId(), 0, mainMtpStorage.getStorageId());
+ Assert.assertTrue(manager.beginMoveObject(movedObj,
+ manager.getStorageRoot(secondaryMtpStorage.getStorageId())));
+
+ Assert.assertTrue(movedDir.delete());
+ File moved = createNewDir(secondaryStorageDir, movedDir.getName());
+ manager.flushEvents();
+ Assert.assertTrue(manager.endMoveObject(
+ manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ manager.getStorageRoot(secondaryMtpStorage.getStorageId()),
+ movedDir.getName(), true));
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(manager.getObject(movedObj.getId()).getPath().toString(),
+ moved.getPath());
+
+ Assert.assertTrue(manager.checkConsistency());
+
+ // Expect events since the dir was visited
+ createNewFile(moved);
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testMoveObjectXStorageDelayed() {
+ logMethodName();
+ File movedFile = createNewFile(mainStorageDir);
+ MtpStorageManager.MtpObject movedObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginMoveObject(movedObj,
+ manager.getStorageRoot(secondaryMtpStorage.getStorageId())));
+
+ Assert.assertTrue(manager.endMoveObject(
+ manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ manager.getStorageRoot(secondaryMtpStorage.getStorageId()),
+ movedFile.getName(), true));
+
+ Assert.assertTrue(movedFile.delete());
+ File moved = createNewFile(secondaryStorageDir, movedFile.getName());
+ manager.flushEvents();
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(manager.getObject(movedObj.getId()).getPath().toString(),
+ moved.getPath());
+
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testMoveObjectXStorageDirVisitedDelayed() {
+ logMethodName();
+ File movedDir = createNewDir(mainStorageDir);
+ MtpStorageManager.MtpObject movedObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ manager.getObjects(movedObj.getId(), 0, mainMtpStorage.getStorageId());
+ Assert.assertTrue(manager.beginMoveObject(movedObj,
+ manager.getStorageRoot(secondaryMtpStorage.getStorageId())));
+
+ Assert.assertTrue(manager.endMoveObject(
+ manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ manager.getStorageRoot(secondaryMtpStorage.getStorageId()),
+ movedDir.getName(), true));
+
+ Assert.assertTrue(movedDir.delete());
+ File moved = createNewDir(secondaryStorageDir, movedDir.getName());
+ manager.flushEvents();
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+ Assert.assertEquals(manager.getObject(movedObj.getId()).getPath().toString(),
+ moved.getPath());
+
+ Assert.assertTrue(manager.checkConsistency());
+
+ // Expect events since the dir was visited
+ createNewFile(moved);
+ manager.flushEvents();
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testMoveObjectXStorageFailed() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginMoveObject(fileObj,
+ manager.getStorageRoot(secondaryMtpStorage.getStorageId())));
+
+ Assert.assertTrue(manager.endMoveObject(
+ manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ manager.getStorageRoot(secondaryMtpStorage.getStorageId()),
+ newFile.getName(), false));
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testMoveObjectXStorageFailedOldRemoved() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginMoveObject(fileObj,
+ manager.getStorageRoot(secondaryMtpStorage.getStorageId())));
+
+ Assert.assertTrue(newFile.delete());
+ manager.flushEvents();
+ Assert.assertTrue(manager.endMoveObject(
+ manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ manager.getStorageRoot(secondaryMtpStorage.getStorageId()),
+ newFile.getName(), false));
+
+ Assert.assertEquals(objectsAdded.size(), 0);
+ Assert.assertEquals(objectsRemoved.size(), 1);
+
+ Assert.assertTrue(manager.checkConsistency());
+ }
+
+ @Test
+ @SmallTest
+ public void testMoveObjectXStorageFailedNewAdded() {
+ logMethodName();
+ File newFile = createNewFile(mainStorageDir);
+ MtpStorageManager.MtpObject fileObj = manager.getObjects(0xFFFFFFFF, 0,
+ mainMtpStorage.getStorageId()).findFirst().get();
+ Assert.assertTrue(manager.beginMoveObject(fileObj,
+ manager.getStorageRoot(secondaryMtpStorage.getStorageId())));
+
+ createNewFile(secondaryStorageDir, newFile.getName());
+ manager.flushEvents();
+ Assert.assertTrue(manager.endMoveObject(
+ manager.getStorageRoot(mainMtpStorage.getStorageId()),
+ manager.getStorageRoot(secondaryMtpStorage.getStorageId()),
+ newFile.getName(), false));
+
+ Assert.assertEquals(objectsAdded.size(), 1);
+ Assert.assertEquals(objectsRemoved.size(), 0);
+
+ Assert.assertTrue(manager.checkConsistency());
+ }
+}
\ No newline at end of file
diff --git a/nfc-extras/tests/Android.mk b/nfc-extras/tests/Android.mk
index be2bb53..34d6508 100644
--- a/nfc-extras/tests/Android.mk
+++ b/nfc-extras/tests/Android.mk
@@ -20,7 +20,8 @@
LOCAL_JAVA_LIBRARIES := \
android.test.runner \
- com.android.nfc_extras
+ com.android.nfc_extras \
+ android.test.base.stubs
LOCAL_STATIC_JAVA_LIBRARIES := junit
diff --git a/packages/CaptivePortalLogin/OWNERS b/packages/CaptivePortalLogin/OWNERS
index 2d71c20..6f77e04 100644
--- a/packages/CaptivePortalLogin/OWNERS
+++ b/packages/CaptivePortalLogin/OWNERS
@@ -1,10 +1,7 @@
set noparent
-per-file Android.mk = build.master@android.com
-per-file Android.mk = ek@google.com
-per-file Android.mk = hugobenichi@google.com
-per-file Android.mk = lorenzo@google.com
-
ek@google.com
hugobenichi@google.com
+jchalard@google.com
lorenzo@google.com
+satk@google.com
diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml
index 4301019..badeb6d 100644
--- a/packages/InputDevices/res/values-af/strings.xml
+++ b/packages/InputDevices/res/values-af/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spaans (Latyn)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letties"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persies"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Aserbeidjaans"</string>
</resources>
diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml
index ce3659a..2375bd4 100644
--- a/packages/InputDevices/res/values-am/strings.xml
+++ b/packages/InputDevices/res/values-am/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ስፓኒሽ (ላቲን)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"ላትቪያኛ"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"ፋርስኛ"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"አዘርባይጃንኛ"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml
index 2b0a6f9..0263a68 100644
--- a/packages/InputDevices/res/values-ar/strings.xml
+++ b/packages/InputDevices/res/values-ar/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"الإسبانية (اللاتينية)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"اللاتفية"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"الفارسية"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"الأذربيجانية"</string>
</resources>
diff --git a/packages/InputDevices/res/values-az/strings.xml b/packages/InputDevices/res/values-az/strings.xml
index a1f7c1c..e0286ee 100644
--- a/packages/InputDevices/res/values-az/strings.xml
+++ b/packages/InputDevices/res/values-az/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"İspan (Latın)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latış"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Fars Dili"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azəri"</string>
</resources>
diff --git a/packages/InputDevices/res/values-b+sr+Latn/strings.xml b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
index c60b59e..bc9330d 100644
--- a/packages/InputDevices/res/values-b+sr+Latn/strings.xml
+++ b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"španski (Latinska Amerika)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"letonski"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"persijska"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"azerbejdžanska"</string>
</resources>
diff --git a/packages/InputDevices/res/values-be/strings.xml b/packages/InputDevices/res/values-be/strings.xml
index e93d03a..cfe078e 100644
--- a/packages/InputDevices/res/values-be/strings.xml
+++ b/packages/InputDevices/res/values-be/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Іспанская (Лацінская Амерыка)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Латышская"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Персідская"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Азербайджанская"</string>
</resources>
diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml
index ee5cfa8..a520a80 100644
--- a/packages/InputDevices/res/values-bg/strings.xml
+++ b/packages/InputDevices/res/values-bg/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Исп. клав. подредба (Лат. Америка)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"латвийски"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"персийски"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"азербайджански"</string>
</resources>
diff --git a/packages/InputDevices/res/values-bn/strings.xml b/packages/InputDevices/res/values-bn/strings.xml
index 945c891..a61e6ce 100644
--- a/packages/InputDevices/res/values-bn/strings.xml
+++ b/packages/InputDevices/res/values-bn/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"স্প্যানিশ (ল্যাটিন)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"লাটভিও"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"ফার্সী"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"আজারবাইজানি"</string>
</resources>
diff --git a/packages/InputDevices/res/values-bs/strings.xml b/packages/InputDevices/res/values-bs/strings.xml
index ca1b8ff..c16b266 100644
--- a/packages/InputDevices/res/values-bs/strings.xml
+++ b/packages/InputDevices/res/values-bs/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"španski (Latinska Amerika)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"latvijski"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"perzijski"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"azerbejdžanski"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml
index 3c6ea15..fe8e809 100644
--- a/packages/InputDevices/res/values-ca/strings.xml
+++ b/packages/InputDevices/res/values-ca/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espanyol (llatí)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letó"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persa"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Àzeri"</string>
</resources>
diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml
index a78663b..a571f94 100644
--- a/packages/InputDevices/res/values-cs/strings.xml
+++ b/packages/InputDevices/res/values-cs/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"španělština (Latinská Amerika)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lotyšská klávesnice"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"perština"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"ázerbájdžánština"</string>
</resources>
diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml
index 08ff4ca..1943d35 100644
--- a/packages/InputDevices/res/values-da/strings.xml
+++ b/packages/InputDevices/res/values-da/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spansk (latinamerika)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lettisk"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persisk"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Aserbajdsjansk"</string>
</resources>
diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml
index 1543ef2..418a3f0 100644
--- a/packages/InputDevices/res/values-de/strings.xml
+++ b/packages/InputDevices/res/values-de/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanisch (Lateinisch)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lettisch"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persisch"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Aserbaidschanisch"</string>
</resources>
diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml
index e5d1409..460d0f8 100644
--- a/packages/InputDevices/res/values-el/strings.xml
+++ b/packages/InputDevices/res/values-el/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Ισπανικά (Λατινικής Αμερικής)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Λετονικά"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Περσικά"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Αζερμπαϊτζανικά"</string>
</resources>
diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml
index 68fcfd7..e6c12a7 100644
--- a/packages/InputDevices/res/values-es-rUS/strings.xml
+++ b/packages/InputDevices/res/values-es-rUS/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Español (latino)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letón"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persa"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerí"</string>
</resources>
diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml
index 86c82ed..7f779c0 100644
--- a/packages/InputDevices/res/values-es/strings.xml
+++ b/packages/InputDevices/res/values-es/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Español (Latinoamérica)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letón"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persa"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerí"</string>
</resources>
diff --git a/packages/InputDevices/res/values-et/strings.xml b/packages/InputDevices/res/values-et/strings.xml
index 8bc5c25..a5d8cf9 100644
--- a/packages/InputDevices/res/values-et/strings.xml
+++ b/packages/InputDevices/res/values-et/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Hispaania (Ladina-Ameerika)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"läti keel"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"pärsia"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"aserbaidžaani"</string>
</resources>
diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml
index 4fadb97..8d62b34 100644
--- a/packages/InputDevices/res/values-eu/strings.xml
+++ b/packages/InputDevices/res/values-eu/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espainiera (Latinoamerika)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letoniera"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Pertsiera"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaijanera"</string>
</resources>
diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml
index 037e044..6effa27 100644
--- a/packages/InputDevices/res/values-fa/strings.xml
+++ b/packages/InputDevices/res/values-fa/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"اسپانیایی (لاتین)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"لتونیایی"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"فارسی"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"آذربایجانی"</string>
</resources>
diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml
index 65bd8d3..9530df6 100644
--- a/packages/InputDevices/res/values-fi/strings.xml
+++ b/packages/InputDevices/res/values-fi/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"espanja (Latinalainen Amerikka)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"latvialainen"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"persia"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"azeri"</string>
</resources>
diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml
index 8b132fa..afb61fd 100644
--- a/packages/InputDevices/res/values-fr-rCA/strings.xml
+++ b/packages/InputDevices/res/values-fr-rCA/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espagnol (latin)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letton"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persan"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaïdjanais"</string>
</resources>
diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml
index baf749d..fadc6a2 100644
--- a/packages/InputDevices/res/values-fr/strings.xml
+++ b/packages/InputDevices/res/values-fr/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espagnol (latin)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letton"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persan"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azéri"</string>
</resources>
diff --git a/packages/InputDevices/res/values-gl/strings.xml b/packages/InputDevices/res/values-gl/strings.xml
index c5bddc5..de7adf1 100644
--- a/packages/InputDevices/res/values-gl/strings.xml
+++ b/packages/InputDevices/res/values-gl/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Español (América Latina)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letón"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persa"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Acerbaixano"</string>
</resources>
diff --git a/packages/InputDevices/res/values-gu/strings.xml b/packages/InputDevices/res/values-gu/strings.xml
index 0f8c752..915f1b6 100644
--- a/packages/InputDevices/res/values-gu/strings.xml
+++ b/packages/InputDevices/res/values-gu/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"સ્પેનિશ (લેટિન)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"લાતવિયન"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"પર્શિયન"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"અઝરબૈજાની"</string>
</resources>
diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml
index c1bbd9d..0abcf87 100644
--- a/packages/InputDevices/res/values-hi/strings.xml
+++ b/packages/InputDevices/res/values-hi/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"स्पेनिश (लैटिन)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"लातवियाई"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"फ़ारसी"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"अज़रबैजानी"</string>
</resources>
diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml
index ec02934..7b27449b 100644
--- a/packages/InputDevices/res/values-hr/strings.xml
+++ b/packages/InputDevices/res/values-hr/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"španjolski (Latinska Amerika)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"latvijska"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"perzijski"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"azerbajdžanski"</string>
</resources>
diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml
index a5412d4..20a0a4f 100644
--- a/packages/InputDevices/res/values-hu/strings.xml
+++ b/packages/InputDevices/res/values-hu/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"spanyol (latin-amerikai)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"lett"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"perzsa"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"azeri"</string>
</resources>
diff --git a/packages/InputDevices/res/values-hy/strings.xml b/packages/InputDevices/res/values-hy/strings.xml
index b152b0f..e2c485c 100644
--- a/packages/InputDevices/res/values-hy/strings.xml
+++ b/packages/InputDevices/res/values-hy/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Իսպաներեն (Լատինական)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"լատիշերեն"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"պարսկերեն"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"ադրբեջաներեն"</string>
</resources>
diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index f2409d4..5e45207 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanyol (Latin)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvi"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persia"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaijan"</string>
</resources>
diff --git a/packages/InputDevices/res/values-is/strings.xml b/packages/InputDevices/res/values-is/strings.xml
index 0e80bd4..25eeea3 100644
--- a/packages/InputDevices/res/values-is/strings.xml
+++ b/packages/InputDevices/res/values-is/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spænskt (latneskt)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lettneska"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persneska"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"aserska"</string>
</resources>
diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml
index 5da935b..4380d48 100644
--- a/packages/InputDevices/res/values-it/strings.xml
+++ b/packages/InputDevices/res/values-it/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spagnolo (America Latina)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lettone"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persiano"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azero"</string>
</resources>
diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml
index 5cbc4f4..27f1611 100644
--- a/packages/InputDevices/res/values-iw/strings.xml
+++ b/packages/InputDevices/res/values-iw/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ספרדית (לטינית)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"לטבית"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"פרסית"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"אזרית"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml
index aa77ddc..c0c6915 100644
--- a/packages/InputDevices/res/values-ja/strings.xml
+++ b/packages/InputDevices/res/values-ja/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"スペイン語(中南米)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"ラトビア語"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"ペルシャ語"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"アゼルバイジャン語"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ka/strings.xml b/packages/InputDevices/res/values-ka/strings.xml
index 0b899c7..471d808 100644
--- a/packages/InputDevices/res/values-ka/strings.xml
+++ b/packages/InputDevices/res/values-ka/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ესპანური (ლათინური)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"ლატვიური"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"სპარსული"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"აზერბაიჯანული"</string>
</resources>
diff --git a/packages/InputDevices/res/values-kk/strings.xml b/packages/InputDevices/res/values-kk/strings.xml
index 659f571..4688ebc 100644
--- a/packages/InputDevices/res/values-kk/strings.xml
+++ b/packages/InputDevices/res/values-kk/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Испан (латын)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Латыш"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Парсы"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"әзiрбайжан"</string>
</resources>
diff --git a/packages/InputDevices/res/values-km/strings.xml b/packages/InputDevices/res/values-km/strings.xml
index a3a789c..fca3737 100644
--- a/packages/InputDevices/res/values-km/strings.xml
+++ b/packages/InputDevices/res/values-km/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"អេស្ប៉ាញ (ឡាតាំង)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"ឡាតវីយ៉ា"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"ពីស៊ាន"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"អាហ្សឺបៃហ្សង់"</string>
</resources>
diff --git a/packages/InputDevices/res/values-kn/strings.xml b/packages/InputDevices/res/values-kn/strings.xml
index f07b439..8f2b51a 100644
--- a/packages/InputDevices/res/values-kn/strings.xml
+++ b/packages/InputDevices/res/values-kn/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ಸ್ಪ್ಯಾನಿಶ್ (ಲ್ಯಾಟಿನ್)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"ಲ್ಯಾಟ್ವಿಯನ್"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"ಪರ್ಶಿಯನ್"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"ಅಜೆರ್ಬೈಜಾನಿ"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml
index 28dde45..f712332 100644
--- a/packages/InputDevices/res/values-ko/strings.xml
+++ b/packages/InputDevices/res/values-ko/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"스페인어(라틴)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"라트비아어"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"페르시아어"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"아제르바이잔어"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ky/strings.xml b/packages/InputDevices/res/values-ky/strings.xml
index e12b69f..5b3e986 100644
--- a/packages/InputDevices/res/values-ky/strings.xml
+++ b/packages/InputDevices/res/values-ky/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Испанча (Латын)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Латвияча"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Фарсиче"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Азербайжанча"</string>
</resources>
diff --git a/packages/InputDevices/res/values-lo/strings.xml b/packages/InputDevices/res/values-lo/strings.xml
index 8c040be..e64d83d 100644
--- a/packages/InputDevices/res/values-lo/strings.xml
+++ b/packages/InputDevices/res/values-lo/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ສະແປນນິດ (ລາຕິນ)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"ລັດວຽນ"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"ເປີຊຽນ"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"ອາເຊີໄບຈານີ"</string>
</resources>
diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml
index 0cd696f..a92ddaf 100644
--- a/packages/InputDevices/res/values-lt/strings.xml
+++ b/packages/InputDevices/res/values-lt/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Ispanų (Lotynų Amerika)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvių k."</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persų"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaidžaniečių"</string>
</resources>
diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml
index 52a0751..881adad 100644
--- a/packages/InputDevices/res/values-lv/strings.xml
+++ b/packages/InputDevices/res/values-lv/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spāņu (latīņu)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latviešu"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persiešu"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaidžāņu"</string>
</resources>
diff --git a/packages/InputDevices/res/values-mk/strings.xml b/packages/InputDevices/res/values-mk/strings.xml
index 2f9befe..49d3d42 100644
--- a/packages/InputDevices/res/values-mk/strings.xml
+++ b/packages/InputDevices/res/values-mk/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"шпански (латиница)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"латвиски"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"персиски"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"азербејџански"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ml/strings.xml b/packages/InputDevices/res/values-ml/strings.xml
index 6b5ed06..d346d9f 100644
--- a/packages/InputDevices/res/values-ml/strings.xml
+++ b/packages/InputDevices/res/values-ml/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"സ്പാനിഷ് (ലാറ്റിൻ)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"ലാറ്റ്വിയന്"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"പേര്ഷ്യന്"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"അസര്ബൈജാനി"</string>
</resources>
diff --git a/packages/InputDevices/res/values-mn/strings.xml b/packages/InputDevices/res/values-mn/strings.xml
index 82e664e..e244722 100644
--- a/packages/InputDevices/res/values-mn/strings.xml
+++ b/packages/InputDevices/res/values-mn/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Испани (Латин)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Латви"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Перс"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Азербайжан"</string>
</resources>
diff --git a/packages/InputDevices/res/values-mr/strings.xml b/packages/InputDevices/res/values-mr/strings.xml
index e8575a8..7189197 100644
--- a/packages/InputDevices/res/values-mr/strings.xml
+++ b/packages/InputDevices/res/values-mr/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"स्पॅनिश (लॅटिन)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"लाट्वियन"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"पर्शियन"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"अझरबैजानी"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ms/strings.xml b/packages/InputDevices/res/values-ms/strings.xml
index 0f402e7..4deafa3 100644
--- a/packages/InputDevices/res/values-ms/strings.xml
+++ b/packages/InputDevices/res/values-ms/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Bahasa Sepanyol (Latin)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Bahasa Latvia"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Bahasa Parsi"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Bahasa Azerbaijan"</string>
</resources>
diff --git a/packages/InputDevices/res/values-my/strings.xml b/packages/InputDevices/res/values-my/strings.xml
index 2467353..b97b6f1 100644
--- a/packages/InputDevices/res/values-my/strings.xml
+++ b/packages/InputDevices/res/values-my/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"စပိန် (လက်တင်)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"လာတ်ဗီယံ"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"ပါရှန်"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"အဇာဘိုင်ဂျန်"</string>
</resources>
diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml
index bd2879e..328effa 100644
--- a/packages/InputDevices/res/values-nb/strings.xml
+++ b/packages/InputDevices/res/values-nb/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spansk (latinsk)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvisk"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persisk"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Aserbajdsjansk"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ne/strings.xml b/packages/InputDevices/res/values-ne/strings.xml
index 55969c9..d33cd32 100644
--- a/packages/InputDevices/res/values-ne/strings.xml
+++ b/packages/InputDevices/res/values-ne/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"स्पेनेली(ल्याटिन)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"लातभियन"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"फारसी"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"अजरबैजानी"</string>
</resources>
diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml
index e40248f..45a3e52 100644
--- a/packages/InputDevices/res/values-nl/strings.xml
+++ b/packages/InputDevices/res/values-nl/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spaans (Latijns-Amerika)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lets"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Perzisch"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbeidzjaans"</string>
</resources>
diff --git a/packages/InputDevices/res/values-pa/strings.xml b/packages/InputDevices/res/values-pa/strings.xml
index 951adc8..f707730 100644
--- a/packages/InputDevices/res/values-pa/strings.xml
+++ b/packages/InputDevices/res/values-pa/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ਸਪੇਨੀ (ਲਾਤੀਨੀ)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"ਲਾਤਵੀਅਨ"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"ਫ਼ਾਰਸੀ"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"ਅਜ਼ੇਰਬੈਜਾਨੀ"</string>
</resources>
diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml
index a307940..0d4a65b 100644
--- a/packages/InputDevices/res/values-pl/strings.xml
+++ b/packages/InputDevices/res/values-pl/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"hiszpański (Ameryka Łacińska)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"łotewski"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Perski"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerski"</string>
</resources>
diff --git a/packages/InputDevices/res/values-pt-rBR/strings.xml b/packages/InputDevices/res/values-pt-rBR/strings.xml
index ad2abe1..9c01cf0 100644
--- a/packages/InputDevices/res/values-pt-rBR/strings.xml
+++ b/packages/InputDevices/res/values-pt-rBR/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espanhol (América Latina)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letão"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persa"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaijano"</string>
</resources>
diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml
index a5beee4..ddc6262 100644
--- a/packages/InputDevices/res/values-pt-rPT/strings.xml
+++ b/packages/InputDevices/res/values-pt-rPT/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espanhol (América Latina)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letão"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persa"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azeri"</string>
</resources>
diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml
index ad2abe1..9c01cf0 100644
--- a/packages/InputDevices/res/values-pt/strings.xml
+++ b/packages/InputDevices/res/values-pt/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espanhol (América Latina)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letão"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persa"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaijano"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml
index a933a70..64edffa 100644
--- a/packages/InputDevices/res/values-ro/strings.xml
+++ b/packages/InputDevices/res/values-ro/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spaniolă (America Latină)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letonă"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persană"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azeră"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index 127a84d..6e147eb 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Испанский (Латинская Америка)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"латышский"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Персидский"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Азербайджанский"</string>
</resources>
diff --git a/packages/InputDevices/res/values-si/strings.xml b/packages/InputDevices/res/values-si/strings.xml
index 8192c18..eda2d97 100644
--- a/packages/InputDevices/res/values-si/strings.xml
+++ b/packages/InputDevices/res/values-si/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ස්පාඤ්ඤය (ලතින්)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"ලැට්වියානු"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"පර්සියානු"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"අසර්බයිජාන"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml
index c631095..101ffff 100644
--- a/packages/InputDevices/res/values-sk/strings.xml
+++ b/packages/InputDevices/res/values-sk/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Španielčina (Latinská Amerika)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Lotyština"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Perzština"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbajdžančina"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml
index c5a19be..4242291 100644
--- a/packages/InputDevices/res/values-sl/strings.xml
+++ b/packages/InputDevices/res/values-sl/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"španščina (Latinska Amerika)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"latvijščina"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"perzijščina"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"azerbajdžanščina"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sq/strings.xml b/packages/InputDevices/res/values-sq/strings.xml
index dbc5b4d..5dcfc54 100644
--- a/packages/InputDevices/res/values-sq/strings.xml
+++ b/packages/InputDevices/res/values-sq/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"spanjisht (latine)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"letonisht"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persisht"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbajxhanisht"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml
index 29eea72..abb796f 100644
--- a/packages/InputDevices/res/values-sr/strings.xml
+++ b/packages/InputDevices/res/values-sr/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"шпански (Латинска Америка)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"летонски"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"персијска"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"азербејџанска"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml
index 9954a2b..2961425 100644
--- a/packages/InputDevices/res/values-sv/strings.xml
+++ b/packages/InputDevices/res/values-sv/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanska (latinamerikansk)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"lettiska"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"persiska"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"azerbajdzjanska"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml
index 13b3954..4366204 100644
--- a/packages/InputDevices/res/values-sw/strings.xml
+++ b/packages/InputDevices/res/values-sw/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Kihispania (Kilatini)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Kilatvia"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Kiajemi"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Kiazabaijani"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ta/strings.xml b/packages/InputDevices/res/values-ta/strings.xml
index fec9d8c..a4d07ac 100644
--- a/packages/InputDevices/res/values-ta/strings.xml
+++ b/packages/InputDevices/res/values-ta/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ஸ்பானிஷ் (லத்தீன்)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"லத்வியன்"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"பெர்சியன்"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"அஜர்பைஜானி"</string>
</resources>
diff --git a/packages/InputDevices/res/values-te/strings.xml b/packages/InputDevices/res/values-te/strings.xml
index 922868d..f7cce96 100644
--- a/packages/InputDevices/res/values-te/strings.xml
+++ b/packages/InputDevices/res/values-te/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"స్పానిష్ (లాటిన్)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"లాత్వియన్"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"పర్షియన్"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"అజర్బైజాన్"</string>
</resources>
diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml
index 69d9ddf..65085dc 100644
--- a/packages/InputDevices/res/values-th/strings.xml
+++ b/packages/InputDevices/res/values-th/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"สเปน (ละติน)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"ลัตเวีย"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"เปอร์เซีย"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"อาเซอร์ไบจาน"</string>
</resources>
diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml
index 4482128..c9fc26d 100644
--- a/packages/InputDevices/res/values-tl/strings.xml
+++ b/packages/InputDevices/res/values-tl/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Spanish (Latin)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latvian"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Persian"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaijani"</string>
</resources>
diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml
index 08e5e3e..98e6b51 100644
--- a/packages/InputDevices/res/values-tr/strings.xml
+++ b/packages/InputDevices/res/values-tr/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"İspanyolca (Latin)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letonca"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Farsça"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerice"</string>
</resources>
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index b81f672..c49ee9e 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Іспанська (латиниця)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Латвійська"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Перська"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"азербайджанська"</string>
</resources>
diff --git a/packages/InputDevices/res/values-ur/strings.xml b/packages/InputDevices/res/values-ur/strings.xml
index 2508d5d..ab95bd5 100644
--- a/packages/InputDevices/res/values-ur/strings.xml
+++ b/packages/InputDevices/res/values-ur/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"ہسپانوی (لاطینی)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"لاتویائی"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"فارسی"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"آزربائیجانی"</string>
</resources>
diff --git a/packages/InputDevices/res/values-uz/strings.xml b/packages/InputDevices/res/values-uz/strings.xml
index 4b9d08b..d6f7b2b 100644
--- a/packages/InputDevices/res/values-uz/strings.xml
+++ b/packages/InputDevices/res/values-uz/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Ispan (lotin)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Latish"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Fors"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Ozarbayjon"</string>
</resources>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index 35c5960..e92d8fc6 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Tiếng Tây Ban Nha (La tinh)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Tiếng Latvia"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Tiếng Ba Tư"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Tiếng Azerbaijan"</string>
</resources>
diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml
index 5f1fc17..61c6bc9 100644
--- a/packages/InputDevices/res/values-zh-rCN/strings.xml
+++ b/packages/InputDevices/res/values-zh-rCN/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"西班牙语(拉丁美洲)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"拉脱维亚语"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"波斯语"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"阿塞拜疆语"</string>
</resources>
diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml
index 086f966..bf0e2b7 100644
--- a/packages/InputDevices/res/values-zh-rHK/strings.xml
+++ b/packages/InputDevices/res/values-zh-rHK/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"西班牙文 (拉丁美洲)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"拉脫維亞文"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"波斯文"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"亞塞拜疆文"</string>
</resources>
diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml
index 5e2890f..785cc3d 100644
--- a/packages/InputDevices/res/values-zh-rTW/strings.xml
+++ b/packages/InputDevices/res/values-zh-rTW/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"西班牙文 (拉丁美洲)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"拉脫維亞文"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"波斯文"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"亞塞拜然文"</string>
</resources>
diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml
index 18d26a2..1643f01 100644
--- a/packages/InputDevices/res/values-zu/strings.xml
+++ b/packages/InputDevices/res/values-zu/strings.xml
@@ -42,4 +42,5 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Isi-Spanish (Latin)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"Isi-Latvian"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Isi-Persian"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Isi-Azebhayijani"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-pt-rPT/strings.xml b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
index eac6d36..5da31bd 100644
--- a/packages/PrintSpooler/res/values-pt-rPT/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
@@ -56,8 +56,8 @@
<string name="print_select_printer" msgid="7388760939873368698">"Selecionar impressora"</string>
<string name="print_forget_printer" msgid="5035287497291910766">"Esquecer impressora"</string>
<plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
- <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> impressora encontrada</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> impressoras encontradas</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> impressora encontrada</item>
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Mais informações acerca desta impressora"</string>
@@ -76,8 +76,8 @@
<string name="disabled_services_title" msgid="7313253167968363211">"Serviços desativados"</string>
<string name="all_services_title" msgid="5578662754874906455">"Todos os serviços"</string>
<plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
- <item quantity="one">Instale para detetar <xliff:g id="COUNT_0">%1$s</xliff:g> impressora</item>
<item quantity="other">Instale para detetar <xliff:g id="COUNT_1">%1$s</xliff:g> impressoras</item>
+ <item quantity="one">Instale para detetar <xliff:g id="COUNT_0">%1$s</xliff:g> impressora</item>
</plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"A imprimir <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"A cancelar <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk
index 894a1ab..69287e8 100644
--- a/packages/SettingsLib/Android.mk
+++ b/packages/SettingsLib/Android.mk
@@ -19,8 +19,6 @@
LOCAL_SHARED_JAVA_LIBRARIES := \
apptoolkit-lifecycle-common
-LOCAL_STATIC_JAVA_LIBRARY := legacy-android-test
-
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_JAR_EXCLUDE_FILES := none
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 75a0a42..a5c2e41 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -33,15 +33,13 @@
<string name="wifi_check_password_try_again" msgid="516958988102584767">"পাসওয়ার্ড দেখে আবার চেষ্টা করুন"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"পরিসরের মধ্যে নয়"</string>
<string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"স্বয়ংক্রিয়ভাবে সংযোগ করবে না"</string>
- <!-- no translation found for wifi_no_internet (4663834955626848401) -->
- <skip />
+ <string name="wifi_no_internet" msgid="4663834955626848401">"ইন্টারনেট অ্যাক্সেস নেই"</string>
<string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> দ্বারা সেভ করা"</string>
<string name="connected_via_network_scorer" msgid="5713793306870815341">"স্বয়ংক্রিয়ভাবে %1$s এর মাধ্যমে সংযুক্ত হয়েছে"</string>
<string name="connected_via_network_scorer_default" msgid="7867260222020343104">"নেটওয়ার্কের রেটিং প্রদানকারীর মাধ্যমে স্বয়ংক্রিয়ভাবে সংযুক্ত"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s মাধ্যমে সংযুক্ত হয়েছে"</string>
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s এর মাধ্যমে উপলব্ধ"</string>
- <!-- no translation found for wifi_connected_no_internet (8202906332837777829) -->
- <skip />
+ <string name="wifi_connected_no_internet" msgid="8202906332837777829">"সংযুক্ত, ইন্টারনেট নেই"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"এই মুহূর্তে অ্যাক্সেস পয়েন্টের কোনও কানেকশন ফাঁকা নেই"</string>
<string name="connected_via_carrier" msgid="7583780074526041912">"%1$s এর মাধ্যমে সংযুক্ত হয়েছে"</string>
<string name="available_via_carrier" msgid="1469036129740799053">"%1$s এর মাধ্যমে পাওয়া যাচ্ছে"</string>
@@ -86,8 +84,7 @@
<string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"ইনপুট ডিভাইসে সংযুক্ত"</string>
<string name="bluetooth_pan_user_profile_summary_connected" msgid="6436258151814414028">"ইন্টারনেটের জন্য সংযুক্ত"</string>
<string name="bluetooth_pan_nap_profile_summary_connected" msgid="1322694224800769308">"স্থানীয় ইন্টারনেটে চলছে"</string>
- <!-- no translation found for bluetooth_pan_profile_summary_use_for (5736111170225304239) -->
- <skip />
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5736111170225304239">"ইন্টারনেটের জন্য ব্যবহার করুন"</string>
<string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"মানচিত্রের জন্য ব্যবহার করুন"</string>
<string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"সিম -এর অ্যাক্সেসের জন্য ব্যবহার করুন"</string>
<string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"মিডিয়া অডিওয়ের জন্য ব্যবহার করুন"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index c52c7ca..d12ac8a 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -128,8 +128,8 @@
<string name="running_process_item_user_label" msgid="3129887865552025943">"Nutzer: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="313159469856372621">"Einige Standardeinstellungen festgelegt"</string>
<string name="launch_defaults_none" msgid="4241129108140034876">"Keine Standardeinstellungen festgelegt"</string>
- <string name="tts_settings" msgid="8186971894801348327">"Text-in-Sprache"</string>
- <string name="tts_settings_title" msgid="1237820681016639683">"Text-in-Sprache-Ausgabe"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Sprachausgabe"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Sprachausgabe-Ausgabe"</string>
<string name="tts_default_rate_title" msgid="6030550998379310088">"Sprechgeschwindigkeit"</string>
<string name="tts_default_rate_summary" msgid="4061815292287182801">"Geschwindigkeit, mit der der Text gesprochen wird"</string>
<string name="tts_default_pitch_title" msgid="6135942113172488671">"Tonlage"</string>
@@ -143,7 +143,7 @@
<string name="tts_install_data_title" msgid="4264378440508149986">"Sprachdaten installieren"</string>
<string name="tts_install_data_summary" msgid="5742135732511822589">"Sprachdaten für Sprachsynthese installieren"</string>
<string name="tts_engine_security_warning" msgid="8786238102020223650">"Dieses Sprachsynthesemodul kann den gesamten gesprochenen Text erfassen, einschließlich personenbezogener Daten wie Passwörter und Kreditkartennummern. Es ist Teil der App \"<xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>\". Möchtest du dieses Sprachsynthesemodul aktivieren?"</string>
- <string name="tts_engine_network_required" msgid="1190837151485314743">"Für diese Sprache ist zur Text-in-Sprache-Ausgabe eine aktive Netzwerkverbindung erforderlich."</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Für diese Sprache ist zur Sprachausgabe-Ausgabe eine aktive Netzwerkverbindung erforderlich."</string>
<string name="tts_default_sample_string" msgid="4040835213373086322">"Dies ist ein Beispiel für Sprachsynthese."</string>
<string name="tts_status_title" msgid="7268566550242584413">"Status der Standardsprache"</string>
<string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> wird vollständig unterstützt."</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 57d1657..22e9d35 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -353,7 +353,7 @@
<string name="daltonizer_mode_deuteranomaly" msgid="5475532989673586329">"Daltonismoa (gorri-berdeak)"</string>
<string name="daltonizer_mode_protanomaly" msgid="8424148009038666065">"Protanopia (gorri-berdeak)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="481725854987912389">"Tritanopia (urdin-horia)"</string>
- <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Kolore-zuzenketa"</string>
+ <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Kolorearen zuzenketa"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Eginbidea esperimentala da eta eragina izan dezake funtzionamenduan."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> hobespena gainjarri zaio"</string>
<string name="power_remaining_duration_only" msgid="845431008899029842">"<xliff:g id="TIME">^1</xliff:g> inguru gelditzen dira"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index f5cf5f6..47d8408 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -401,7 +401,7 @@
<string name="active_input_method_subtypes" msgid="3596398805424733238">"Métodos de entrada activos"</string>
<string name="use_system_language_to_select_input_method_subtypes" msgid="5747329075020379587">"Usar idiomas do sistema"</string>
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Non se puido abrir a configuración de <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
- <string name="ime_security_warning" msgid="4135828934735934248">"É posible que este método de entrada poida recompilar todo o texto que escribas, incluídos os datos persoais como os contrasinais e os números de tarxetas de crédito. Provén da aplicación <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Queres usar este método de entrada?"</string>
+ <string name="ime_security_warning" msgid="4135828934735934248">"Este método de introdución de texto pode recompilar todo o que escribas, incluídos os datos persoais como os contrasinais e os números de tarxetas de crédito. Provén da aplicación <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Queres usar este método de introdución de texto?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Nota: Tras un reinicio, non se pode iniciar esta aplicación ata que desbloquees o teléfono"</string>
<string name="ims_reg_title" msgid="7609782759207241443">"Estado de rexistro de IMS"</string>
<string name="ims_reg_status_registered" msgid="933003316932739188">"Rexistrado"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 826bab3..fcca957 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -40,7 +40,7 @@
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s के द्वारा उपलब्ध"</string>
<string name="available_via_passpoint" msgid="1617440946846329613">"%1$s के द्वारा उपलब्ध"</string>
<string name="wifi_connected_no_internet" msgid="8202906332837777829">"कनेक्ट हो गया है, लेकिन इंटरनेट नहीं है"</string>
- <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"एक्सेस पॉइंट फ़िलहाल भरा हुआ है"</string>
+ <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"एक्सेस प्वाइंट फ़िलहाल भरा हुआ है"</string>
<string name="connected_via_carrier" msgid="7583780074526041912">"%1$s के ज़रिए कनेक्ट"</string>
<string name="available_via_carrier" msgid="1469036129740799053">"%1$s के ज़रिए उपलब्ध"</string>
<string name="speed_label_very_slow" msgid="1867055264243608530">"अत्यधिक धीमी"</string>
@@ -176,7 +176,7 @@
<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="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>
@@ -201,7 +201,7 @@
<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>
- <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"पूर्ण वॉल्यूम अक्षम करें"</string>
+ <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"ब्लूटूथ से आवाज़ के नियंत्रण की सुविधा रोकें"</string>
<string name="bluetooth_enable_inband_ringing" msgid="3291686366721786740">"इन-बैंड रिंग करना चालू करें"</string>
<string name="bluetooth_select_avrcp_version_string" msgid="3750059931120293633">"ब्लूटूथ AVRCP वर्शन"</string>
<string name="bluetooth_select_avrcp_version_dialog_title" msgid="7277329668298705702">"ब्लूटूथ AVRCP वर्शन चुनें"</string>
@@ -247,7 +247,7 @@
<string name="verify_apps_over_usb_title" msgid="4177086489869041953">"USB पर ऐप की पुष्टि करें"</string>
<string name="verify_apps_over_usb_summary" msgid="9164096969924529200">"नुकसानदेह व्यवहार के लिए ADB/ADT के द्वारा इंस्टॉल किए गए ऐप्स जाँचें."</string>
<string name="bluetooth_show_devices_without_names_summary" msgid="2351196058115755520">"बिना नाम वाले ब्लूटूथ डिवाइस (केवल MAC पते वाले) दिखाए जाएंगे"</string>
- <string name="bluetooth_disable_absolute_volume_summary" msgid="6031284410786545957">"दूरस्थ डिवाइस के साथ वॉल्यूम की समस्याओं जैसे अस्वीकार्य तेज़ वॉल्यूम या नियंत्रण की कमी की स्थिति में ब्लूटूथ पूर्ण वॉल्यूम सुविधा को अक्षम करता है."</string>
+ <string name="bluetooth_disable_absolute_volume_summary" msgid="6031284410786545957">"दूर के डिवाइस पर आवाज़ बहुत बढ़ जाने या उससे नियंत्रण हटने जैसी समस्याएं होने पर, यह ब्लूटूथ के ज़रिए आवाज़ के नियंत्रण की सुविधा रोक देता है."</string>
<string name="bluetooth_enable_inband_ringing_summary" msgid="2787866074741784975">"फ़ोन की रिंगटोन को ब्लूटूथ हेडसेट पर बजने दें"</string>
<string name="enable_terminal_title" msgid="95572094356054120">"स्थानीय टर्मिनल"</string>
<string name="enable_terminal_summary" msgid="67667852659359206">"लोकल शेल तक पहुंचने की सुविधा देने वाले टर्मिनल ऐप को चालू करें"</string>
@@ -317,8 +317,8 @@
<string name="enable_freeform_support" msgid="1461893351278940416">"फ़्रीफ़ॉर्म विंडो (एक साथ कई विंडो दिखाना) चालू करें"</string>
<string name="enable_freeform_support_summary" msgid="8247310463288834487">"जाँच के लिए बनी फ़्रीफ़ॉर्म विंडो के लिए सहायता चालू करें."</string>
<string name="local_backup_password_title" msgid="3860471654439418822">"डेस्कटॉप बैकअप पासवर्ड"</string>
- <string name="local_backup_password_summary_none" msgid="6951095485537767956">"डेस्कटॉप पूर्ण बैकअप वर्तमान में सुरक्षित नहीं हैं"</string>
- <string name="local_backup_password_summary_change" msgid="5376206246809190364">"डेस्कटॉप के पूर्ण बैकअप का पासवर्ड बदलने या निकालने के लिए टैप करें"</string>
+ <string name="local_backup_password_summary_none" msgid="6951095485537767956">"डेस्कटॉप के पूरे बैक अप फ़िलहाल सुरक्षित नहीं हैं"</string>
+ <string name="local_backup_password_summary_change" msgid="5376206246809190364">"डेस्कटॉप के पूरे बैक अप का पासवर्ड बदलने या हटाने के लिए टैप करें"</string>
<string name="local_backup_password_toast_success" msgid="582016086228434290">"नया बैकअप पासवर्ड सेट किया गया"</string>
<string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"नया पासवर्ड तथा पुष्टि मेल नही खाते"</string>
<string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"सुरक्षित पासवर्ड सेट करने में विफल रहा"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index fa2499f..5c73d54 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -21,6 +21,9 @@
import android.app.Application;
import android.app.usage.StorageStats;
import android.app.usage.StorageStatsManager;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.OnLifecycleEvent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -52,11 +55,6 @@
import com.android.internal.R;
import com.android.internal.util.ArrayUtils;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnDestroy;
-import com.android.settingslib.core.lifecycle.events.OnPause;
-import com.android.settingslib.core.lifecycle.events.OnResume;
import java.io.File;
import java.io.IOException;
@@ -595,7 +593,7 @@
.replaceAll("").toLowerCase();
}
- public class Session implements LifecycleObserver, OnPause, OnResume, OnDestroy {
+ public class Session implements LifecycleObserver {
final Callbacks mCallbacks;
boolean mResumed;
@@ -621,6 +619,7 @@
}
}
+ @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
synchronized (mEntriesMap) {
@@ -633,6 +632,7 @@
if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
}
+ @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
synchronized (mEntriesMap) {
@@ -752,6 +752,7 @@
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
+ @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroy() {
if (!mHasLifecycle) {
// TODO: Legacy, remove this later once all usages are switched to Lifecycle
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 2873fb6..9caff10 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -135,7 +135,8 @@
if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
if (profile instanceof MapProfile) {
profile.setPreferred(mDevice, true);
- } else if (!mProfiles.contains(profile)) {
+ }
+ if (!mProfiles.contains(profile)) {
mRemovedProfiles.remove(profile);
mProfiles.add(profile);
if (profile instanceof PanProfile &&
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
old mode 100755
new mode 100644
index 9cda669..991d922
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -25,6 +25,7 @@
import android.bluetooth.BluetoothMap;
import android.bluetooth.BluetoothMapClient;
import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothPbap;
import android.bluetooth.BluetoothPbapClient;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
@@ -140,9 +141,11 @@
BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
}
- //Create PBAP server profile, but do not add it to list of profiles
- // as we do not need to monitor the profile as part of profile list
+ //Create PBAP server profile
+ if(DEBUG) Log.d(TAG, "Adding local PBAP profile");
mPbapProfile = new PbapServerProfile(context);
+ addProfile(mPbapProfile, PbapServerProfile.NAME,
+ BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED);
if (DEBUG) Log.d(TAG, "LocalBluetoothProfileManager construction complete");
}
@@ -495,6 +498,13 @@
mMapProfile.setPreferred(device, true);
}
+ if ((mPbapProfile != null) &&
+ (mPbapProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED)) {
+ profiles.add(mPbapProfile);
+ removedProfiles.remove(mPbapProfile);
+ mPbapProfile.setPreferred(device, true);
+ }
+
if (mMapClientProfile != null) {
profiles.add(mMapClientProfile);
removedProfiles.remove(mMapClientProfile);
@@ -503,8 +513,6 @@
if (mUsePbapPce) {
profiles.add(mPbapClientProfile);
removedProfiles.remove(mPbapClientProfile);
- profiles.remove(mPbapProfile);
- removedProfiles.add(mPbapProfile);
}
if (DEBUG) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index f3b6912..58465f2 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -91,7 +91,7 @@
public boolean disconnect(BluetoothDevice device) {
if (mService == null) return false;
- return mService.disconnect();
+ return mService.disconnect(device);
}
public int getConnectionStatus(BluetoothDevice device) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index a8262c8..974b2a4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -271,7 +271,7 @@
* @param now The current time, used to tell whether daylight savings is active.
* @return A CharSequence suitable for display as the offset label of {@code tz}.
*/
- private static CharSequence getGmtOffsetText(TimeZoneFormat tzFormatter, Locale locale,
+ public static CharSequence getGmtOffsetText(TimeZoneFormat tzFormatter, Locale locale,
TimeZone tz, Date now) {
final SpannableStringBuilder builder = new SpannableStringBuilder();
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
new file mode 100644
index 0000000..2b6d09f
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
@@ -0,0 +1,108 @@
+/*
+ * 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.settingslib.fuelgauge;
+
+import android.os.IDeviceIdleController;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.support.annotation.VisibleForTesting;
+import android.util.ArraySet;
+import android.util.Log;
+
+/**
+ * Handles getting/changing the whitelist for the exceptions to battery saving features.
+ */
+public class PowerWhitelistBackend {
+
+ private static final String TAG = "PowerWhitelistBackend";
+
+ private static final String DEVICE_IDLE_SERVICE = "deviceidle";
+
+ private static PowerWhitelistBackend sInstance;
+
+ private final IDeviceIdleController mDeviceIdleService;
+ private final ArraySet<String> mWhitelistedApps = new ArraySet<>();
+ private final ArraySet<String> mSysWhitelistedApps = new ArraySet<>();
+
+ public PowerWhitelistBackend() {
+ mDeviceIdleService = IDeviceIdleController.Stub.asInterface(
+ ServiceManager.getService(DEVICE_IDLE_SERVICE));
+ refreshList();
+ }
+
+ @VisibleForTesting
+ PowerWhitelistBackend(IDeviceIdleController deviceIdleService) {
+ mDeviceIdleService = deviceIdleService;
+ refreshList();
+ }
+
+ public int getWhitelistSize() {
+ return mWhitelistedApps.size();
+ }
+
+ public boolean isSysWhitelisted(String pkg) {
+ return mSysWhitelistedApps.contains(pkg);
+ }
+
+ public boolean isWhitelisted(String pkg) {
+ return mWhitelistedApps.contains(pkg);
+ }
+
+ public void addApp(String pkg) {
+ try {
+ mDeviceIdleService.addPowerSaveWhitelistApp(pkg);
+ mWhitelistedApps.add(pkg);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to reach IDeviceIdleController", e);
+ }
+ }
+
+ public void removeApp(String pkg) {
+ try {
+ mDeviceIdleService.removePowerSaveWhitelistApp(pkg);
+ mWhitelistedApps.remove(pkg);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to reach IDeviceIdleController", e);
+ }
+ }
+
+ @VisibleForTesting
+ public void refreshList() {
+ mSysWhitelistedApps.clear();
+ mWhitelistedApps.clear();
+ try {
+ String[] whitelistedApps = mDeviceIdleService.getFullPowerWhitelist();
+ for (String app : whitelistedApps) {
+ mWhitelistedApps.add(app);
+ }
+ String[] sysWhitelistedApps = mDeviceIdleService.getSystemPowerWhitelist();
+ for (String app : sysWhitelistedApps) {
+ mSysWhitelistedApps.add(app);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to reach IDeviceIdleController", e);
+ }
+ }
+
+ public static PowerWhitelistBackend getInstance() {
+ if (sInstance == null) {
+ sInstance = new PowerWhitelistBackend();
+ }
+ return sInstance;
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
index 3f826cc..6025d68 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
@@ -16,21 +16,21 @@
package com.android.settingslib.location;
-import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import android.os.Process;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
import android.util.IconDrawableFactory;
import android.util.Log;
-
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
/**
@@ -38,11 +38,13 @@
*/
public class RecentLocationApps {
private static final String TAG = RecentLocationApps.class.getSimpleName();
- private static final String ANDROID_SYSTEM_PACKAGE_NAME = "android";
+ @VisibleForTesting
+ static final String ANDROID_SYSTEM_PACKAGE_NAME = "android";
private static final int RECENT_TIME_INTERVAL_MILLIS = 15 * 60 * 1000;
- private static final int[] LOCATION_OPS = new int[] {
+ @VisibleForTesting
+ static final int[] LOCATION_OPS = new int[] {
AppOpsManager.OP_MONITOR_LOCATION,
AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
};
@@ -59,6 +61,7 @@
/**
* Fills a list of applications which queried location recently within specified time.
+ * Apps are sorted by recency. Apps with more recent location requests are in the front.
*/
public List<Request> getAppList() {
// Retrieve a location usage list from AppOps
@@ -91,7 +94,18 @@
requests.add(request);
}
}
+ return requests;
+ }
+ public List<Request> getAppListSorted() {
+ List<Request> requests = getAppList();
+ // Sort the list of Requests by recency. Most recent request first.
+ Collections.sort(requests, Collections.reverseOrder(new Comparator<Request>() {
+ @Override
+ public int compare(Request request1, Request request2) {
+ return Long.compare(request1.requestFinishTime, request2.requestFinishTime);
+ }
+ }));
return requests;
}
@@ -108,10 +122,12 @@
List<AppOpsManager.OpEntry> entries = ops.getOps();
boolean highBattery = false;
boolean normalBattery = false;
+ long locationRequestFinishTime = 0L;
// Earliest time for a location request to end and still be shown in list.
long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
for (AppOpsManager.OpEntry entry : entries) {
if (entry.isRunning() || entry.getTime() >= recentLocationCutoffTime) {
+ locationRequestFinishTime = entry.getTime() + entry.getDuration();
switch (entry.getOp()) {
case AppOpsManager.OP_MONITOR_LOCATION:
normalBattery = true;
@@ -133,15 +149,13 @@
}
// The package is fresh enough, continue.
-
int uid = ops.getUid();
int userId = UserHandle.getUserId(uid);
Request request = null;
try {
- IPackageManager ipm = AppGlobals.getPackageManager();
- ApplicationInfo appInfo =
- ipm.getApplicationInfo(packageName, PackageManager.GET_META_DATA, userId);
+ ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser(
+ packageName, PackageManager.GET_META_DATA, userId);
if (appInfo == null) {
Log.w(TAG, "Null application info retrieved for package " + packageName
+ ", userId " + userId);
@@ -158,12 +172,10 @@
badgedAppLabel = null;
}
request = new Request(packageName, userHandle, icon, appLabel, highBattery,
- badgedAppLabel);
- } catch (RemoteException e) {
- Log.w(TAG, "Error while retrieving application info for package " + packageName
- + ", userId " + userId, e);
+ badgedAppLabel, locationRequestFinishTime);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "package name not found for " + packageName + ", userId " + userId);
}
-
return request;
}
@@ -174,15 +186,18 @@
public final CharSequence label;
public final boolean isHighBattery;
public final CharSequence contentDescription;
+ public final long requestFinishTime;
private Request(String packageName, UserHandle userHandle, Drawable icon,
- CharSequence label, boolean isHighBattery, CharSequence contentDescription) {
+ CharSequence label, boolean isHighBattery, CharSequence contentDescription,
+ long requestFinishTime) {
this.packageName = packageName;
this.userHandle = userHandle;
this.icon = icon;
this.label = label;
this.isHighBattery = isHighBattery;
this.contentDescription = contentDescription;
+ this.requestFinishTime = requestFinishTime;
}
}
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
index 85b04c8..e840a4b 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
@@ -45,9 +45,9 @@
import android.view.inputmethod.InputMethodInfo;
import com.android.settingslib.BaseTest;
+import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.compat.ArgumentMatcher;
import java.util.ArrayList;
import java.util.List;
@@ -241,7 +241,7 @@
return ri;
}
- private class IntentMatcher extends ArgumentMatcher<Intent> {
+ private class IntentMatcher implements ArgumentMatcher<Intent> {
private final Intent mIntent;
IntentMatcher(Intent intent) {
@@ -249,11 +249,8 @@
}
@Override
- public boolean matchesObject(Object argument) {
- if (argument instanceof Intent) {
- return ((Intent) argument).filterEquals(mIntent);
- }
- return false;
+ public boolean matches(Intent argument) {
+ return argument != null && argument.filterEquals(mIntent);
}
@Override
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 4a1d392..6615b8c 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
@@ -723,9 +723,9 @@
CountDownLatch latch = new CountDownLatch(1);
// Once the new info has been fetched, we need to wait for the access points to be copied
+ mAccessPointsChangedLatch = new CountDownLatch(1);
doAnswer(invocation -> {
latch.countDown();
- mAccessPointsChangedLatch = new CountDownLatch(1);
return info;
}).when(mockWifiManager).getConnectionInfo();
@@ -767,10 +767,12 @@
WifiTracker tracker = createMockedWifiTracker();
startTracking(tracker);
+ CountDownLatch ready = new CountDownLatch(1);
CountDownLatch latch = new CountDownLatch(1);
CountDownLatch lock = new CountDownLatch(1);
tracker.mMainHandler.post(() -> {
try {
+ ready.countDown();
lock.await();
latch.countDown();
} catch (InterruptedException e) {
@@ -786,6 +788,9 @@
tracker.mMainHandler.sendEmptyMessage(
WifiTracker.MainHandler.MSG_WIFI_STATE_CHANGED);
+ try {
+ ready.await(); // Make sure we have entered the first message handler
+ } catch (InterruptedException e) {}
tracker.onStop();
verify(mockWifiListener, atMost(1)).onAccessPointsChanged();
diff --git a/packages/SettingsLib/tests/robotests/Android.mk b/packages/SettingsLib/tests/robotests/Android.mk
index 2738027..02a4973 100644
--- a/packages/SettingsLib/tests/robotests/Android.mk
+++ b/packages/SettingsLib/tests/robotests/Android.mk
@@ -49,7 +49,7 @@
LOCAL_JAVA_LIBRARIES := \
junit \
- platform-robolectric-3.4.2-prebuilt
+ platform-robolectric-3.5.1-prebuilt
LOCAL_INSTRUMENTATION_FOR := SettingsLibShell
LOCAL_MODULE := SettingsLibRoboTests
@@ -74,4 +74,4 @@
LOCAL_ROBOTEST_TIMEOUT := 36000
-include prebuilts/misc/common/robolectric/3.4.2/run_robotests.mk
+include prebuilts/misc/common/robolectric/3.5.1/run_robotests.mk
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
index 698e442..df850be 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
@@ -38,32 +38,25 @@
final String resDir = appRoot + "/tests/robotests/res";
final String assetsDir = appRoot + config.assetDir();
- final AndroidManifest manifest = new AndroidManifest(Fs.fileFromPath(manifestPath),
- Fs.fileFromPath(resDir), Fs.fileFromPath(assetsDir)) {
+ return new AndroidManifest(Fs.fileFromPath(manifestPath), Fs.fileFromPath(resDir),
+ Fs.fileFromPath(assetsDir), "com.android.settingslib") {
@Override
public List<ResourcePath> getIncludedResourcePaths() {
List<ResourcePath> paths = super.getIncludedResourcePaths();
- SettingsLibRobolectricTestRunner.getIncludedResourcePaths(getPackageName(), paths);
+ paths.add(new ResourcePath(
+ null,
+ Fs.fileFromPath("./frameworks/base/packages/SettingsLib/res"),
+ null));
+ paths.add(new ResourcePath(
+ null,
+ Fs.fileFromPath("./frameworks/base/core/res/res"),
+ null));
+ paths.add(new ResourcePath(
+ null,
+ Fs.fileFromPath("./frameworks/support/v7/appcompat/res"),
+ null));
return paths;
}
};
- manifest.setPackageName("com.android.settingslib");
- return manifest;
}
-
- static void getIncludedResourcePaths(String packageName, List<ResourcePath> paths) {
- paths.add(new ResourcePath(
- null,
- Fs.fileFromPath("./frameworks/base/packages/SettingsLib/res"),
- null));
- paths.add(new ResourcePath(
- null,
- Fs.fileFromPath("./frameworks/base/core/res/res"),
- null));
- paths.add(new ResourcePath(
- null,
- Fs.fileFromPath("./frameworks/support/v7/appcompat/res"),
- null));
- }
-
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
new file mode 100644
index 0000000..fc0019d
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.settingslib.fuelgauge;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.os.IDeviceIdleController;
+
+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.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class PowerWhitelistBackendTest {
+
+ private static final String PACKAGE_ONE = "com.example.packageone";
+ private static final String PACKAGE_TWO = "com.example.packagetwo";
+
+ private PowerWhitelistBackend mPowerWhitelistBackend;
+ @Mock
+ private IDeviceIdleController mDeviceIdleService;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ doReturn(new String[] {}).when(mDeviceIdleService).getFullPowerWhitelist();
+ doReturn(new String[] {}).when(mDeviceIdleService).getSystemPowerWhitelist();
+ doNothing().when(mDeviceIdleService).addPowerSaveWhitelistApp(anyString());
+ doNothing().when(mDeviceIdleService).removePowerSaveWhitelistApp(anyString());
+ mPowerWhitelistBackend = new PowerWhitelistBackend(mDeviceIdleService);
+ }
+
+ @Test
+ public void testIsWhitelisted() throws Exception {
+ doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getFullPowerWhitelist();
+ mPowerWhitelistBackend.refreshList();
+
+ assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
+ assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse();
+
+ mPowerWhitelistBackend.addApp(PACKAGE_TWO);
+
+ verify(mDeviceIdleService, atLeastOnce()).addPowerSaveWhitelistApp(PACKAGE_TWO);
+ assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
+ assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isTrue();
+
+ mPowerWhitelistBackend.removeApp(PACKAGE_TWO);
+
+ verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_TWO);
+ assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
+ assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse();
+
+ mPowerWhitelistBackend.removeApp(PACKAGE_ONE);
+
+ verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_ONE);
+ assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isFalse();
+ assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse();
+ }
+
+ @Test
+ public void testIsSystemWhitelisted() throws Exception {
+ doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getSystemPowerWhitelist();
+ mPowerWhitelistBackend.refreshList();
+
+ assertThat(mPowerWhitelistBackend.isSysWhitelisted(PACKAGE_ONE)).isTrue();
+ assertThat(mPowerWhitelistBackend.isSysWhitelisted(PACKAGE_TWO)).isFalse();
+ assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isFalse();
+
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
new file mode 100644
index 0000000..226166b
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
@@ -0,0 +1,162 @@
+package com.android.settingslib.location;
+
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.when;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.OpEntry;
+import android.app.AppOpsManager.PackageOps;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(
+ manifest = TestConfig.MANIFEST_PATH,
+ sdk = TestConfig.SDK_VERSION)
+public class RecentLocationAppsTest {
+
+ private static final int TEST_UID = 1234;
+ private static final long NOW = System.currentTimeMillis();
+ // App running duration in milliseconds
+ private static final int DURATION = 10;
+ private static final long ONE_MIN_AGO = NOW - TimeUnit.MINUTES.toMillis(1);
+ private static final long FOURTEEN_MIN_AGO = NOW - TimeUnit.MINUTES.toMillis(14);
+ private static final long TWENTY_MIN_AGO = NOW - TimeUnit.MINUTES.toMillis(20);
+ private static final String[] TEST_PACKAGE_NAMES =
+ {"package_1MinAgo", "package_14MinAgo", "package_20MinAgo"};
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private AppOpsManager mAppOpsManager;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private UserManager mUserManager;
+ private int mTestUserId;
+ private RecentLocationApps mRecentLocationApps;
+
+
+
+ @Before
+ public void setUp() throws NameNotFoundException {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
+ when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+ when(mPackageManager.getApplicationLabel(isA(ApplicationInfo.class)))
+ .thenReturn("testApplicationLabel");
+ when(mPackageManager.getUserBadgedLabel(isA(CharSequence.class), isA(UserHandle.class)))
+ .thenReturn("testUserBadgedLabel");
+ mTestUserId = UserHandle.getUserId(TEST_UID);
+ when(mUserManager.getUserProfiles())
+ .thenReturn(Collections.singletonList(new UserHandle(mTestUserId)));
+
+ long[] testRequestTime = {ONE_MIN_AGO, FOURTEEN_MIN_AGO, TWENTY_MIN_AGO};
+ List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime);
+ when(mAppOpsManager.getPackagesForOps(RecentLocationApps.LOCATION_OPS)).thenReturn(appOps);
+ mockTestApplicationInfos(mTestUserId, TEST_PACKAGE_NAMES);
+
+ mRecentLocationApps = new RecentLocationApps(mContext);
+ }
+
+ @Test
+ public void testGetAppList_shouldFilterRecentApps() {
+ List<RecentLocationApps.Request> requests = mRecentLocationApps.getAppList();
+ // Only two of the apps have requested location within 15 min.
+ assertThat(requests).hasSize(2);
+ // Make sure apps are ordered by recency
+ assertThat(requests.get(0).packageName).isEqualTo(TEST_PACKAGE_NAMES[0]);
+ assertThat(requests.get(0).requestFinishTime).isEqualTo(ONE_MIN_AGO + DURATION);
+ assertThat(requests.get(1).packageName).isEqualTo(TEST_PACKAGE_NAMES[1]);
+ assertThat(requests.get(1).requestFinishTime).isEqualTo(FOURTEEN_MIN_AGO + DURATION);
+ }
+
+ @Test
+ public void testGetAppList_shouldNotShowAndroidOS() throws NameNotFoundException {
+ // Add android OS to the list of apps.
+ PackageOps androidSystemPackageOps =
+ createPackageOps(
+ RecentLocationApps.ANDROID_SYSTEM_PACKAGE_NAME,
+ Process.SYSTEM_UID,
+ AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
+ ONE_MIN_AGO,
+ DURATION);
+ long[] testRequestTime =
+ {ONE_MIN_AGO, FOURTEEN_MIN_AGO, TWENTY_MIN_AGO, ONE_MIN_AGO};
+ List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime);
+ appOps.add(androidSystemPackageOps);
+ when(mAppOpsManager.getPackagesForOps(RecentLocationApps.LOCATION_OPS)).thenReturn(appOps);
+ mockTestApplicationInfos(
+ Process.SYSTEM_UID, RecentLocationApps.ANDROID_SYSTEM_PACKAGE_NAME);
+
+ List<RecentLocationApps.Request> requests = mRecentLocationApps.getAppList();
+ // Android OS shouldn't show up in the list of apps.
+ assertThat(requests).hasSize(2);
+ // Make sure apps are ordered by recency
+ assertThat(requests.get(0).packageName).isEqualTo(TEST_PACKAGE_NAMES[0]);
+ assertThat(requests.get(0).requestFinishTime).isEqualTo(ONE_MIN_AGO + DURATION);
+ assertThat(requests.get(1).packageName).isEqualTo(TEST_PACKAGE_NAMES[1]);
+ assertThat(requests.get(1).requestFinishTime).isEqualTo(FOURTEEN_MIN_AGO + DURATION);
+ }
+
+ private void mockTestApplicationInfos(int userId, String... packageNameList)
+ throws NameNotFoundException {
+ for (String packageName : packageNameList) {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = packageName;
+ when(mPackageManager.getApplicationInfoAsUser(
+ packageName, PackageManager.GET_META_DATA, userId)).thenReturn(appInfo);
+ }
+ }
+
+ private List<PackageOps> createTestPackageOpsList(String[] packageNameList, long[] time) {
+ List<PackageOps> packageOpsList = new ArrayList<>();
+ for (int i = 0; i < packageNameList.length ; i++) {
+ PackageOps packageOps = createPackageOps(
+ packageNameList[i],
+ TEST_UID,
+ AppOpsManager.OP_MONITOR_LOCATION,
+ time[i],
+ DURATION);
+ packageOpsList.add(packageOps);
+ }
+ return packageOpsList;
+ }
+
+ private PackageOps createPackageOps(
+ String packageName, int uid, int op, long time, int duration) {
+ return new PackageOps(
+ packageName,
+ uid,
+ Collections.singletonList(createOpEntryWithTime(op, time, duration)));
+ }
+
+ private OpEntry createOpEntryWithTime(int op, long time, int duration) {
+ return new OpEntry(op, AppOpsManager.MODE_ALLOWED, time, 0L, duration, 0, "");
+ }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index fabdbd4..1d3f26ee 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -881,6 +881,9 @@
dumpSetting(s, p,
Settings.Global.GPU_DEBUG_LAYERS,
GlobalSettingsProto.GPU_DEBUG_LAYERS);
+ dumpSetting(s, p,
+ Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
+ GlobalSettingsProto.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING);
// Settings.Global.SHOW_PROCESSES intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Global.LOW_POWER_MODE,
diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml
index c745fe8..bf0d679 100644
--- a/packages/SystemUI/res-keyguard/values-af/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-af/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Toestel is <xliff:g id="NUMBER_0">%d</xliff:g> uur lank nie ontsluit nie. Bevestig wagwoord.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Nie herken nie"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Voer SIM-PIN in; jy het <xliff:g id="NUMBER_1">%d</xliff:g> pogings oor.</item>
+ <item quantity="one">Verkeerde SIM-PIN. Jy het <xliff:g id="NUMBER_0">%d</xliff:g> poging oor voordat jy jou diensverskaffer moet kontak om jou toestel te ontsluit.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM is nou gedeaktiveer. Voer PUK-kode in om voort te gaan. Jy het <xliff:g id="_NUMBER_1">%d</xliff:g> pogings oor voordat die SIM permanent onbruikbaar word. Kontak die diensverskaffer vir besonderhede.</item>
+ <item quantity="one">SIM is nou gedeaktiveer. Voer PUK-kode in om voort te gaan. Jy het <xliff:g id="_NUMBER_0">%d</xliff:g> poging oor voordat die SIM permanent onbruikbaar word. Kontak die diensverskaffer vir besonderhede.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index f4e3001..340e514 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">መሣሪያው ለ<xliff:g id="NUMBER_1">%d</xliff:g> ሰዓቶች አልተከፈተም ነበር። የይለፍ ቃል ያረጋግጡ።</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"አልታወቀም"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">የሲም ፒን ያስገቡ፣ <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
+ <item quantity="other">የሲም ፒን ያስገቡ፣ <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">ሲም አሁን ተሰናክሏል። ለመቀጠል የPUK ኮድ ያስገቡ። ሲም እስከመጨረሻው መጠቀም የማይቻል ከመሆኑ በፊት <xliff:g id="_NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል። ዝርዝሮችን ለማግኘት የአገልግሎት አቅራቢን ያነጋግሩ።</item>
+ <item quantity="other">ሲም አሁን ተሰናክሏል። ለመቀጠል የPUK ኮድ ያስገቡ። ሲም እስከመጨረሻው መጠቀም የማይቻል ከመሆኑ በፊት <xliff:g id="_NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል። ዝርዝሮችን ለማግኘት የአገልግሎት አቅራቢን ያነጋግሩ።</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 839fb87..7027d6f 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -159,4 +159,20 @@
<item quantity="one">لم يتم إلغاء تأمين الجهاز لمدة <xliff:g id="NUMBER_0">%d</xliff:g> ساعة. تأكيد كلمة المرور.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"لم يتم التعرف عليها"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="zero">أدخل رقم التعريف الشخصي لشريحة SIM، وتتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولات.</item>
+ <item quantity="two">أدخل رقم التعريف الشخصي لشريحة SIM، وتتبقى لديك محاولتان (<xliff:g id="NUMBER_1">%d</xliff:g>).</item>
+ <item quantity="few">أدخل رقم التعريف الشخصي لشريحة SIM، وتتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولات.</item>
+ <item quantity="many">أدخل رقم التعريف الشخصي لشريحة SIM، وتتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولة.</item>
+ <item quantity="other">أدخل رقم التعريف الشخصي لشريحة SIM، وتتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولة.</item>
+ <item quantity="one">أدخل رقم التعريف الشخصي لشريحة SIM، وتتبقى لديك محاولة واحدة (<xliff:g id="NUMBER_0">%d</xliff:g>) يجب أن تتصل بعدها بمشغّل شبكة الجوّال لإلغاء قفل الجهاز.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="zero">تم إيقاف شريحة SIM الآن. أدخل رمز PUK للمتابعة، وتتبقى لديك <xliff:g id="_NUMBER_1">%d</xliff:g> محاولة قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. ويمكنك الاتصال بمشغل شبكة الجوّال لمعرفة التفاصيل.</item>
+ <item quantity="two">تم إيقاف شريحة SIM الآن. أدخل رمز PUK للمتابعة، وتتبقى لديك محاولتان (<xliff:g id="_NUMBER_1">%d</xliff:g>) قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. ويمكنك الاتصال بمشغل شبكة الجوّال لمعرفة التفاصيل.</item>
+ <item quantity="few">تم إيقاف شريحة SIM الآن. أدخل رمز PUK للمتابعة، وتتبقى لديك <xliff:g id="_NUMBER_1">%d</xliff:g> محاولات قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. ويمكنك الاتصال بمشغل شبكة الجوّال لمعرفة التفاصيل.</item>
+ <item quantity="many">تم إيقاف شريحة SIM الآن. أدخل رمز PUK للمتابعة، وتتبقى لديك <xliff:g id="_NUMBER_1">%d</xliff:g> محاولة قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. ويمكنك الاتصال بمشغل شبكة الجوّال لمعرفة التفاصيل.</item>
+ <item quantity="other">تم إيقاف شريحة SIM الآن. أدخل رمز PUK للمتابعة، وتتبقى لديك <xliff:g id="_NUMBER_1">%d</xliff:g> محاولة قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. ويمكنك الاتصال بمشغل شبكة الجوّال لمعرفة التفاصيل.</item>
+ <item quantity="one">تم إيقاف شريحة SIM الآن. أدخل رمز PUK للمتابعة، وتتبقى لديك محاولة واحدة (<xliff:g id="_NUMBER_0">%d</xliff:g>) قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. ويمكنك الاتصال بمشغل شبكة الجوّال لمعرفة التفاصيل.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml
index a868b7c..b0503ea 100644
--- a/packages/SystemUI/res-keyguard/values-az/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-az/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Cihaz <xliff:g id="NUMBER_0">%d</xliff:g> saat kiliddən çıxarılmayıb. Parolu təsdiq edin.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Tanınmır"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">SIM PIN-ni daxil edin, <xliff:g id="NUMBER_1">%d</xliff:g> cəhdiniz qalır.</item>
+ <item quantity="one">Yanlış SIM PIN kodu, cihazınızı kiliddən çıxarmaq üçün operatorunuzla əlaqə saxlamadan öncə <xliff:g id="NUMBER_0">%d</xliff:g> cəhdiniz qalır.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM indi deaktivdir. Davam etmək üçün PUK kodunu daxil edin. SIM birdəfəlik yararsız olmadan öncə <xliff:g id="_NUMBER_1">%d</xliff:g> cəhdiniz qalır. Ətraflı məlumat üçün operatorla əlaqə saxlayın.</item>
+ <item quantity="one">SIM indi deaktivdir. Davam etmək üçün PUK kodunu daxil edin. SIM birdəfəlik yararsız olmadan öncə <xliff:g id="_NUMBER_0">%d</xliff:g> cəhdiniz qalır. Ətraflı məlumat üçün operatorla əlaqə saxlayın.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
index e24001c..079ba92 100644
--- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
@@ -141,4 +141,14 @@
<item quantity="other">Niste otključali uređaj <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite lozinku.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Nije prepoznat"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
+ <item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
+ <item quantity="other">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaj pre nego što SIM postane trajno neupotrebljiv. Detaljne informacije potražite od mobilnog operatera.</item>
+ <item quantity="few">SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja pre nego što SIM postane trajno neupotrebljiv. Detaljne informacije potražite od mobilnog operatera.</item>
+ <item quantity="other">SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja pre nego što SIM postane trajno neupotrebljiv. Detaljne informacije potražite od mobilnog operatera.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml
index 359271d..a3a381549 100644
--- a/packages/SystemUI/res-keyguard/values-be/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-be/strings.xml
@@ -147,4 +147,16 @@
<item quantity="other">Прылада не была разблакіравана на працягу <xliff:g id="NUMBER_1">%d</xliff:g> гадзіны. Увядзіце пароль.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Не распазнаны"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Увядзіце PIN-код SIM-карты. У вас ёсць яшчэ <xliff:g id="NUMBER_1">%d</xliff:g> спроба.</item>
+ <item quantity="few">Увядзіце PIN-код SIM-карты. У вас ёсць яшчэ <xliff:g id="NUMBER_1">%d</xliff:g> спробы.</item>
+ <item quantity="many">Увядзіце PIN-код SIM-карты. У вас ёсць яшчэ <xliff:g id="NUMBER_1">%d</xliff:g> спроб.</item>
+ <item quantity="other">Увядзіце PIN-код SIM-карты. У вас ёсць яшчэ <xliff:g id="NUMBER_1">%d</xliff:g> спробы.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">SIM-карта заблакіравана. Каб працягнуць, увядзіце PUK-код. У вас ёсць яшчэ <xliff:g id="_NUMBER_1">%d</xliff:g> спроба, пасля чаго SIM-карта будзе заблакіравана назаўсёды. Звярніцеся да аператара, каб даведацца больш.</item>
+ <item quantity="few">SIM-карта заблакіравана. Каб працягнуць, увядзіце PUK-код. У вас ёсць яшчэ <xliff:g id="_NUMBER_1">%d</xliff:g> спробы, пасля чаго SIM-карта будзе заблакіравана назаўсёды. Звярніцеся да аператара, каб даведацца больш.</item>
+ <item quantity="many">SIM-карта заблакіравана. Каб працягнуць, увядзіце PUK-код. У вас ёсць яшчэ <xliff:g id="_NUMBER_1">%d</xliff:g> спроб, пасля чаго SIM-карта будзе заблакіравана назаўсёды. Звярніцеся да аператара, каб даведацца больш.</item>
+ <item quantity="other">SIM-карта заблакіравана. Каб працягнуць, увядзіце PUK-код. У вас ёсць яшчэ <xliff:g id="_NUMBER_1">%d</xliff:g> спробы, пасля чаго SIM-карта будзе заблакіравана назаўсёды. Звярніцеся да аператара, каб даведацца больш.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml
index c098396..87c7519 100644
--- a/packages/SystemUI/res-keyguard/values-bg/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Устройството не е отключвано от <xliff:g id="NUMBER_0">%d</xliff:g> час. Потвърдете паролата.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Не е разпознато"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Въведете ПИН кода за SIM картата – остават ви <xliff:g id="NUMBER_1">%d</xliff:g> опита.</item>
+ <item quantity="one">Въведете ПИН кода за SIM картата – остава ви <xliff:g id="NUMBER_0">%d</xliff:g> опит, преди да трябва да се свържете с оператора си, за да отключите устройството.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM картата вече е деактивирана. Въведете PUK кода, за да продължите. Остават ви <xliff:g id="_NUMBER_1">%d</xliff:g> опита, преди SIM картата да стане неизползваема завинаги. Свържете се с оператора за подробности.</item>
+ <item quantity="one">SIM картата вече е деактивирана. Въведете PUK кода, за да продължите. Остава ви <xliff:g id="_NUMBER_0">%d</xliff:g> опит, преди SIM картата да стане неизползваема завинаги. Свържете се с оператора за подробности.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml
index 22e67b8..791f5e9 100644
--- a/packages/SystemUI/res-keyguard/values-bn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">ডিভাইসটি <xliff:g id="NUMBER_1">%d</xliff:g> ঘন্টা ধরে আনলক করা হয় নি। পাসওয়ার্ড নিশ্চিত করুন।</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"স্বীকৃত নয়"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">সিমের পিন লিখুন। আপনি আর <xliff:g id="NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন।</item>
+ <item quantity="other">সিমের পিন লিখুন। আপনি আর <xliff:g id="NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন।</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">সিম অক্ষম করা হয়েছে। চালিয়ে যেতে PUK কোড লিখুন। আপনি আর <xliff:g id="_NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন, তারপরে এই সিমটি আর একেবারেই ব্যবহার করা যাবে না। বিশদে জানতে পরিষেবা প্রদানকারীর সাথে যোগাযোগ করুন।</item>
+ <item quantity="other">সিম অক্ষম করা হয়েছে। চালিয়ে যেতে PUK কোড লিখুন। আপনি আর <xliff:g id="_NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন, তারপরে এই সিমটি আর একেবারেই ব্যবহার করা যাবে না। বিশদে জানতে পরিষেবা প্রদানকারীর সাথে যোগাযোগ করুন।</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index 52712287..b74a618 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -141,4 +141,14 @@
<item quantity="other">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite lozinku.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Nije prepoznat"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Unesite PIN kôd za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
+ <item quantity="few">Unesite PIN kôd za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
+ <item quantity="other">Unesite PIN kôd za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">SIM kartica je onemogućena. Unesite PUK kôd da nastavite. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaj prije nego što SIM kartica postane trajno neupotrebljiva. Obratite se operateru za više informacija.</item>
+ <item quantity="few">SIM kartica je onemogućena. Unesite PUK kôd da nastavite. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Obratite se operateru za više informacija.</item>
+ <item quantity="other">SIM kartica je onemogućena. Unesite PUK kôd da nastavite. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Obratite se operateru za više informacija.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml
index 9aa8229..dd2844a 100644
--- a/packages/SystemUI/res-keyguard/values-ca/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Fa <xliff:g id="NUMBER_0">%d</xliff:g> hora que no es desbloqueja el dispositiu. Confirma la contrasenya.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"No s\'ha reconegut"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Introdueix el PIN de la SIM. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents.</item>
+ <item quantity="one">Introdueix el PIN de la SIM. Et queda <xliff:g id="NUMBER_0">%d</xliff:g> intent; si no l\'encertes, contacta amb l\'operador de telefonia mòbil per desbloquejar el dispositiu.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">La targeta SIM s\'ha desactivat. Introdueix el codi PUK per continuar. Et queden <xliff:g id="_NUMBER_1">%d</xliff:g> intents; si no l\'encertes, la SIM no es podrà tornar a fer servir. Contacta amb l\'operador de telefonia mòbil per obtenir-ne més informació.</item>
+ <item quantity="one">La targeta SIM s\'ha desactivat. Introdueix el codi PUK per continuar. Et queda <xliff:g id="_NUMBER_0">%d</xliff:g> intent; si no l\'encertes, la SIM no es podrà tornar a fer servir. Contacta amb l\'operador de telefonia mòbil per obtenir-ne més informació.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml
index f5864cd..cf7abf1 100644
--- a/packages/SystemUI/res-keyguard/values-cs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml
@@ -147,4 +147,16 @@
<item quantity="one">Zařízení již <xliff:g id="NUMBER_0">%d</xliff:g> hodinu nebylo odemknuto. Zadejte heslo.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Nerozpoznáno"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="few">Zadejte PIN SIM karty. Zbývají <xliff:g id="NUMBER_1">%d</xliff:g> pokusy.</item>
+ <item quantity="many">Zadejte PIN SIM karty. Zbývá <xliff:g id="NUMBER_1">%d</xliff:g> pokusu.</item>
+ <item quantity="other">Zadejte PIN SIM karty. Zbývá <xliff:g id="NUMBER_1">%d</xliff:g> pokusů.</item>
+ <item quantity="one">Zadejte PIN SIM karty. Zbývá <xliff:g id="NUMBER_0">%d</xliff:g> pokus, poté bude muset zařízení odemknout operátor.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="few">SIM karta je nyní zablokována. Chcete-li pokračovat, zadejte kód PUK. Máte ještě <xliff:g id="_NUMBER_1">%d</xliff:g> pokusy, poté bude SIM karta natrvalo zablokována. Podrobnosti vám poskytne operátor.</item>
+ <item quantity="many">SIM karta je nyní zablokována. Chcete-li pokračovat, zadejte kód PUK. Máte ještě <xliff:g id="_NUMBER_1">%d</xliff:g> pokusu, poté bude SIM karta natrvalo zablokována. Podrobnosti vám poskytne operátor.</item>
+ <item quantity="other">SIM karta je nyní zablokována. Chcete-li pokračovat, zadejte kód PUK. Máte ještě <xliff:g id="_NUMBER_1">%d</xliff:g> pokusů, poté bude SIM karta natrvalo zablokována. Podrobnosti vám poskytne operátor.</item>
+ <item quantity="one">SIM karta je nyní zablokována. Chcete-li pokračovat, zadejte kód PUK. Máte ještě <xliff:g id="_NUMBER_0">%d</xliff:g> pokus, poté bude SIM karta natrvalo zablokována. Podrobnosti vám poskytne operátor.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml
index cb9989f..156da2e 100644
--- a/packages/SystemUI/res-keyguard/values-da/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-da/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">Enheden blev sidst låst op for <xliff:g id="NUMBER_1">%d</xliff:g> timer siden. Bekræft adgangskoden.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Ikke genkendt"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Indtast pinkoden til SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøg tilbage.</item>
+ <item quantity="other">Indtast pinkoden til SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøg tilbage.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">SIM-kortet er nu deaktiveret. Indtast PUK-koden for at fortsætte. Du har <xliff:g id="_NUMBER_1">%d</xliff:g> forsøg tilbage, før SIM-kortet bliver permanent ubrugeligt. Kontakt dit mobilselskab for at få flere oplysninger.</item>
+ <item quantity="other">SIM-kortet er nu deaktiveret. Indtast PUK-koden for at fortsætte. Du har <xliff:g id="_NUMBER_1">%d</xliff:g> forsøg tilbage, før SIM-kortet bliver permanent ubrugeligt. Kontakt dit mobilselskab for at få flere oplysninger.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml
index 372ca16..852a9c1 100644
--- a/packages/SystemUI/res-keyguard/values-de/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-de/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Das Gerät wurde seit <xliff:g id="NUMBER_0">%d</xliff:g> Stunde nicht mehr entsperrt. Bitte bestätige das Passwort.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Nicht erkannt"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Gib die PIN für die SIM-Karte ein. Du hast noch <xliff:g id="NUMBER_1">%d</xliff:g> Versuche.</item>
+ <item quantity="one">Gib die PIN für die SIM-Karte ein. Du hast noch <xliff:g id="NUMBER_0">%d</xliff:g> Versuch, bevor das Gerät vom Mobilfunkanbieter entsperrt werden muss.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">Die SIM-Karte ist jetzt deaktiviert. Gib den PUK-Code ein, um fortzufahren. Du hast noch <xliff:g id="_NUMBER_1">%d</xliff:g> Versuche, bevor die SIM-Karte endgültig gesperrt wird. Weitere Informationen erhältst du von deinem Mobilfunkanbieter.</item>
+ <item quantity="one">Die SIM-Karte ist jetzt deaktiviert. Gib den PUK-Code ein, um fortzufahren. Du hast noch <xliff:g id="_NUMBER_0">%d</xliff:g> Versuch, bevor die SIM-Karte endgültig gesperrt wird. Weitere Informationen erhältst du von deinem Mobilfunkanbieter.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml
index 48faeba..0127ef1 100644
--- a/packages/SystemUI/res-keyguard/values-el/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-el/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Η συσκευή δεν έχει ξεκλειδωθεί εδώ και <xliff:g id="NUMBER_0">%d</xliff:g> ώρα. Επιβεβαιώστε τον κωδικό πρόσβασης.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Δεν αναγνωρίστηκε"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Καταχωρίστε τον αριθμό PIN της κάρτας SIM. Απομένουν <xliff:g id="NUMBER_1">%d</xliff:g> ακόμη προσπάθειες.</item>
+ <item quantity="one">Εσφαλμένος αριθμός PIN κάρτας SIM. Απομένει άλλη <xliff:g id="NUMBER_0">%d</xliff:g> προσπάθεια. Στη συνέχεια, θα πρέπει να επικοινωνήσετε με τον πάροχο κινητής τηλεφωνίας, για να ξεκλειδώσετε τη συσκευή.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">Η κάρτα SIM απενεργοποιήθηκε. Καταχωρίστε τον κωδικό PUK, για να συνεχίσετε. Απομένουν <xliff:g id="_NUMBER_1">%d</xliff:g> ακόμη προσπάθειες προτού να μην είναι πλέον δυνατή η χρήση της κάρτας SIM. Επικοινωνήστε με την εταιρεία κινητής τηλεφωνίας για λεπτομέρειες.</item>
+ <item quantity="one">Η κάρτα SIM απενεργοποιήθηκε. Καταχωρίστε τον κωδικό PUK, για να συνεχίσετε. Απομένει <xliff:g id="_NUMBER_0">%d</xliff:g> ακόμη προσπάθεια προτού να μην είναι πλέον δυνατή η χρήση της κάρτας SIM. Επικοινωνήστε με την εταιρεία κινητής τηλεφωνίας για λεπτομέρειες.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
index b7c026f..df0c45b 100644
--- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Hace <xliff:g id="NUMBER_0">%d</xliff:g> hora que no se desbloquea el dispositivo. Confirma la contraseña.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"No se reconoció"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Ingresa el PIN de la SIM. Te quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos más.</item>
+ <item quantity="one">Ingresa el PIN de la SIM. Te queda <xliff:g id="NUMBER_0">%d</xliff:g> intento antes de que debas comunicarte con tu proveedor para desbloquear el dispositivo.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">Se inhabilitó la SIM. Para continuar, ingresa el código PUK. Te quedan <xliff:g id="_NUMBER_1">%d</xliff:g> intentos más antes de que la SIM quede inutilizable permanentemente. Comunícate con tu proveedor para obtener más detalles.</item>
+ <item quantity="one">Se inhabilitó la SIM. Para continuar, ingresa el código PUK. Te queda <xliff:g id="_NUMBER_0">%d</xliff:g> intento más antes de que la SIM quede inutilizable permanentemente. Comunícate con tu proveedor para obtener más detalles.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml
index ac99a84..ec36fd7 100644
--- a/packages/SystemUI/res-keyguard/values-es/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">El dispositivo no se ha desbloqueado durante <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirma la contraseña.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"No reconocido"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Introduce el PIN de la tarjeta SIM. Te quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos.</item>
+ <item quantity="one">Introduce el PIN de la tarjeta SIM. Te queda <xliff:g id="NUMBER_0">%d</xliff:g> para tener que ponerte en contacto con tu operador para desbloquear el dispositivo.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">La tarjeta SIM está inhabilitada. Introduce el código PUK para continuar. Te quedan <xliff:g id="_NUMBER_1">%d</xliff:g> intentos para que la tarjeta SIM quede inservible de forma permanente. Ponte en contacto con tu operador para obtener más información.</item>
+ <item quantity="one">La tarjeta SIM está inhabilitada. Introduce el código PUK para continuar. Te queda <xliff:g id="_NUMBER_0">%d</xliff:g> intento para que la tarjeta SIM quede inservible de forma permanente. Ponte en contacto con tu operador para obtener más información.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml
index b2c00bf..bca98ef 100644
--- a/packages/SystemUI/res-keyguard/values-et/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-et/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Seadet pole avatud <xliff:g id="NUMBER_0">%d</xliff:g> tund. Kinnitage parool.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Ei tuvastatud"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Sisestage SIM-kaardi PIN-kood. Jäänud on <xliff:g id="NUMBER_1">%d</xliff:g> katset.</item>
+ <item quantity="one">Sisestage SIM-kaardi PIN-kood. Jäänud on <xliff:g id="NUMBER_0">%d</xliff:g> katse enne, kui peate seadme avamiseks ühendust võtma operaatoriga.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM-kaart on nüüd keelatud. Jätkamiseks sisestage PUK-kood. Teil on jäänud veel <xliff:g id="_NUMBER_1">%d</xliff:g> katset enne, kui SIM-kaart püsivalt lukustatakse. Lisateavet küsige operaatorilt.</item>
+ <item quantity="one">SIM-kaart on nüüd keelatud. Jätkamiseks sisestage PUK-kood. Teil on jäänud veel <xliff:g id="_NUMBER_0">%d</xliff:g> katse enne, kui SIM-kaart püsivalt lukustatakse. Lisateavet küsige operaatorilt.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index c8eab84..3d79ae2 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Gailua ez da desblokeatu <xliff:g id="NUMBER_0">%d</xliff:g> orduz. Berretsi pasahitza.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Ez da ezagutu"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Idatzi SIM txartelaren PIN kodea. <xliff:g id="NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu.</item>
+ <item quantity="one">Idatzi SIM txartelaren PIN kodea. <xliff:g id="NUMBER_0">%d</xliff:g> saiakera geratzen zaizu; oker idatziz gero, operadoreari eskatu beharko diozu gailua desblokeatzeko.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">Desgaitu egin da SIM txartela. Aurrera egiteko, idatzi PUK kodea. <xliff:g id="_NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu SIM txartela betiko erabilgaitz geratu aurretik. Xehetasunak lortzeko, jarri operadorearekin harremanetan.</item>
+ <item quantity="one">Desgaitu egin da SIM txartela. Aurrera egiteko, idatzi PUK kodea. <xliff:g id="_NUMBER_0">%d</xliff:g> saiakera geratzen zaizu SIM txartela betiko erabilgaitz geratu aurretik. Xehetasunak lortzeko, jarri operadorearekin harremanetan.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml
index 819b0e8..ce46e6a 100644
--- a/packages/SystemUI/res-keyguard/values-fa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">قفل دستگاه <xliff:g id="NUMBER_1">%d</xliff:g> ساعت باز نشده است. گذرواژه را تأیید کنید.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"شناسایی نشد"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">پین سیمکارت را وارد کنید. <xliff:g id="NUMBER_1">%d</xliff:g> تلاش دیگری باقی مانده است.</item>
+ <item quantity="other">پین سیمکارت را وارد کنید. <xliff:g id="NUMBER_1">%d</xliff:g> تلاش دیگری باقی مانده است.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">سیمکارت اکنون غیرفعال است. برای ادامه دادن کد PUK را وارد کنید. <xliff:g id="_NUMBER_1">%d</xliff:g> تلاش دیگر باقی مانده است و پس از آن سیمکارت برای همیشه غیرقابلاستفاده میشود. برای اطلاع از جزئیات با شرکت مخابراتی تماس بگیرید.</item>
+ <item quantity="other">سیمکارت اکنون غیرفعال است. برای ادامه دادن کد PUK را وارد کنید. <xliff:g id="_NUMBER_1">%d</xliff:g> تلاش دیگر باقی مانده است و پس از آن سیمکارت برای همیشه غیرقابلاستفاده میشود. برای اطلاع از جزئیات با شرکت مخابراتی تماس بگیرید.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index dc2a441..2b14e3c 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Laitteen lukitusta ei ole avattu <xliff:g id="NUMBER_0">%d</xliff:g> tuntiin. Vahvista salasana.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Ei tunnistettu"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Anna SIM-kortin PIN-koodi. Sinulla on <xliff:g id="NUMBER_1">%d</xliff:g> yritystä jäljellä.</item>
+ <item quantity="one">Anna SIM-kortin PIN-koodi. <xliff:g id="NUMBER_0">%d</xliff:g> yrityksen jälkeen laite lukittuu, ja vain operaattori voi avata sen.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM-kortti on nyt lukittu. Anna PUK-koodi, niin voit jatkaa. Sinulla on <xliff:g id="_NUMBER_1">%d</xliff:g> yritystä jäljellä, ennen kuin SIM-kortti poistuu pysyvästi käytöstä. Pyydä lisätietoja operaattoriltasi.</item>
+ <item quantity="one">SIM-kortti on nyt lukittu. Anna PUK-koodi, niin voit jatkaa. Sinulla on <xliff:g id="_NUMBER_0">%d</xliff:g> yritys jäljellä, ennen kuin SIM-kortti poistuu pysyvästi käytöstä. Pyydä lisätietoja operaattoriltasi.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index 8e614e0..b033311 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heures. Confirmez le mot de passe.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Doigt non reconnu"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Entrez le NIP de votre carte SIM. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative.</item>
+ <item quantity="other">Entrez le NIP de votre carte SIM. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">La carte SIM est maintenant désactivée. Entrez le code PUK pour continuer. Il vous reste <xliff:g id="_NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM devienne définitivement inutilisable. Pour obtenir plus de détails, communiquez avec votre fournisseur de services.</item>
+ <item quantity="other">La carte SIM est maintenant désactivée. Entrez le code PUK pour continuer. Il vous reste <xliff:g id="_NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM devienne définitivement inutilisable. Pour obtenir plus de détails, communiquez avec votre fournisseur de services.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml
index b3ed4c5..34b638e 100644
--- a/packages/SystemUI/res-keyguard/values-fr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">L\'appareil n\'a pas été déverrouillé depuis <xliff:g id="NUMBER_1">%d</xliff:g> heures. Confirmez le mot de passe.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Non reconnu"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Saisissez le code PIN de la carte SIM. <xliff:g id="NUMBER_1">%d</xliff:g> tentative restante.</item>
+ <item quantity="other">Saisissez le code PIN de la carte SIM. <xliff:g id="NUMBER_1">%d</xliff:g> tentatives restantes.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">La carte SIM est maintenant désactivée. Saisissez le code PUK pour continuer. Il vous reste <xliff:g id="_NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM ne devienne définitivement inutilisable. Pour de plus amples informations, veuillez contacter votre opérateur.</item>
+ <item quantity="other">La carte SIM est maintenant désactivée. Saisissez le code PUK pour continuer. Il vous reste <xliff:g id="_NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM ne devienne définitivement inutilisable. Pour de plus amples informations, veuillez contacter votre opérateur.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml
index 7be5f41..e21e844 100644
--- a/packages/SystemUI/res-keyguard/values-gl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">O dispositivo non se desbloqueou durante <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirma o contrasinal.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Non se recoñece"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Introduce o código PIN da SIM. Quédanche <xliff:g id="NUMBER_1">%d</xliff:g> intentos.</item>
+ <item quantity="one">Introduce o código PIN da SIM. Quédache <xliff:g id="NUMBER_0">%d</xliff:g> intento antes de que teñas que contactar co operador para desbloquear o dispositivo.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">A SIM está desactivada. Introduce o código PUK para continuar. Quédanche <xliff:g id="_NUMBER_1">%d</xliff:g> intentos antes de que a SIM quede inutilizable para sempre. Contacta co operador para obter información.</item>
+ <item quantity="one">A SIM está desactivada. Introduce o código PUK para continuar. Quédache <xliff:g id="_NUMBER_0">%d</xliff:g> intento antes de que a SIM quede inutilizable para sempre. Contacta co operador para obter información.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml
index 48aa75b..c62bab8 100644
--- a/packages/SystemUI/res-keyguard/values-gu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">ઉપકરણને <xliff:g id="NUMBER_1">%d</xliff:g> કલાક માટે અનલૉક કરવામાં આવ્યું નથી. પાસવર્ડની પુષ્ટિ કરો.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"ઓળખાયેલ નથી"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">સિમ પિન દાખલ કરો, તમારી પાસે <xliff:g id="NUMBER_1">%d</xliff:g> પ્રયાસ બાકી છે.</item>
+ <item quantity="other">સિમ પિન દાખલ કરો, તમારી પાસે <xliff:g id="NUMBER_1">%d</xliff:g> પ્રયાસો બાકી છે.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">સિમ હવે બંધ કરેલ છે. ચાલુ રાખવા માટે PUK કોડ દાખલ કરો. સિમ કાયમીરૂપે બિનઉપયોગી બની જાય એ પહેલાં તમારી પાસે <xliff:g id="_NUMBER_1">%d</xliff:g> પ્રયાસ બાકી છે. વિગતો માટે કૅરિઅરનો સંપર્ક કરો.</item>
+ <item quantity="other">સિમ હવે બંધ કરેલ છે. ચાલુ રાખવા માટે PUK કોડ દાખલ કરો. સિમ કાયમીરૂપે બિનઉપયોગી બની જાય એ પહેલાં તમારી પાસે <xliff:g id="_NUMBER_1">%d</xliff:g> પ્રયાસો બાકી છે. વિગતો માટે કૅરિઅરનો સંપર્ક કરો.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml
index 40966c0..641c072 100644
--- a/packages/SystemUI/res-keyguard/values-hi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">डिवाइस को <xliff:g id="NUMBER_1">%d</xliff:g> घंटों से अनलॉक नहीं किया गया है. पासवर्ड की पुष्टि करें.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"उंगली की पहचान नहीं हो सकी"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">सिम का पिन डालें, आपके पास <xliff:g id="NUMBER_1">%d</xliff:g> मौके बचे हैं.</item>
+ <item quantity="other">सिम का पिन डालें, आपके पास <xliff:g id="NUMBER_1">%d</xliff:g> मौके बचे हैं.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">सिम बंद कर दिया गया है. जारी रखने के लिए PUK कोड डालें. आपके पास <xliff:g id="_NUMBER_1">%d</xliff:g> मौके बचे हैं, उसके बाद, सिम हमेशा के लिए काम करना बंद कर देगा. जानकारी के लिए, मोबाइल और इंटरनेट सेवा देने वाली कंपनी से संपर्क करें.</item>
+ <item quantity="other">सिम बंद कर दिया गया है. जारी रखने के लिए PUK कोड डालें. आपके पास <xliff:g id="_NUMBER_1">%d</xliff:g> मौके बचे हैं, उसके बाद, सिम हमेशा के लिए काम करना बंद कर देगा. जानकारी के लिए, मोबाइल और इंटरनेट सेवा देने वाली कंपनी से संपर्क करें.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml
index 4247ede..cfdf5cd 100644
--- a/packages/SystemUI/res-keyguard/values-hr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml
@@ -141,4 +141,14 @@
<item quantity="other">Uređaj nije bio otključan <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite zaporku.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Nije prepoznat"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
+ <item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
+ <item quantity="other">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaj prije nego što SIM kartica postane trajno neupotrebljiva. Više informacija zatražite od mobilnog operatera.</item>
+ <item quantity="few">SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Više informacija zatražite od mobilnog operatera.</item>
+ <item quantity="other">SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Više informacija zatražite od mobilnog operatera.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml
index 30ccb67..71f34a9 100644
--- a/packages/SystemUI/res-keyguard/values-hu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Az eszköz zárolása <xliff:g id="NUMBER_0">%d</xliff:g> órája nem lett feloldva. Erősítse meg a jelszót.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Nem sikerült felismerni"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Adja meg a SIM-kártya PIN-kódját. <xliff:g id="NUMBER_1">%d</xliff:g> próbálkozása maradt.</item>
+ <item quantity="one">Adja meg a SIM-kártya PIN-kódját. <xliff:g id="NUMBER_0">%d</xliff:g> próbálkozása maradt. Ha elfogynak a próbálkozási lehetőségek, az eszköz feloldásához fel kell vennie a kapcsolatot szolgáltatójával.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">A SIM-kártya le van tiltva. A folytatáshoz adja meg a PUK-kódot. Még <xliff:g id="_NUMBER_1">%d</xliff:g> próbálkozása van, mielőtt végleg használhatatlanná válik a SIM-kártya. További információért forduljon a szolgáltatóhoz.</item>
+ <item quantity="one">A SIM-kártya le van tiltva. A folytatáshoz adja meg a PUK-kódot. Még <xliff:g id="_NUMBER_0">%d</xliff:g> próbálkozása van, mielőtt végleg használhatatlanná válik a SIM-kártya. További információért forduljon a szolgáltatóhoz.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml
index 7914b5f..2936935 100644
--- a/packages/SystemUI/res-keyguard/values-hy/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">Սարքը չի ապակողպվել <xliff:g id="NUMBER_1">%d</xliff:g> ժամվա ընթացքում: Հաստատեք գաղտնաբառը:</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Չճանաչվեց"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Մուտքագրեք SIM քարտի PIN կոդը: Մնացել է <xliff:g id="NUMBER_1">%d</xliff:g> փորձ:</item>
+ <item quantity="other">Մուտքագրեք SIM քարտի PIN կոդը: Մնացել է <xliff:g id="NUMBER_1">%d</xliff:g> փորձ:</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">SIM քարտն անջատված է: Շարունակելու համար մուտքագրեք PUK կոդը: Մնացել է <xliff:g id="_NUMBER_1">%d</xliff:g> փորձ, որից հետո SIM քարտն այլևս հնարավոր չի լինի օգտագործել: Մանրամասների համար դիմեք օպերատորին:</item>
+ <item quantity="other">SIM քարտն անջատված է: Շարունակելու համար մուտքագրեք PUK կոդը: Մնացել է <xliff:g id="_NUMBER_1">%d</xliff:g> փորձ, որից հետո SIM քարտն այլևս հնարավոր չի լինի օգտագործել: Մանրամասների համար դիմեք օպերատորին:</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml
index e417a3f..ee3621c 100644
--- a/packages/SystemUI/res-keyguard/values-in/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-in/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Perangkat belum dibuka kuncinya selama <xliff:g id="NUMBER_0">%d</xliff:g> jam. Konfirmasi sandi.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Tidak dikenali"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Masukkan PIN SIM, tersisa <xliff:g id="NUMBER_1">%d</xliff:g> percobaan.</item>
+ <item quantity="one">Masukkan PIN SIM, tersisa <xliff:g id="NUMBER_0">%d</xliff:g> percobaan sebelum Anda harus menghubungi operator untuk membuka kunci perangkat.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM kini dinonaktifkan. Masukkan kode PUK untuk melanjutkan. Tersisa <xliff:g id="_NUMBER_1">%d</xliff:g> percobaan sebelum SIM tidak dapat digunakan secara permanen. Hubungi operator untuk mengetahui detailnya.</item>
+ <item quantity="one">SIM kini dinonaktifkan. Masukkan kode PUK untuk melanjutkan. Tersisa <xliff:g id="_NUMBER_0">%d</xliff:g> percobaan sebelum SIM tidak dapat digunakan secara permanen. Hubungi operator untuk mengetahui detailnya.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml
index 6d92dba..cead5c4 100644
--- a/packages/SystemUI/res-keyguard/values-is/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-is/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">Tækið hefur ekki verið tekið úr lás í <xliff:g id="NUMBER_1">%d</xliff:g> klukkustundir. Staðfestu aðgangsorðið.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Þekktist ekki"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Sláðu inn PIN-númer SIM-korts. Það er <xliff:g id="NUMBER_1">%d</xliff:g> tilraun eftir.</item>
+ <item quantity="other">Sláðu inn PIN-númer SIM-korts. Það eru <xliff:g id="NUMBER_1">%d</xliff:g> tilraunir eftir.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">SIM-kortið er nú óvirkt. Sláðu inn PUK-númer til að halda áfram. Það er <xliff:g id="_NUMBER_1">%d</xliff:g> tilraun eftir þar til SIM-kortið verður ónothæft til frambúðar. Hafðu samband við símafyrirtækið til að fá upplýsingar.</item>
+ <item quantity="other">SIM-kortið er nú óvirkt. Sláðu inn PUK-númer til að halda áfram. Það eru <xliff:g id="_NUMBER_1">%d</xliff:g> tilraunir eftir þar til SIM-kortið verður ónothæft til frambúðar. Hafðu samband við símafyrirtækið til að fá upplýsingar.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index f449202..001f90c 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Il dispositivo non viene sbloccato da <xliff:g id="NUMBER_0">%d</xliff:g> ora. Conferma la password.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Non riconosciuta"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Inserisci il codice PIN della SIM. Hai ancora <xliff:g id="NUMBER_1">%d</xliff:g> tentativi a disposizione.</item>
+ <item quantity="one">Inserisci il codice PIN della SIM. Hai ancora <xliff:g id="NUMBER_0">%d</xliff:g> tentativo a disposizione, dopodiché dovrai contattare l\'operatore per sbloccare il dispositivo.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">La scheda SIM è ora disattivata. Inserisci il codice PUK per continuare. Hai ancora <xliff:g id="_NUMBER_1">%d</xliff:g> tentativi a disposizione prima che la SIM diventi definitivamente inutilizzabile. Contatta l\'operatore per avere informazioni dettagliate.</item>
+ <item quantity="one">La scheda SIM è ora disattivata. Inserisci il codice PUK per continuare. Hai ancora <xliff:g id="_NUMBER_0">%d</xliff:g> tentativo a disposizione prima che la SIM diventi definitivamente inutilizzabile. Contatta l\'operatore per avere informazioni dettagliate.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml
index cb61b7d..8be3a89 100644
--- a/packages/SystemUI/res-keyguard/values-iw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml
@@ -147,4 +147,16 @@
<item quantity="one">נעילת המכשיר לא בוטלה במשך <xliff:g id="NUMBER_0">%d</xliff:g> שעה. הזן את הסיסמה.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"לא זוהתה"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="two">יש להזין PIN של כרטיס SIM, נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות נוספים.</item>
+ <item quantity="many">יש להזין PIN של כרטיס SIM, נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות נוספים.</item>
+ <item quantity="other">יש להזין PIN של כרטיס SIM, נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות נוספים.</item>
+ <item quantity="one">יש להזין PIN של כרטיס SIM. נותר לך <xliff:g id="NUMBER_0">%d</xliff:g> ניסיון נוסף לפני שיהיה צורך ליצור קשר עם הספק כדי לבטל את נעילת המכשיר.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="two">כרטיס ה-SIM מושבת כעת. יש להזין קוד PUK כדי להמשיך. נותרו לך <xliff:g id="_NUMBER_1">%d</xliff:g> ניסיונות נוספים לפני שכרטיס ה-SIM ינעל לצמיתות. למידע נוסף, ניתן לפנות לספק שלך.</item>
+ <item quantity="many">כרטיס ה-SIM מושבת כעת. יש להזין קוד PUK כדי להמשיך. נותרו לך <xliff:g id="_NUMBER_1">%d</xliff:g> ניסיונות נוספים לפני שכרטיס ה-SIM ינעל לצמיתות. למידע נוסף, ניתן לפנות לספק שלך.</item>
+ <item quantity="other">כרטיס ה-SIM מושבת כעת. יש להזין קוד PUK כדי להמשיך. נותרו לך <xliff:g id="_NUMBER_1">%d</xliff:g> ניסיונות נוספים לפני שכרטיס ה-SIM ינעל לצמיתות. למידע נוסף, ניתן לפנות לספק שלך.</item>
+ <item quantity="one">כרטיס ה-SIM מושבת כעת. יש להזין קוד PUK כדי להמשיך. נותר לך <xliff:g id="_NUMBER_0">%d</xliff:g> ניסיון נוסף לפני שכרטיס ה-SIM ינעל לצמיתות. למידע נוסף, ניתן לפנות לספק שלך.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml
index 8aa2e47..6bdc200 100644
--- a/packages/SystemUI/res-keyguard/values-ja/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">端末のロックが <xliff:g id="NUMBER_0">%d</xliff:g> 時間、解除されていません。パスワードを確認してください。</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"認識されませんでした"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">SIM PIN を入力してください。入力できるのはあと <xliff:g id="NUMBER_1">%d</xliff:g> 回です。</item>
+ <item quantity="one">SIM PIN を入力してください。入力できるのはあと <xliff:g id="NUMBER_0">%d</xliff:g> 回です。この回数を超えた場合は、携帯通信会社にお問い合わせください。</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM が無効になりました。続行するには PUK コードを入力してください。入力できるのはあと <xliff:g id="_NUMBER_1">%d</xliff:g> 回です。この回数を超えると SIM は完全に使用できなくなります。詳しくは携帯通信会社にお問い合わせください。</item>
+ <item quantity="one">SIM が無効になりました。続行するには PUK コードを入力してください。入力できるのはあと <xliff:g id="_NUMBER_0">%d</xliff:g> 回です。この回数を超えると SIM は完全に使用できなくなります。詳しくは携帯通信会社にお問い合わせください。</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml
index 1ff7a96..31ccaae 100644
--- a/packages/SystemUI/res-keyguard/values-ka/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">მოწყობილობა არ განბლოკილა <xliff:g id="NUMBER_0">%d</xliff:g> საათის განმავლობაში. დაადასტურეთ პაროლი.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"არ არის ამოცნობილი"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">შეიყვანეთ SIM ბარათის PIN-კოდი. თქვენ დაგრჩათ <xliff:g id="NUMBER_1">%d</xliff:g> მცდელობა.</item>
+ <item quantity="one">შეიყვანეთ SIM ბარათის PIN-კოდი. თქვენ დაგრჩათ <xliff:g id="NUMBER_0">%d</xliff:g> მცდელობა, რომლის შემდეგაც მოწყობილობის განსაბლოკად დაგჭირდებათ თქვენს ოპერატორთან დაკავშირება.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM ბარათი ახლა დეაქტივირებულია. გასაგრძელებლად შეიყვანეთ PUK-კოდი. თქვენ დაგრჩათ <xliff:g id="_NUMBER_1">%d</xliff:g> მცდელობა, სანამ SIM სამუდამოდ გამოუსადეგარი გახდება. დეტალური ინფორმაციისთვის დაუკავშირდით თქვენს ოპერატორს.</item>
+ <item quantity="one">SIM ბარათი ახლა დეაქტივირებულია. გასაგრძელებლად შეიყვანეთ PUK-კოდი. თქვენ დაგრჩათ <xliff:g id="_NUMBER_0">%d</xliff:g> მცდელობა, სანამ SIM სამუდამოდ გამოუსადეგარი გახდება. დეტალური ინფორმაციისთვის დაუკავშირდით თქვენს ოპერატორს.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml
index 4b0a7ff..f9e12f1 100644
--- a/packages/SystemUI/res-keyguard/values-kk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Құрылғы құлпы <xliff:g id="NUMBER_0">%d</xliff:g> сағаттан бері ашылмаған. Құпия сөзді растаңыз.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Анықталмады"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">SIM PIN кодын енгізіңіз. <xliff:g id="NUMBER_1">%d</xliff:g> мүмкіндік қалды.</item>
+ <item quantity="one">SIM PIN кодын енгізіңіз. <xliff:g id="NUMBER_0">%d</xliff:g> мүмкіндік қалды, одан кейін оператордан SIM картасының құлпын ашуды сұрауға тура келеді.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM картасы өшірілді. Жалғастыру үшін PUK кодын енгізіңіз. <xliff:g id="_NUMBER_1">%d</xliff:g> мүмкіндік қалды, одан кейін SIM картасы біржола құлыпталады. Толығырақ мәліметті оператордан алыңыз.</item>
+ <item quantity="one">SIM картасы өшірілді. Жалғастыру үшін PUK кодын енгізіңіз. <xliff:g id="_NUMBER_0">%d</xliff:g> мүмкіндік қалды, одан кейін SIM картасы біржола құлыпталады. Толығырақ мәліметті оператордан алыңыз.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index 6a48197..3bf4379 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">ឧបករណ៍បានជាប់សោអស់រយៈពេល <xliff:g id="NUMBER_0">%d</xliff:g> ម៉ោងហើយ។ សូមបញ្ជាក់ពាក្យសម្ងាត់។</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"មិនអាចសម្គាល់បានទេ"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">បញ្ចូលកូដ PIN របស់ស៊ីម អ្នកនៅសល់ការព្យាយាម <xliff:g id="NUMBER_1">%d</xliff:g> ដងទៀត។</item>
+ <item quantity="one">បញ្ចូលកូដ PIN របស់ស៊ីម អ្នកនៅសល់ការព្យាយាម <xliff:g id="NUMBER_0">%d</xliff:g> ដងទៀត មុនពេលដែលអ្នកត្រូវទាក់ទងទៅក្រុមហ៊ុនសេវាទូរសព្ទរបស់អ្នកដើម្បីដោះសោឧបករណ៍របស់អ្នក។</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">ឥឡូវនេះស៊ីមត្រូវបានបិទ។ សូមបញ្ចូលកូដ PUK ដើម្បីបន្ត។ អ្នកនៅសល់ការព្យាយាម <xliff:g id="_NUMBER_1">%d</xliff:g> ដងទៀតមុនពេលស៊ីមមិនអាចប្រើបានជាអចិន្ត្រៃយ៍។ ទាក់ទងទៅក្រុមហ៊ុនសេវាទូរសព្ទសម្រាប់ព័ត៌មានលម្អិត។</item>
+ <item quantity="one">ឥឡូវនេះស៊ីមត្រូវបានបិទ។ សូមបញ្ចូលកូដ PUK ដើម្បីបន្ត។ អ្នកនៅសល់ការព្យាយាម <xliff:g id="_NUMBER_0">%d</xliff:g> ដងទៀតមុនពេលស៊ីមមិនអាចប្រើបានជាអចិន្ត្រៃយ៍។ ទាក់ទងទៅក្រុមហ៊ុនសេវាទូរសព្ទសម្រាប់ព័ត៌មានលម្អិត។</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml
index 093c5cb..2c29112 100644
--- a/packages/SystemUI/res-keyguard/values-kn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">ಸಾಧನವನ್ನು <xliff:g id="NUMBER_1">%d</xliff:g> ಗಂಟೆಗಳವರೆಗೆ ಅನ್ಲಾಕ್ ಮಾಡಿರಲಿಲ್ಲ. ಪಾಸ್ವರ್ಡ್ ಖಚಿತಪಡಿಸಿ.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">ಸಿಮ್ ಪಿನ್ ನಮೂದಿಸಿ, ನಿಮ್ಮಲ್ಲಿ <xliff:g id="NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.</item>
+ <item quantity="other">ಸಿಮ್ ಪಿನ್ ನಮೂದಿಸಿ, ನಿಮ್ಮಲ್ಲಿ <xliff:g id="NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">ಸಿಮ್ ಅನ್ನು ಈಗ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ. ಮುಂದುವರಿಸಲು PUK ಕೋಡ್ ನಮೂದಿಸಿ. ಸಿಮ್ ಶಾಶ್ವತವಾಗಿ ನಿಷ್ಪ್ರಯೋಜಕವಾಗುವ ಮುನ್ನ ನಿಮ್ಮಲ್ಲಿ <xliff:g id="_NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ. ವಿವರಗಳಿಗಾಗಿ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸಿ.</item>
+ <item quantity="other">ಸಿಮ್ ಅನ್ನು ಈಗ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ. ಮುಂದುವರಿಸಲು PUK ಕೋಡ್ ನಮೂದಿಸಿ. ಸಿಮ್ ಶಾಶ್ವತವಾಗಿ ನಿಷ್ಪ್ರಯೋಜಕವಾಗುವ ಮುನ್ನ ನಿಮ್ಮಲ್ಲಿ <xliff:g id="_NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ. ವಿವರಗಳಿಗಾಗಿ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸಿ.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml
index 9b48a4b..1a34229 100644
--- a/packages/SystemUI/res-keyguard/values-ko/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">기기가 <xliff:g id="NUMBER_0">%d</xliff:g>시간 동안 잠금 해제되지 않았습니다. 비밀번호를 입력하세요.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"인식할 수 없음"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">SIM PIN을 입력하세요. <xliff:g id="NUMBER_1">%d</xliff:g>번 더 시도할 수 있습니다.</item>
+ <item quantity="one">SIM PIN을 입력하세요. <xliff:g id="NUMBER_0">%d</xliff:g>번 더 실패하면 이동통신사에 문의하여 기기를 잠금 해제해야 합니다.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM이 사용 중지되었습니다. 계속하려면 PUK 코드를 입력하세요. <xliff:g id="_NUMBER_1">%d</xliff:g>번 더 실패하면 SIM을 완전히 사용할 수 없게 됩니다. 자세한 내용은 이동통신사에 문의하세요.</item>
+ <item quantity="one">SIM이 사용 중지되었습니다. 계속하려면 PUK 코드를 입력하세요. <xliff:g id="_NUMBER_0">%d</xliff:g>번 더 실패하면 SIM을 완전히 사용할 수 없게 됩니다. 자세한 내용은 이동통신사에 문의하세요.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml
index d4054e8..a4f5b7d 100644
--- a/packages/SystemUI/res-keyguard/values-ky/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Түзмөктүн кулпусу <xliff:g id="NUMBER_0">%d</xliff:g> саат бою ачылган жок. Сырсөздү ырастаңыз.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Таанылган жок"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">SIM-картанын PIN-кодун киргизиңиз, сизде <xliff:g id="NUMBER_1">%d</xliff:g> аракет калды.</item>
+ <item quantity="one">SIM-картанын PIN-кодун киргизиңиз, сизде <xliff:g id="NUMBER_0">%d</xliff:g> аракет калды. Эми түзмөктү бөгөттөн чыгаруу үчүн байланыш операторуңузга кайрылышыңыз керек.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM-карта азыр жарактан чыкты. Улантуу үчүн PUK-кодду киргизиңиз. SIM-картанын биротоло жарактан чыгарына <xliff:g id="_NUMBER_1">%d</xliff:g> аракет калды. Чоо-жайын билүү үчүн байланыш операторуна кайрылыңыз.</item>
+ <item quantity="one">SIM-карта азыр жарактан чыкты. Улантуу үчүн PUK-кодду киргизиңиз. SIM-картанын биротоло жарактан чыгаарына <xliff:g id="_NUMBER_0">%d</xliff:g> аракет калды. Чоо-жайын билүү үчүн байланыш операторуна кайрылыңыз.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml
index e72349b..545d32a 100644
--- a/packages/SystemUI/res-keyguard/values-lo/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">ອຸປະກອນບໍ່ໄດ້ຖືກປົດລັອກເປັນເວລາ <xliff:g id="NUMBER_0">%d</xliff:g> ຊົ່ວໂມງ. ຢືນຢັນລະຫັດຜ່ານ.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"ບໍ່ຮັບຮູ້"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">ລະຫັດ PIN ຂອງ SIM ບໍ່ຖືກຕ້ອງ, ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ເທື່ອ.</item>
+ <item quantity="one">ລະຫັດ PIN ຂອງ SIM ບໍ່ຖືກຕ້ອງ, ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອກ່ອນທີ່ທ່ານຈະຕ້ອງຕິດຕໍ່ຫາຜູ້ໃຫ້ບໍລິການຂອງທ່ານເພື່ອປົດລັອກອຸປະກອນທ່ານ.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">ຕອນນີ້ປິດການນຳໃຊ້ SIM ແລ້ວ. ໃສ່ລະຫັດ PUK ເພື່ອດຳເນີນການຕໍ່. ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="_NUMBER_1">%d</xliff:g> ເທື່ອກ່ອນທີ່ SIM ຈະບໍ່ສາມາດໃຊ້ໄດ້ຖາວອນ. ກະລຸນາຕິດຕໍ່ຜູ້ໃຫ້ບໍລິການສຳລັບລາຍລະອຽດ.</item>
+ <item quantity="one">ຕອນນີ້ປິດການນຳໃຊ້ SIM ແລ້ວ. ໃສ່ລະຫັດ PUK ເພື່ອດຳເນີນການຕໍ່. ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="_NUMBER_0">%d</xliff:g> ເທື່ອກ່ອນທີ່ SIM ຈະບໍ່ສາມາດໃຊ້ໄດ້ຖາວອນ. ກະລຸນາຕິດຕໍ່ຜູ້ໃຫ້ບໍລິການສຳລັບລາຍລະອຽດ.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml
index 0f51f00..bf041a0 100644
--- a/packages/SystemUI/res-keyguard/values-lt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml
@@ -147,4 +147,16 @@
<item quantity="other">Įrenginys nebuvo atrakintas <xliff:g id="NUMBER_1">%d</xliff:g> valandų. Patvirtinkite slaptažodį.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Neatpažinta"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Įveskite SIM kortelės PIN kodą. Jums liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymas.</item>
+ <item quantity="few">Įveskite SIM kortelės PIN kodą. Jums liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymai.</item>
+ <item quantity="many">Įveskite SIM kortelės PIN kodą. Jums liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymo.</item>
+ <item quantity="other">Įveskite SIM kortelės PIN kodą. Jums liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymų.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">SIM kortelė dabar yra išjungta. Jei norite tęsti, įveskite PUK kodą. Jums liko <xliff:g id="_NUMBER_1">%d</xliff:g> bandymas. Paskui visiškai nebegalėsite naudoti SIM kortelės. Jei reikia išsamios informacijos, susisiekite su operatoriumi.</item>
+ <item quantity="few">SIM kortelė dabar yra išjungta. Jei norite tęsti, įveskite PUK kodą. Jums liko <xliff:g id="_NUMBER_1">%d</xliff:g> bandymai. Paskui visiškai nebegalėsite naudoti SIM kortelės. Jei reikia išsamios informacijos, susisiekite su operatoriumi.</item>
+ <item quantity="many">SIM kortelė dabar yra išjungta. Jei norite tęsti, įveskite PUK kodą. Jums liko <xliff:g id="_NUMBER_1">%d</xliff:g> bandymo. Paskui visiškai nebegalėsite naudoti SIM kortelės. Jei reikia išsamios informacijos, susisiekite su operatoriumi.</item>
+ <item quantity="other">SIM kortelė dabar yra išjungta. Jei norite tęsti, įveskite PUK kodą. Jums liko <xliff:g id="_NUMBER_1">%d</xliff:g> bandymų. Paskui visiškai nebegalėsite naudoti SIM kortelės. Jei reikia išsamios informacijos, susisiekite su operatoriumi.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml
index bdd5dd5..475e9ce 100644
--- a/packages/SystemUI/res-keyguard/values-lv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml
@@ -141,4 +141,14 @@
<item quantity="other">Ierīce nav tikusi atbloķēta <xliff:g id="NUMBER_1">%d</xliff:g> stundas. Apstipriniet paroli.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Nav atpazīts"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="zero">Ievadiet SIM kartes PIN kodu. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizes.</item>
+ <item quantity="one">Ievadiet SIM kartes PIN kodu. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizi.</item>
+ <item quantity="other">Ievadiet SIM kartes PIN kodu. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizes.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="zero">SIM karte tagad ir atspējota. Ievadiet PUK kodu, lai turpinātu. Varat mēģināt vēl <xliff:g id="_NUMBER_1">%d</xliff:g> reizes. Kļūdas gadījumā SIM karti vairs nevarēs izmantot. Lai iegūtu detalizētu informāciju, sazinieties ar mobilo sakaru operatoru.</item>
+ <item quantity="one">SIM karte tagad ir atspējota. Ievadiet PUK kodu, lai turpinātu. Varat mēģināt vēl <xliff:g id="_NUMBER_1">%d</xliff:g> reizi. Kļūdas gadījumā SIM karti vairs nevarēs izmantot. Lai iegūtu detalizētu informāciju, sazinieties ar mobilo sakaru operatoru.</item>
+ <item quantity="other">SIM karte tagad ir atspējota. Ievadiet PUK kodu, lai turpinātu. Varat mēģināt vēl <xliff:g id="_NUMBER_1">%d</xliff:g> reizes. Kļūdas gadījumā SIM karti vairs nevarēs izmantot. Lai iegūtu detalizētu informāciju, sazinieties ar mobilo sakaru operatoru.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml
index e14989f..0951787 100644
--- a/packages/SystemUI/res-keyguard/values-mk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">Уредот не е отклучен веќе <xliff:g id="NUMBER_1">%d</xliff:g> часа. Потврдете ја лозинката.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Непознат"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Внесете PIN-код за SIM-картичката. Ви преостанува уште <xliff:g id="NUMBER_1">%d</xliff:g> обид.</item>
+ <item quantity="other">Внесете PIN-код за SIM-картичката. Ви преостануваат уште <xliff:g id="NUMBER_1">%d</xliff:g> обиди.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">SIM-картичката сега е оневозможена. Внесете PUK-код за да продолжите. Ви преостанува уште <xliff:g id="_NUMBER_1">%d</xliff:g> обид пред SIM-картичката да стане трајно неупотреблива. Контактирајте го операторот за детали.</item>
+ <item quantity="other">SIM-картичката сега е оневозможена. Внесете PUK-код за да продолжите. Ви преостануваат уште <xliff:g id="_NUMBER_1">%d</xliff:g> обиди пред SIM-картичката да стане трајно неупотреблива. Контактирајте го операторот за детали.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml
index 0b21231..d62537d 100644
--- a/packages/SystemUI/res-keyguard/values-ml/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">ഉപകരണം <xliff:g id="NUMBER_0">%d</xliff:g> മണിക്കൂറായി അൺലോക്ക് ചെയ്തിട്ടില്ല. പാസ്വേഡ് സ്ഥിരീകരിക്കുക.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"തിരിച്ചറിഞ്ഞില്ല"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">സിം പിൻ നൽകുക, <xliff:g id="NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി ശേഷിക്കുന്നു.</item>
+ <item quantity="one">സിം പിൻ നൽകുക, ഉപകരണം അൺലോക്ക് ചെയ്യാൻ കാരിയറുമായി ബന്ധപ്പെടേണ്ടിവരുന്നതിന് മുമ്പ് <xliff:g id="NUMBER_0">%d</xliff:g> ശ്രമം കൂടി ശേഷിക്കുന്നു.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">സിം ഇപ്പോൾ പ്രവർത്തനരഹിതമാക്കി. തുടരുന്നതിന് PUK കോഡ് നൽകുക. സിം ശാശ്വതമായി ഉപയോഗശൂന്യമാകുന്നതിന് മുമ്പായി <xliff:g id="_NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി ശേഷിക്കുന്നു. വിശദാംശങ്ങൾക്ക് കാരിയറുമായി ബന്ധപ്പെടുക.</item>
+ <item quantity="one">സിം ഇപ്പോൾ പ്രവർത്തനരഹിതമാക്കി. തുടരുന്നതിന് PUK കോഡ് നൽകുക. സിം ശാശ്വതമായി ഉപയോഗശൂന്യമാകുന്നതിന് മുമ്പായി <xliff:g id="_NUMBER_0">%d</xliff:g> ശ്രമം കൂടി ശേഷിക്കുന്നു. വിശദാംശങ്ങൾക്ക് കാരിയറുമായി ബന്ധപ്പെടുക.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml
index c8d0b2d..5f9b63d 100644
--- a/packages/SystemUI/res-keyguard/values-mn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Төхөөрөмжийн түгжээг <xliff:g id="NUMBER_0">%d</xliff:g> цагийн турш тайлаагүй байна. Нууц үгээ баталгаажуулна уу.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Танигдахгүй байна"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">SIM-н ПИН кодыг оруулна уу. Танд <xliff:g id="NUMBER_1">%d</xliff:g> оролдлого үлдлээ.</item>
+ <item quantity="one">SIM-н ПИН кодыг оруулна уу. Танд оператор компанитайгаа холбогдохгүйгээр төхөөрөмжийн түгжээг тайлах <xliff:g id="NUMBER_0">%d</xliff:g> оролдлого үлдлээ.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM-г идэвхгүй болголоо. Үргэлжлүүлэхийн тулд PUK кодыг оруулна уу. Таны SIM бүрмөсөн хүчингүй болох хүртэл <xliff:g id="_NUMBER_1">%d</xliff:g> оролдлого үлдлээ. Дэлгэрэнгүй мэдээлэл авахын тулд оператор компанитайгаа холбогдоно уу.</item>
+ <item quantity="one">SIM-г идэвхгүй болголоо. Үргэлжлүүлэхийн тулд PUK кодыг оруулна уу. Таны SIM бүрмөсөн хүчингүй болох хүртэл <xliff:g id="_NUMBER_0">%d</xliff:g> оролдлого үлдлээ. Дэлгэрэнгүй мэдээлэл авахын тулд оператор компанитайгаа холбогдоно уу.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml
index cfda6cc..7d04edc 100644
--- a/packages/SystemUI/res-keyguard/values-mr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">डिव्हाइस <xliff:g id="NUMBER_1">%d</xliff:g> तासांसाठी अनलॉक केले गेले नाही. पासवर्डची खात्री करा.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"ओळखले नाही"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">सिम पिन टाका, तुमच्याकडे <xliff:g id="NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहे.</item>
+ <item quantity="other">सिम पिन टाका, तुमच्याकडे <xliff:g id="NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहेत.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">सिम आता बंद केलेले आहे. सुरू ठेवण्यासाठी PUK कोड टाका. सिम कायमचे बंद होण्याआधी तुमच्याकडे <xliff:g id="_NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहे. तपशीलांसाठी वाहकाशी संपर्क साधा.</item>
+ <item quantity="other">सिम आता बंद केलेले आहे. सुरू ठेवण्यासाठी PUK कोड टाका. सिम कायमचे बंद होण्याआधी तुमच्याकडे <xliff:g id="_NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहेत. तपशीलांसाठी वाहकाशी संपर्क साधा.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml
index 6b8c1e9..d37e7ee 100644
--- a/packages/SystemUI/res-keyguard/values-ms/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Peranti tidak dibuka kuncinya selama <xliff:g id="NUMBER_0">%d</xliff:g> jam. Sahkan kata laluan.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Tidak dikenali"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Masukkan PIN SIM, tinggal <xliff:g id="NUMBER_1">%d</xliff:g> percubaan lagi.</item>
+ <item quantity="one">Masukkan PIN SIM, tinggal <xliff:g id="NUMBER_0">%d</xliff:g> percubaan lagi sebelum anda harus menghubungi pembawa anda untuk membuka kunci peranti.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">Kini SIM dilumpuhkan. Masukkan kod PUK untuk meneruskan. Tinggal <xliff:g id="_NUMBER_1">%d</xliff:g> percubaan sebelum SIM tidak boleh digunakan secara kekal. Hubungi pembawa untuk mendapatkan butiran.</item>
+ <item quantity="one">Kini SIM dilumpuhkan. Masukkan kod PUK untuk meneruskan. Tinggal <xliff:g id="_NUMBER_0">%d</xliff:g> percubaan sebelum SIM tidak boleh digunakan secara kekal. Hubungi pembawa untuk mendapatkan butiran.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml
index 46e8c2e..6534fbb 100644
--- a/packages/SystemUI/res-keyguard/values-my/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-my/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">စက်ပစ္စည်းကို <xliff:g id="NUMBER_0">%d</xliff:g> နာရီကြာ လော့ခ်ဖွင့်ခဲ့ခြင်း မရှိပါ။ စကားဝှက်အား အတည်ပြုပါ။</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"မသိပါ"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">ဆင်းမ်ကဒ် ပင်နံပါတ် ထည့်သွင်းပါ၊ သင့်တွင် <xliff:g id="NUMBER_1">%d</xliff:g> ခါ ကြိုးစားခွင့် ကျန်ပါသေးသည်။</item>
+ <item quantity="one">ဆင်းမ်ကဒ် ပင်နံပါတ် ထည့်သွင်းပါ၊ သင့်စက်ကို ဖွင့်ရန် ဝန်ဆောင်မှုပေးသူသို့ မဆက်သွယ်မီ သင့်တွင် <xliff:g id="NUMBER_0">%d</xliff:g> ခါ ကြိုးစားခွင့် ကျန်ပါသေးသည်။</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">ဆင်းမ်ကဒ်သည် ယခု ပိတ်သွားပါပြီ။ ရှေ့ဆက်ရန် ပင်နံပါတ် ပြန်ဖွင့်သည့် ကုဒ်ကို ထည့်ပါ။ ဆင်းမ်ကဒ်ကို အပြီးသုံးမရအောင်မပြုမီ သင့်တွင် <xliff:g id="_NUMBER_1">%d</xliff:g> ခါ ကြိုးစားခွင့်ကျန်ပါသေးသည်။ အသေးစိတ်အချက်များအတွက် ဝန်ဆောင်မှုပေးသူကို ဆက်သွယ်ပါ။</item>
+ <item quantity="one">ဆင်းမ်ကဒ်သည် ယခု ပိတ်သွားပါပြီ။ ရှေ့ဆက်ရန် ပင်နံပါတ် ပြန်ဖွင့်သည့် ကုဒ်ကို ထည့်ပါ။ ဆင်းမ်ကဒ်ကို အပြီးသုံးမရအောင်မပြုမီ သင့်တွင် <xliff:g id="_NUMBER_0">%d</xliff:g> ခါ ကြိုးစားခွင့်ကျန်ပါသေးသည်။ အသေးစိတ်အချက်များအတွက် ဝန်ဆောင်မှုပေးသူကို ဆက်သွယ်ပါ။</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml
index b206395..ca51c96 100644
--- a/packages/SystemUI/res-keyguard/values-nb/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Enheten har ikke blitt låst opp på <xliff:g id="NUMBER_0">%d</xliff:g> time. Bekreft passordet.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Ikke gjenkjent"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Skriv inn PIN-koden for SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøk igjen.</item>
+ <item quantity="one">Skriv inn PIN-koden for SIM-kortet. Du har <xliff:g id="NUMBER_0">%d</xliff:g> forsøk igjen før du må kontakte operatøren din for å låse opp enheten.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM-kortet er deaktivert nå. Skriv inn PUK-koden for å fortsette. Du har <xliff:g id="_NUMBER_1">%d</xliff:g> forsøk igjen før SIM-kortet blir permanent ubrukelig. Kontakt operatøren for å få vite mer.</item>
+ <item quantity="one">SIM-kortet er deaktivert nå. Skriv inn PUK-koden for å fortsette. Du har <xliff:g id="_NUMBER_0">%d</xliff:g> forsøk igjen før SIM-kortet blir permanent ubrukelig. Kontakt operatøren for å få vite mer.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index d5ac1d8..90d6c5cfc 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">यन्त्र <xliff:g id="NUMBER_0">%d</xliff:g> घन्टा देखि अनलक भएको छैन। पासवर्ड पुष्टि गर्नुहोस्।</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"पहिचान भएन"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">SIM को PIN प्रविष्ट गर्नुहोस् तपाईंसँग <xliff:g id="NUMBER_1">%d</xliff:g> प्रयासहरू बाँकी छन्।</item>
+ <item quantity="one">SIM को PIN प्रविष्ट गर्नुहोस्, तपाईंसँग <xliff:g id="NUMBER_0">%d</xliff:g> प्रयास बाँकी छ, त्यसपछि भने आफ्नो यन्त्र अनलक गर्नका लागि तपाईंले अनिवार्य रूपमा आफ्नो सेवा प्रदायकलाई सम्पर्क गर्नपर्ने हुन्छ।</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM लाई असक्षम पारिएको छ। जारी राख्न PUK कोड प्रविष्ट गर्नुहोस्। तपाईंसँग <xliff:g id="_NUMBER_1">%d</xliff:g> प्रयासहरू बाँकी छन्, त्यसपछि SIM सदाका लागि प्रयोग गर्न नमिल्ने हुन्छ। विवरणहरूका लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।</item>
+ <item quantity="one">SIM लाई असक्षम पारिएको छ। जारी राख्न PUK कोड प्रविष्ट गर्नुहोस्। तपाईंसँग <xliff:g id="_NUMBER_0">%d</xliff:g> प्रयास बाँकी छ, त्यसपछि SIM सदाका लागि प्रयोग गर्न नमिल्ने हुन्छ। विवरणहरूका लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml
index 6a1f3c9..85e0a95 100644
--- a/packages/SystemUI/res-keyguard/values-nl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Apparaat is al <xliff:g id="NUMBER_0">%d</xliff:g> uur niet ontgrendeld. Bevestig het wachtwoord.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Niet herkend"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Geef de pincode van de simkaart op. Je hebt nog <xliff:g id="NUMBER_1">%d</xliff:g> pogingen over.</item>
+ <item quantity="one">Geef de pincode van de simkaart op. Je hebt nog <xliff:g id="NUMBER_0">%d</xliff:g> poging over voordat je contact met je provider moet opnemen om het apparaat te ontgrendelen.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">De simkaart is nu uitgeschakeld. Geef de pukcode op om door te gaan. Je hebt nog <xliff:g id="_NUMBER_1">%d</xliff:g> pogingen over voordat de simkaart definitief onbruikbaar wordt. Neem contact op met je provider voor meer informatie.</item>
+ <item quantity="one">De simkaart is nu uitgeschakeld. Geef de pukcode op om door te gaan. Je hebt nog <xliff:g id="_NUMBER_0">%d</xliff:g> poging over voordat de simkaart definitief onbruikbaar wordt. Neem contact op met je provider voor meer informatie.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index 0bab97d..d5d27ca 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">ਡੀਵਾਈਸ <xliff:g id="NUMBER_1">%d</xliff:g> ਘੰਟਿਆਂ ਤੋਂ ਅਣਲਾਕ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ। ਪਾਸਵਰਡ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ, ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ।</item>
+ <item quantity="other">ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ, ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ।</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">ਸਿਮ ਹੁਣ ਬੰਦ ਹੋ ਗਿਆ ਹੈ। ਜਾਰੀ ਰੱਖਣ ਲਈ PUK ਕੋਡ ਦਾਖਲ ਕਰੋ। ਸਿਮ ਦੇ ਪੱਕੇ ਤੌਰ \'ਤੇ ਬੇਕਾਰ ਹੋ ਜਾਣ ਤੋਂ ਪਹਿਲਾਂ ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="_NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ। ਵੇਰਵਿਆਂ ਲਈ ਕੈਰੀਅਰ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।</item>
+ <item quantity="other">ਸਿਮ ਹੁਣ ਬੰਦ ਹੋ ਗਿਆ ਹੈ। ਜਾਰੀ ਰੱਖਣ ਲਈ PUK ਕੋਡ ਦਾਖਲ ਕਰੋ। ਸਿਮ ਦੇ ਪੱਕੇ ਤੌਰ \'ਤੇ ਬੇਕਾਰ ਹੋ ਜਾਣ ਤੋਂ ਪਹਿਲਾਂ ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="_NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ। ਵੇਰਵਿਆਂ ਲਈ ਕੈਰੀਅਰ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index 59f4412..ee6ad62 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -147,4 +147,16 @@
<item quantity="one">Urządzenie nie zostało odblokowane od <xliff:g id="NUMBER_0">%d</xliff:g> godziny. Potwierdź hasło.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Nie rozpoznano"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="few">Wpisz kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> próby.</item>
+ <item quantity="many">Wpisz kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> prób.</item>
+ <item quantity="other">Wpisz kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> próby.</item>
+ <item quantity="one">Wpisz kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_0">%d</xliff:g> próbę, zanim będzie trzeba skontaktować się z operatorem, by odblokować to urządzenie.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="few">Karta SIM została wyłączona. Wpisz kod PUK, by przejść dalej. Masz jeszcze <xliff:g id="_NUMBER_1">%d</xliff:g> próby, zanim karta SIM zostanie trwale zablokowana. Aby uzyskać szczegółowe informacje, skontaktuj się z operatorem.</item>
+ <item quantity="many">Karta SIM została wyłączona. Wpisz kod PUK, by przejść dalej. Masz jeszcze <xliff:g id="_NUMBER_1">%d</xliff:g> prób, zanim karta SIM zostanie trwale zablokowana. Aby uzyskać szczegółowe informacje, skontaktuj się z operatorem.</item>
+ <item quantity="other">Karta SIM została wyłączona. Wpisz kod PUK, by przejść dalej. Masz jeszcze <xliff:g id="_NUMBER_1">%d</xliff:g> próby, zanim karta SIM zostanie trwale zablokowana. Aby uzyskać szczegółowe informacje, skontaktuj się z operatorem.</item>
+ <item quantity="one">Karta SIM została wyłączona. Wpisz kod PUK, by przejść dalej. Masz jeszcze <xliff:g id="_NUMBER_0">%d</xliff:g> próbę, zanim karta SIM zostanie trwale zablokowana. Aby uzyskać szczegółowe informacje, skontaktuj się z operatorem.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
index 362ffcf..bb547f15 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirme a senha.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Não reconhecido"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Informe o PIN do SIM. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativa restante.</item>
+ <item quantity="other">Informe o PIN do SIM. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativas restantes.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">O SIM agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativa restante antes de o SIM se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item>
+ <item quantity="other">O SIM agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativas restantes antes de o SIM se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
index 70d13e6..6a0e627 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirme a palavra-passe.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Não reconhecido"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Introduza o PIN do cartão SIM. Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas.</item>
+ <item quantity="one">Introduza o PIN do cartão SIM. Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de ser necessário contactar o operador para desbloquear o dispositivo.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">O SIM encontra-se desativado. Introduza o código PUK para continuar. Tem mais <xliff:g id="_NUMBER_1">%d</xliff:g> tentativas antes de o cartão SIM ficar permanentemente inutilizável. Contacte o operador para obter mais detalhes.</item>
+ <item quantity="one">O SIM encontra-se desativado. Introduza o código PUK para continuar. Tem mais <xliff:g id="_NUMBER_0">%d</xliff:g> tentativa antes de o cartão SIM ficar permanentemente inutilizável. Contacte o operador para obter mais detalhes.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml
index 362ffcf..bb547f15 100644
--- a/packages/SystemUI/res-keyguard/values-pt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirme a senha.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Não reconhecido"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Informe o PIN do SIM. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativa restante.</item>
+ <item quantity="other">Informe o PIN do SIM. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativas restantes.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">O SIM agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativa restante antes de o SIM se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item>
+ <item quantity="other">O SIM agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativas restantes antes de o SIM se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index d0dc17e..341c952 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -141,4 +141,14 @@
<item quantity="one">Dispozitivul nu a fost deblocat de <xliff:g id="NUMBER_0">%d</xliff:g> oră. Confirmați parola.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Nu este recunoscută"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="few">Introduceți codul PIN pentru cardul SIM. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> încercări.</item>
+ <item quantity="other">Introduceți codul PIN pentru cardul SIM. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> de încercări.</item>
+ <item quantity="one">Introduceți codul PIN pentru cardul SIM. V-a mai rămas <xliff:g id="NUMBER_0">%d</xliff:g> încercare, după care va trebui să contactați operatorul pentru a vă debloca dispozitivul.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="few">Cardul SIM este dezactivat acum. Introduceți codul PUK pentru a continua. V-au mai rămas <xliff:g id="_NUMBER_1">%d</xliff:g> încercări până când cardul SIM va deveni inutilizabil definitiv. Contactați operatorul pentru detalii.</item>
+ <item quantity="other">Cardul SIM este dezactivat acum. Introduceți codul PUK pentru a continua. V-au mai rămas <xliff:g id="_NUMBER_1">%d</xliff:g> de încercări până când cardul SIM va deveni inutilizabil definitiv. Contactați operatorul pentru detalii.</item>
+ <item quantity="one">Cardul SIM este dezactivat acum. Introduceți codul PUK pentru a continua. V-a mai rămas <xliff:g id="_NUMBER_0">%d</xliff:g> încercare până când cardul SIM va deveni inutilizabil definitiv. Contactați operatorul pentru detalii.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml
index f629957..1a794ee 100644
--- a/packages/SystemUI/res-keyguard/values-ru/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml
@@ -147,4 +147,16 @@
<item quantity="other">Устройство не разблокировалось в течение <xliff:g id="NUMBER_1">%d</xliff:g> часа. Введите пароль ещё раз.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Не распознано"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Неверный PIN-код. Осталась <xliff:g id="NUMBER_1">%d</xliff:g> попытка.</item>
+ <item quantity="few">Неверный PIN-код. Осталось <xliff:g id="NUMBER_1">%d</xliff:g> попытки.</item>
+ <item quantity="many">Неверный PIN-код. Осталось <xliff:g id="NUMBER_1">%d</xliff:g> попыток.</item>
+ <item quantity="other">Неверный PIN-код. Осталось <xliff:g id="NUMBER_1">%d</xliff:g> попытки.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">SIM-карта отключена. Чтобы продолжить, введите PUK-код. Осталась <xliff:g id="_NUMBER_1">%d</xliff:g> попытка. После этого SIM-карта будет заблокирована навсегда. За подробной информацией обратитесь к оператору связи.</item>
+ <item quantity="few">SIM-карта отключена. Чтобы продолжить, введите PUK-код. Осталось <xliff:g id="_NUMBER_1">%d</xliff:g> попытки. После этого SIM-карта будет заблокирована навсегда. За подробной информацией обратитесь к оператору связи.</item>
+ <item quantity="many">SIM-карта отключена. Чтобы продолжить, введите PUK-код. Осталось <xliff:g id="_NUMBER_1">%d</xliff:g> попыток. После этого SIM-карта будет заблокирована навсегда. За подробной информацией обратитесь к оператору связи.</item>
+ <item quantity="other">SIM-карта отключена. Чтобы продолжить, введите PUK-код. Осталось <xliff:g id="_NUMBER_1">%d</xliff:g> попытки. После этого SIM-карта будет заблокирована навсегда. За подробной информацией обратитесь к оператору связи.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml
index 4981855..d36e752 100644
--- a/packages/SystemUI/res-keyguard/values-si/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-si/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">උපාංගය පැය <xliff:g id="NUMBER_1">%d</xliff:g>ක් අගුලු හැර නැත. මුරපදය තහවුරු කරන්න.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"හඳුනා නොගන්නා ලදී"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">SIM PIN ඇතුළු කරන්න, ඔබ සතුව උත්සාහයන් <xliff:g id="NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත.</item>
+ <item quantity="other">SIM PIN ඇතුළු කරන්න, ඔබ සතුව උත්සාහයන් <xliff:g id="NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">SIM දැන් අබල කර ඇත. දිගටම කරගෙන යාමට PUK කේතය ඇතුළු කරන්න. SIM ස්ථිරවම භාවිත කළ නොහැකි බවට පත් වීමට පෙර ඔබ සතුව උත්සාහයන් <xliff:g id="_NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත. විස්තර සඳහා වාහක සම්බන්ධ කර ගන්න.</item>
+ <item quantity="other">SIM දැන් අබල කර ඇත. දිගටම කරගෙන යාමට PUK කේතය ඇතුළු කරන්න. SIM ස්ථිරවම භාවිත කළ නොහැකි බවට පත් වීමට පෙර ඔබ සතුව උත්සාහයන් <xliff:g id="_NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත. විස්තර සඳහා වාහක සම්බන්ධ කර ගන්න.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml
index 99007ee..b69f502 100644
--- a/packages/SystemUI/res-keyguard/values-sk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml
@@ -147,4 +147,16 @@
<item quantity="one">Zariadenie nebolo odomknuté <xliff:g id="NUMBER_0">%d</xliff:g> hodinu. Potvrďte heslo.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Nerozpoznané"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="few">Zadajte kód PIN SIM karty. Zostávajú vám <xliff:g id="NUMBER_1">%d</xliff:g> pokusy.</item>
+ <item quantity="many">Zadajte kód PIN SIM karty. Zostáva vám <xliff:g id="NUMBER_1">%d</xliff:g> pokusu.</item>
+ <item quantity="other">Zadajte kód PIN SIM karty. Zostáva vám <xliff:g id="NUMBER_1">%d</xliff:g> pokusov.</item>
+ <item quantity="one">Zadajte kód PIN SIM karty. Zostáva vám <xliff:g id="NUMBER_0">%d</xliff:g> pokus, potom budete musieť kontaktovať svojho operátora, aby odomkol zariadenie.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="few">SIM karta je deaktivovaná. Pokračujte zadaním kódu PUK. Zostávajú vám <xliff:g id="_NUMBER_1">%d</xliff:g> pokusy, potom sa SIM karta natrvalo zablokuje. Podrobnosti vám poskytne operátor.</item>
+ <item quantity="many">SIM karta je deaktivovaná. Pokračujte zadaním kódu PUK. Zostáva vám <xliff:g id="_NUMBER_1">%d</xliff:g> pokusu, potom sa SIM karta natrvalo zablokuje. Podrobnosti vám poskytne operátor.</item>
+ <item quantity="other">SIM karta je deaktivovaná. Pokračujte zadaním kódu PUK. Zostáva vám <xliff:g id="_NUMBER_1">%d</xliff:g> pokusov, potom sa SIM karta natrvalo zablokuje. Podrobnosti vám poskytne operátor.</item>
+ <item quantity="one">SIM karta je deaktivovaná. Pokračujte zadaním kódu PUK. Zostáva vám <xliff:g id="_NUMBER_0">%d</xliff:g> pokus, potom sa SIM karta natrvalo zablokuje. Podrobnosti vám poskytne operátor.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml
index 2a08765..cd53c6c 100644
--- a/packages/SystemUI/res-keyguard/values-sl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml
@@ -147,4 +147,16 @@
<item quantity="other">Naprava ni bila odklenjena <xliff:g id="NUMBER_1">%d</xliff:g> ur. Potrdite geslo.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Ni prepoznano"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Vnesite kodo PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskus.</item>
+ <item quantity="two">Vnesite kodo PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskusa.</item>
+ <item quantity="few">Vnesite kodo PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskuse.</item>
+ <item quantity="other">Vnesite kodo PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskusov.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">Kartica SIM je zdaj onemogočena. Če želite nadaljevati, vnesite kodo PUK. Na voljo imate še <xliff:g id="_NUMBER_1">%d</xliff:g> poskus. Potem bo kartica SIM postala trajno neuporabna. Za podrobnosti se obrnite na operaterja.</item>
+ <item quantity="two">Kartica SIM je zdaj onemogočena. Če želite nadaljevati, vnesite kodo PUK. Na voljo imate še <xliff:g id="_NUMBER_1">%d</xliff:g> poskusa. Potem bo kartica SIM postala trajno neuporabna. Za podrobnosti se obrnite na operaterja.</item>
+ <item quantity="few">Kartica SIM je zdaj onemogočena. Če želite nadaljevati, vnesite kodo PUK. Na voljo imate še <xliff:g id="_NUMBER_1">%d</xliff:g> poskuse. Potem bo kartica SIM postala trajno neuporabna. Za podrobnosti se obrnite na operaterja.</item>
+ <item quantity="other">Kartica SIM je zdaj onemogočena. Če želite nadaljevati, vnesite kodo PUK. Na voljo imate še <xliff:g id="_NUMBER_1">%d</xliff:g> poskusov. Potem bo kartica SIM postala trajno neuporabna. Za podrobnosti se obrnite na operaterja.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml
index fd52d39..0a55795 100644
--- a/packages/SystemUI/res-keyguard/values-sq/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Pajisja nuk është shkyçur për <xliff:g id="NUMBER_0">%d</xliff:g> orë. Konfirmo fjalëkalimin.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Nuk njihet"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Fut kodin PIN të kartës SIM, të kanë mbetur edhe <xliff:g id="NUMBER_1">%d</xliff:g> tentativa.</item>
+ <item quantity="one">Fut kodin PIN të kartës SIM, të ka mbetur edhe <xliff:g id="NUMBER_0">%d</xliff:g> tentativë para se të kontaktosh me operatorin tënd celular për ta shkyçur pajisjen.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">Karta SIM tani është çaktivizuar. Fut kodin PUK për të vazhduar. Të kanë mbetur edhe <xliff:g id="_NUMBER_1">%d</xliff:g> përpjekje përpara se karta SIM të bëhet përgjithmonë e papërdorshme. Kontakto me operatorin për detaje.</item>
+ <item quantity="one">Karta SIM tani është çaktivizuar. Fut kodin PUK për të vazhduar. Të ka mbetur edhe <xliff:g id="_NUMBER_0">%d</xliff:g> përpjekje përpara se karta SIM të bëhet përgjithmonë e papërdorshme. Kontakto me operatorin për detaje.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml
index 9d06e00..08db0bd1 100644
--- a/packages/SystemUI/res-keyguard/values-sr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml
@@ -141,4 +141,14 @@
<item quantity="other">Нисте откључали уређај <xliff:g id="NUMBER_1">%d</xliff:g> сати. Потврдите лозинку.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Није препознат"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Унесите PIN за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушај.</item>
+ <item quantity="few">Унесите PIN за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушаја.</item>
+ <item quantity="other">Унесите PIN за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушаја.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">SIM је сада онемогућен. Унесите PUK кôд да бисте наставили. Имате још <xliff:g id="_NUMBER_1">%d</xliff:g> покушај пре него што SIM постане трајно неупотребљив. Детаљне информације потражите од мобилног оператера.</item>
+ <item quantity="few">SIM је сада онемогућен. Унесите PUK кôд да бисте наставили. Имате још <xliff:g id="_NUMBER_1">%d</xliff:g> покушаја пре него што SIM постане трајно неупотребљив. Детаљне информације потражите од мобилног оператера.</item>
+ <item quantity="other">SIM је сада онемогућен. Унесите PUK кôд да бисте наставили. Имате још <xliff:g id="_NUMBER_1">%d</xliff:g> покушаја пре него што SIM постане трајно неупотребљив. Детаљне информације потражите од мобилног оператера.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml
index dc41e0d..54a1d0d 100644
--- a/packages/SystemUI/res-keyguard/values-sv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Enheten har inte låsts upp på <xliff:g id="NUMBER_0">%d</xliff:g> timme. Bekräfta lösenordet.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Identifierades inte"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Ange pinkod för SIM-kortet. <xliff:g id="NUMBER_1">%d</xliff:g> försök återstår.</item>
+ <item quantity="one">Ange pinkod för SIM-kortet. <xliff:g id="NUMBER_0">%d</xliff:g> försök återstår innan du måste kontakta operatören för att låsa upp enheten.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM-kortet är inaktiverat. Ange PUK-koden om du vill fortsätta. <xliff:g id="_NUMBER_1">%d</xliff:g> försök återstår innan SIM-kortet blir obrukbart. Kontakta operatören för mer information.</item>
+ <item quantity="one">SIM-kortet är inaktiverat. Ange PUK-koden om du vill fortsätta. <xliff:g id="_NUMBER_0">%d</xliff:g> försök återstår innan SIM-kortet blir obrukbart. Kontakta operatören för mer information.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml
index ff75819..7e85131 100644
--- a/packages/SystemUI/res-keyguard/values-sw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Hujafungua kifaa kwa saa <xliff:g id="NUMBER_0">%d</xliff:g>. Thibitisha nenosiri.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Haikutambua alama ya kidole"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Weka PIN ya SIM, umesalia na majaribio <xliff:g id="NUMBER_1">%d</xliff:g>.</item>
+ <item quantity="one">PIN ya SIM uliyoweka si sahihi. Umesalia na jaribio <xliff:g id="NUMBER_0">%d</xliff:g> kabla ya kulazimika kuwasiliana na mtoa huduma wako ili afungue kifaa chako.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">Sasa SIM imefungwa. Weka msimbo wa PUK ili uendelee. Umesalia na majaribio <xliff:g id="_NUMBER_1">%d</xliff:g> kabla ya SIM kuacha kufanya kazi kabisa. Wasiliana na mtoa huduma kwa maelezo.</item>
+ <item quantity="one">Sasa SIM imefungwa. Weka msimbo wa PUK ili uendelee. Umesalia na jaribio <xliff:g id="_NUMBER_0">%d</xliff:g> kabla ya SIM kuacha kufanya kazi kabisa. Wasiliana na mtoa huduma kwa maelezo.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml
index 0ba941c..2ce57a0 100644
--- a/packages/SystemUI/res-keyguard/values-ta/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one"><xliff:g id="NUMBER_0">%d</xliff:g> மணிநேரமாகச் சாதனம் திறக்கப்படவில்லை. கடவுச்சொல்லை உறுதிப்படுத்தவும்.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"அடையாளங்காண முடியவில்லை"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">சிம் பின்னை உள்ளிடவும், மேலும் <xliff:g id="NUMBER_1">%d</xliff:g> முறை முயற்சிக்கலாம்.</item>
+ <item quantity="one">சிம் பின்னை உள்ளிடவும், நீங்கள் <xliff:g id="NUMBER_0">%d</xliff:g> முறை மட்டுமே முயற்சிக்க முடியுமென்பதால், அதற்கு முன்பு மொபைல் நிறுவனத்தைத் தொடர்பு கொண்டு சாதனத்தைத் திறக்க முயலவும்.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">சிம் தற்போது முடக்கப்பட்டுள்ளது. தொடர்வதற்கு, PUK குறியீட்டை உள்ளிடவும். நீங்கள் <xliff:g id="_NUMBER_1">%d</xliff:g> முறை மட்டுமே முயற்சிக்க முடியும். அதன்பிறகு சிம் நிரந்தரமாக முடக்கப்படும். விவரங்களுக்கு, மொபைல் நிறுவனத்தைத் தொடர்புகொள்ளவும்.</item>
+ <item quantity="one">சிம் தற்போது முடக்கப்பட்டுள்ளது. தொடர்வதற்கு, PUK குறியீட்டை உள்ளிடவும். நீங்கள் <xliff:g id="_NUMBER_0">%d</xliff:g> முறை மட்டுமே முயற்சிக்க முடியும். அதன்பிறகு சிம் நிரந்தரமாக முடக்கப்படும். விவரங்களுக்கு, மொபைல் நிறுவனத்தைத் தொடர்புகொள்ளவும்.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index 73f7094..bdee4a3 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one"><xliff:g id="NUMBER_0">%d</xliff:g> గంట పాటు పరికరాన్ని అన్లాక్ చేయలేదు. పాస్వర్డ్ని నమోదు చేయండి.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"గుర్తించలేదు"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">SIM పిన్ని నమోదు చేయండి, మీకు <xliff:g id="NUMBER_1">%d</xliff:g> ప్రయత్నలు మిగిలి ఉన్నాయి.</item>
+ <item quantity="one">SIM పిన్ని నమోదు చేయండి, మీరు మీ పరికరాన్ని అన్లాక్ చేయడానికి తప్పనిసరిగా మీ క్యారియర్ను సంప్రదించడానికి ముందు మీకు <xliff:g id="NUMBER_0">%d</xliff:g> ప్రయత్నం మిగిలి ఉంది.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM ఇప్పుడు నిలిపివేయబడింది. PUK కోడ్ను నమోదు చేయండి. SIM శాశ్వతంగా నిరుపయోగం కాకుండా ఉండటానికి మీకు <xliff:g id="_NUMBER_1">%d</xliff:g> ప్రయత్నాలు మిగిలి ఉన్నాయి. వివరాల కోసం కారియర్ను సంప్రదించండి.</item>
+ <item quantity="one">SIM ఇప్పుడు నిలిపివేయబడింది. PUK కోడ్ను నమోదు చేయండి. SIM శాశ్వతంగా నిరుపయోగం కాకుండా ఉండటానికి మీకు <xliff:g id="_NUMBER_0">%d</xliff:g> ప్రయత్నం మిగిలి ఉంది వివరాల కోసం కారియర్ను సంప్రదించండి.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml
index d802f01..aa33421 100644
--- a/packages/SystemUI/res-keyguard/values-th/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-th/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">ไม่มีการปลดล็อกอุปกรณ์มา <xliff:g id="NUMBER_0">%d</xliff:g> ชั่วโมงแล้ว ยืนยันรหัสผ่าน</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"ไม่รู้จัก"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">ป้อน PIN ของซิม คุณพยายามได้อีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง</item>
+ <item quantity="one">PIN ของซิมไม่ถูกต้อง คุณพยายามได้อีก <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งก่อนที่จะต้องติดต่อผู้ให้บริการเพื่อปลดล็อกอุปกรณ์</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">ซิมถูกปิดใช้งานในขณะนี้ โปรดป้อนรหัส PUK เพื่อทำต่อ คุณพยายามได้อีก <xliff:g id="_NUMBER_1">%d</xliff:g> ครั้งก่อนที่ซิมจะไม่สามารถใช้งานได้อย่างถาวร โปรดติดต่อสอบถามรายละเอียดจากผู้ให้บริการ</item>
+ <item quantity="one">ซิมถูกปิดใช้งานในขณะนี้ โปรดป้อนรหัส PUK เพื่อทำต่อ คุณพยายามได้อีก <xliff:g id="_NUMBER_0">%d</xliff:g> ครั้งก่อนที่ซิมจะไม่สามารถใช้งานได้อย่างถาวร โปรดติดต่อสอบถามรายละเอียดจากผู้ให้บริการ</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml
index 7e84d2f..30657d4 100644
--- a/packages/SystemUI/res-keyguard/values-tl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">Hindi na-unlock ang device sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> na oras. Kumpirmahin ang password.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Hindi nakilala"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Ilagay ang PIN ng SIM, mayroon kang <xliff:g id="NUMBER_1">%d</xliff:g> natitirang pagsubok.</item>
+ <item quantity="other">Ilagay ang PIN ng SIM, mayroon kang <xliff:g id="NUMBER_1">%d</xliff:g> na natitirang pagsubok.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">Naka-disable na ang SIM. Ilagay ang PUK code upang magpatuloy. Mayroon kang <xliff:g id="_NUMBER_1">%d</xliff:g> natitirang pagsubok bago tuluyang hindi magamit ang SIM. Makipag-ugnayan sa carrier para sa mga detalye.</item>
+ <item quantity="other">Naka-disable na ang SIM. Ilagay ang PUK code upang magpatuloy. Mayroon kang <xliff:g id="_NUMBER_1">%d</xliff:g> na natitirang pagsubok bago tuluyang hindi magamit ang SIM. Makipag-ugnayan sa carrier para sa mga detalye.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml
index b80e4db..ca53dfb8 100644
--- a/packages/SystemUI/res-keyguard/values-tr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Cihazın kilidi son <xliff:g id="NUMBER_0">%d</xliff:g> saattir açılmadı. Şifreyi doğrulayın.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Tanınmadı"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">SIM PIN\'inizi girin. <xliff:g id="NUMBER_1">%d</xliff:g> deneme hakkınız kaldı.</item>
+ <item quantity="one">SIM PIN\'inizi girin. Cihazınızın kilidini açmak için operatörünüzle bağlantı kurmak zorunda kalmadan önce <xliff:g id="NUMBER_0">%d</xliff:g> deneme hakkınız kaldı.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM artık devre dışı. Devam etmek için PUK kodunu girin. SIM kalıcı olarak kullanım dışı kalmadan önce <xliff:g id="_NUMBER_1">%d</xliff:g> deneme hakkınız kaldı. Ayrıntılı bilgi için operatörünüzle iletişim kurun.</item>
+ <item quantity="one">SIM artık devre dışı. Devam etmek için PUK kodunu girin. SIM kalıcı olarak kullanım dışı kalmadan önce <xliff:g id="_NUMBER_0">%d</xliff:g> deneme hakkınız kaldı. Ayrıntılı bilgi için operatörünüzle iletişim kurun.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml
index f25928a..4b79744 100644
--- a/packages/SystemUI/res-keyguard/values-uk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml
@@ -147,4 +147,16 @@
<item quantity="other">Ви не розблоковували пристрій <xliff:g id="NUMBER_1">%d</xliff:g> години. Підтвердьте пароль.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Не розпізнано"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Введіть PIN-код SIM-карти. Залишилася <xliff:g id="NUMBER_1">%d</xliff:g> спроба.</item>
+ <item quantity="few">Введіть PIN-код SIM-карти. Залишилося <xliff:g id="NUMBER_1">%d</xliff:g> спроби.</item>
+ <item quantity="many">Введіть PIN-код SIM-карти. Залишилося <xliff:g id="NUMBER_1">%d</xliff:g> спроб.</item>
+ <item quantity="other">Введіть PIN-код SIM-карти. Залишилося <xliff:g id="NUMBER_1">%d</xliff:g> спроби.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">SIM-карту заблоковано. Щоб продовжити, введіть PUK-код. Залишилася <xliff:g id="_NUMBER_1">%d</xliff:g> спроба. Після цього SIM-карту буде назавжди заблоковано. Щоб дізнатися більше, зверніться до свого оператора.</item>
+ <item quantity="few">SIM-карту заблоковано. Щоб продовжити, введіть PUK-код. Залишилося <xliff:g id="_NUMBER_1">%d</xliff:g> спроби. Після цього SIM-карту буде назавжди заблоковано. Щоб дізнатися більше, зверніться до свого оператора.</item>
+ <item quantity="many">SIM-карту заблоковано. Щоб продовжити, введіть PUK-код. Залишилося <xliff:g id="_NUMBER_1">%d</xliff:g> спроб. Після цього SIM-карту буде назавжди заблоковано. Щоб дізнатися більше, зверніться до свого оператора.</item>
+ <item quantity="other">SIM-карту заблоковано. Щоб продовжити, введіть PUK-код. Залишилося <xliff:g id="_NUMBER_1">%d</xliff:g> спроби. Після цього SIM-карту буде назавжди заблоковано. Щоб дізнатися більше, зверніться до свого оператора.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index 3bb59f0..cd99c92 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">آلہ <xliff:g id="NUMBER_0">%d</xliff:g> گھنٹہ سے غیر مقفل نہیں کیا گیا۔ پاسورڈ کی توثیق کریں۔</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"تسلیم شدہ نہیں ہے"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">SIM کا PIN درج کریں، آپ کے پاس <xliff:g id="NUMBER_1">%d</xliff:g> کوششیں بچی ہیں۔</item>
+ <item quantity="one">SIM کا PIN درج کریں، آپ کے پاس <xliff:g id="NUMBER_0">%d</xliff:g> کوشش بچی ہے، اس کے بعد آپ کو اپنا آلہ غیر مقفل کرنے کیلئے اپنے کیریئر سے رابطہ کرنا ہوگا۔</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM اب غیر فعال ہے۔ جاری رکھنے کیلئے PUK کوڈ درج کریں۔ SIM کے مستقل طور پر ناقابل استعمال ہونے سے پہلے آپ کے پاس <xliff:g id="_NUMBER_1">%d</xliff:g> کوششیں بچی ہیں۔ تفصیلات کیلئے کیریئر سے رابطہ کریں۔</item>
+ <item quantity="one">SIM اب غیر فعال ہے۔ جاری رکھنے کیلئے PUK کوڈ درج کریں۔ SIM کے مستقل طور پر ناقابل استعمال ہونے سے پہلے آپ کے پاس <xliff:g id="_NUMBER_0">%d</xliff:g> کوشش بچی ہے۔ تفصیلات کیلئے کیریئر سے رابطہ کریں۔</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml
index 699ad60..204bb8b 100644
--- a/packages/SystemUI/res-keyguard/values-uz/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Qurilma <xliff:g id="NUMBER_0">%d</xliff:g> soatdan beri qulfdan chiqarilgani yo‘q. Parolni yana bir marta kiriting.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Barmoq izi aniqlanmadi"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">SIM PIN kodini kiriting, sizda <xliff:g id="NUMBER_1">%d</xliff:g> ta urinish bor.</item>
+ <item quantity="one">SIM PIN kodini kiriting, qurilmani qulfdan chiqarish uchun sizda <xliff:g id="NUMBER_0">%d</xliff:g> ta urinish bor.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM karta faolsizlantirildi. Davom etish uchun PUK kodni kiriting. Yana <xliff:g id="_NUMBER_1">%d</xliff:g> marta xato qilsangiz, SIM kartangiz butunlay qulflanadi. Batafsil axborot olish uchun tarmoq operatoriga murojaat qiling.</item>
+ <item quantity="one">SIM karta faolsizlantirildi. Davom etish uchun PUK kodni kiriting. Yana <xliff:g id="_NUMBER_0">%d</xliff:g> marta xato qilsangiz, SIM kartangiz butunlay qulflanadi. Batafsil axborot olish uchun tarmoq operatoriga murojaat qiling.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml
index 67af2bf..d20b87e 100644
--- a/packages/SystemUI/res-keyguard/values-vi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">Thiết bị đã không được mở khóa trong <xliff:g id="NUMBER_0">%d</xliff:g> giờ. Xác nhận mật khẩu.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Không nhận dạng được"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">Hãy nhập mã PIN của SIM, bạn còn <xliff:g id="NUMBER_1">%d</xliff:g> lần thử.</item>
+ <item quantity="one">Hãy nhập mã PIN của SIM, bạn còn <xliff:g id="NUMBER_0">%d</xliff:g> lần thử trước khi bạn phải liên hệ với nhà cung cấp dịch vụ để mở khóa thiết bị của mình.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM hiện đã bị tắt. Hãy nhập mã PUK để tiếp tục. Bạn còn <xliff:g id="_NUMBER_1">%d</xliff:g> lần thử trước khi SIM vĩnh viễn không sử dụng được. Hãy liên hệ với nhà cung cấp dịch vụ để biết chi tiết.</item>
+ <item quantity="one">SIM hiện đã bị tắt. Hãy nhập mã PUK để tiếp tục. Bạn còn <xliff:g id="_NUMBER_0">%d</xliff:g> lần thử trước khi SIM vĩnh viễn không thể sử dụng được. Hãy liên hệ với nhà cung cấp dịch vụ để biết chi tiết.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index d523c20..8ee5812 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">设备已保持锁定状态达 <xliff:g id="NUMBER_0">%d</xliff:g> 小时。请确认密码。</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"无法识别"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">请输入 SIM 卡 PIN 码,您还可以尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
+ <item quantity="one">请输入 SIM 卡 PIN 码,您还可以尝试 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍不正确,则需要联系运营商帮您解锁设备。</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM 卡现已停用,请输入 PUK 码继续使用。您还可以尝试 <xliff:g id="_NUMBER_1">%d</xliff:g> 次。如果仍不正确,该 SIM 卡将永远无法使用。有关详情,请联系您的运营商。</item>
+ <item quantity="one">SIM 卡现已停用,请输入 PUK 码继续使用。您还可以尝试 <xliff:g id="_NUMBER_0">%d</xliff:g> 次。如果仍不正确,该 SIM 卡将永远无法使用。有关详情,请联系您的运营商。</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
index f343141..5b0754e 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">裝置在過去 <xliff:g id="NUMBER_0">%d</xliff:g> 小時內未有解鎖,請確認密碼。</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"未能識別"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">輸入 SIM 卡的 PIN,您還可以再試 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
+ <item quantity="one">輸入 SIM 卡的 PIN,您還可以再試 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍然輸入錯誤,您必須聯絡流動網絡供應商解鎖您的裝置。</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM 卡已停用。請輸入 PUK 碼以繼續進行。您還可以再試 <xliff:g id="_NUMBER_1">%d</xliff:g> 次。如果仍然輸入錯誤,SIM 卡將永久無法使用。詳情請與流動網絡供應商聯絡。</item>
+ <item quantity="one">SIM 卡已停用。請輸入 PUK 碼以繼續進行。您還可以再試 <xliff:g id="_NUMBER_0">%d</xliff:g> 次。如果仍然輸入錯誤,SIM 卡將永久無法使用。詳情請與流動網絡供應商聯絡。</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
index 9e0165a..45ab593 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
@@ -135,4 +135,12 @@
<item quantity="one">裝置已有 <xliff:g id="NUMBER_0">%d</xliff:g> 小時未解鎖。請確認密碼。</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"無法識別"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="other">請輸入 SIM 卡的 PIN 碼,你還可以再試 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
+ <item quantity="one">請輸入 SIM 卡的 PIN 碼,你還可以再試 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍然失敗,就必須請電信業者為裝置解鎖。</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="other">SIM 卡現在已遭停用。請輸入 PUK 碼以繼續進行。你還可以再試 <xliff:g id="_NUMBER_1">%d</xliff:g> 次,如果仍然失敗,SIM 卡將永久無法使用。詳情請與電信業者聯絡。</item>
+ <item quantity="one">SIM 卡現在已遭停用。請輸入 PUK 碼以繼續進行。你還可以再試 <xliff:g id="_NUMBER_0">%d</xliff:g> 次,如果仍然失敗,SIM 卡將永久無法使用。詳情請與電信業者聯絡。</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml
index 2f456bd..2329320 100644
--- a/packages/SystemUI/res-keyguard/values-zu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml
@@ -135,4 +135,12 @@
<item quantity="other">Idivayisi ayikavulwa ngamahora angu-<xliff:g id="NUMBER_1">%d</xliff:g>. Qinisekisa iphasiwedi.</item>
</plurals>
<string name="fingerprint_not_recognized" msgid="348813995267914625">"Akubonwa"</string>
+ <plurals name="kg_password_default_pin_message" formatted="false" msgid="6203676909479972943">
+ <item quantity="one">Faka i-PIN ye-SIM, unemizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g> esele.</item>
+ <item quantity="other">Faka i-PIN ye-SIM, unemizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g> esele.</item>
+ </plurals>
+ <plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
+ <item quantity="one">I-SIM manje ikhutshaziwe. Faka ikhodi ye-PUK ukuze uqhubeke. Unemizamo engu-<xliff:g id="_NUMBER_1">%d</xliff:g> esele ngaphambi kokuthi i-SIM ingasebenziseki unaphakade. Xhumana nenkampani yenethiwekhi ngemininingwane.</item>
+ <item quantity="other">I-SIM manje ikhutshaziwe. Faka ikhodi ye-PUK ukuze uqhubeke. Unemizamo engu-<xliff:g id="_NUMBER_1">%d</xliff:g> esele ngaphambi kokuthi i-SIM ingasebenziseki unaphakade. Xhumana nenkampani yenethiwekhi ngemininingwane.</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/drawable/ic_cast.xml b/packages/SystemUI/res/drawable/ic_cast.xml
new file mode 100644
index 0000000..b86dfea
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_cast.xml
@@ -0,0 +1,31 @@
+<!--
+ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M1 18v2c0 .55 .45 1 1 1h2c0-1.66-1.34-3-3-3zm0-2.94c-.01 .51 .32 .93 .82 1.02
+2.08 .36 3.74 2 4.1 4.08 .09 .48 .5 .84 .99 .84 .61 0 1.09-.54 1-1.14a6.996
+6.996 0 0 0-5.8-5.78c-.59-.09-1.09 .38 -1.11 .98 zm0-4.03c-.01 .52 .34 .96 .85
+1.01 4.26 .43 7.68 3.82 8.1 8.08 .05 .5 .48 .88 .99 .88 .59 0 1.06-.51
+1-1.1-.52-5.21-4.66-9.34-9.87-9.85-.57-.05-1.05 .4 -1.07 .98 zM21 3H3c-1.1 0-2
+.9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_speaker.xml b/packages/SystemUI/res/drawable/ic_speaker.xml
new file mode 100644
index 0000000..1ea293c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_speaker.xml
@@ -0,0 +1,26 @@
+<!--
+ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal" >
+ <path
+ android:pathData="M17,2L7,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,1.99 2,1.99L17,22c1.1,0 2,-0.9 2,-2L19,4c0,-1.1 -0.9,-2 -2,-2zM12,4c1.1,0 2,0.9 2,2s-0.9,2 -2,2c-1.11,0 -2,-0.9 -2,-2s0.89,-2 2,-2zM12,20c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,12c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_speaker_group.xml b/packages/SystemUI/res/drawable/ic_speaker_group.xml
new file mode 100644
index 0000000..d6867d7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_speaker_group.xml
@@ -0,0 +1,32 @@
+<!--
+ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal" >
+ <path
+ android:pathData="M18.2,1L9.8,1C8.81,1 8,1.81 8,2.8v14.4c0,0.99 0.81,1.79 1.8,1.79l8.4,0.01c0.99,0 1.8,-0.81 1.8,-1.8L20,2.8c0,-0.99 -0.81,-1.8 -1.8,-1.8zM14,3c1.1,0 2,0.89 2,2s-0.9,2 -2,2 -2,-0.89 -2,-2 0.9,-2 2,-2zM14,16.5c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"
+ android:fillColor="#FFFFFFFF"/>
+ <path
+ android:pathData="M14,12.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"
+ android:fillColor="#FFFFFFFF"/>
+ <path
+ android:pathData="M6,5H4v16c0,1.1 0.89,2 2,2h10v-2H6V5z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_tv.xml b/packages/SystemUI/res/drawable/ic_tv.xml
new file mode 100644
index 0000000..cc2ae91
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_tv.xml
@@ -0,0 +1,26 @@
+<!--
+ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal" >
+ <path
+ android:pathData="M21,3L3,3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h5v2h8v-2h5c1.1,0 1.99,-0.9 1.99,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM21,17L3,17L3,5h18v12z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/output_chooser.xml b/packages/SystemUI/res/layout/output_chooser.xml
new file mode 100644
index 0000000..3d0ab35
--- /dev/null
+++ b/packages/SystemUI/res/layout/output_chooser.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<!-- extends FrameLayout -->
+<com.android.systemui.volume.OutputChooserLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:sysui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/output_chooser"
+ android:minWidth="320dp"
+ android:minHeight="320dp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="20dp" >
+
+ <com.android.systemui.qs.AutoSizingList
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ sysui:itemHeight="@dimen/qs_detail_item_height"
+ style="@style/AutoSizingList"/>
+
+ <LinearLayout
+ android:id="@android:id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="20dp"
+ android:textAppearance="@style/TextAppearance.QS.DetailEmpty"/>
+ </LinearLayout>
+</com.android.systemui.volume.OutputChooserLayout>
diff --git a/packages/SystemUI/res/layout/output_chooser_item.xml b/packages/SystemUI/res/layout/output_chooser_item.xml
new file mode 100644
index 0000000..ed7df4b
--- /dev/null
+++ b/packages/SystemUI/res/layout/output_chooser_item.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/qs_detail_item_height"
+ android:background="@drawable/btn_borderless_rect"
+ android:clickable="true"
+ android:focusable="true"
+ android:gravity="center_vertical"
+ android:orientation="horizontal" >
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/qs_detail_item_icon_width"
+ android:layout_height="@dimen/qs_detail_item_icon_size"
+ android:layout_marginStart="@dimen/qs_detail_item_icon_marginStart"
+ android:layout_marginEnd="@dimen/qs_detail_item_icon_marginEnd"
+ android:tint="?android:attr/textColorPrimary"/>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="12dp"
+ android:layout_weight="1"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ android:ellipsize="end"
+ android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ android:layout_marginTop="2dp"
+ android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary" />
+ </LinearLayout>
+
+ <ImageView
+ android:id="@android:id/icon2"
+ style="@style/QSBorderlessButton"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_marginStart="30dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:scaleType="center"
+ android:contentDescription="@*android:string/media_route_controller_disconnect"
+ android:tint="?android:attr/textColorPrimary" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index bab1e5e..f0d2346 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -70,58 +70,77 @@
android:layout_height="@dimen/volume_button_size"
android:clickable="true"
android:soundEffectsEnabled="false"
- android:src="@drawable/ic_volume_collapse_animation"
+ android:src="@drawable/ic_volume_expand_animation"
android:background="@drawable/ripple_drawable"
tools:ignore="RtlHardcoded" />
</LinearLayout>
- <!-- special row for ringer mode -->
<RelativeLayout
- android:id="@+id/ringer_mode"
+ android:id="@+id/footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/rounded_bg_full"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_below="@id/volume_dialog_content"
android:layout_margin="10dp">
+ <!-- special row for ringer mode -->
+ <RelativeLayout
+ android:id="@+id/ringer_mode"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/rounded_bg_full"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:layout_toStartOf="@id/output_chooser"
+ android:layout_margin="10dp">
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/ringer_icon"
+ style="@style/VolumeButtons"
+ android:background="?android:selectableItemBackgroundBorderless"
+ android:layout_width="@dimen/volume_button_size"
+ android:layout_height="@dimen/volume_button_size"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:soundEffectsEnabled="false" />
+
+ <TextView
+ android:id="@+id/ringer_title"
+ android:text="@string/ring_toggle_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@+id/ringer_icon"
+ android:layout_marginStart="64dp"
+ android:textColor="?android:attr/colorControlNormal"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:paddingStart="@dimen/volume_row_header_padding_start" />
+
+ <TextView
+ android:id="@+id/ringer_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:layout_marginEnd="14dp"
+ android:maxLines="1"
+ android:textColor="?android:attr/colorControlNormal"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ </RelativeLayout>
<com.android.keyguard.AlphaOptimizedImageButton
- android:id="@+id/ringer_icon"
+ android:id="@+id/output_chooser"
style="@style/VolumeButtons"
android:background="?android:selectableItemBackgroundBorderless"
android:layout_width="@dimen/volume_button_size"
android:layout_height="@dimen/volume_button_size"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:soundEffectsEnabled="false" />
-
- <TextView
- android:id="@+id/ringer_title"
- android:text="@string/stream_ring"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:maxLines="1"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:layout_toEndOf="@+id/ringer_icon"
- android:layout_marginStart="64dp"
- android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:paddingStart="@dimen/volume_row_header_padding_start" />
-
- <TextView
- android:id="@+id/ringer_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="end"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
- android:layout_marginEnd="14dp"
- android:maxLines="1"
- android:textColor="?android:attr/colorControlNormal"
- android:textAppearance="?android:attr/textAppearanceSmall" />
-
+ android:src="@drawable/ic_settings_bluetooth"
+ android:soundEffectsEnabled="false" />
</RelativeLayout>
</RelativeLayout>
</com.android.systemui.volume.VolumeUiLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index e527089..15a7d4c 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Dubbele multitoonfrekwensie"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Toeganklikheid"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Oproepe"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Lui"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibreer"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Demp"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tik om te ontdemp."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tik om op vibreer te stel. Toeganklikheidsdienste kan dalk gedemp wees."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tik om te demp. Toeganklikheidsdienste kan dalk gedemp wees."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Kitsprogramme"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Kitsprogramme hoef nie geïnstalleer te word nie."</string>
<string name="app_info" msgid="6856026610594615344">"Programinligting"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Gaan na web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Gaan na blaaier"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobiele data"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi is af"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth is af"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 8ee77e1..ee9acae 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ብሉቱዝ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"ድርብ ባለ በርካታ ቅላጼ ድግምግሞሽ"</string>
<string name="stream_accessibility" msgid="301136219144385106">"ተደራሽነት"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"ጥሪዎች"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"ጥሪ"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"ንዘር"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"ድምጸ-ከል አድርግ"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s። ወደ ንዝረት ለማቀናበር መታ ያድርጉ። የተደራሽነት አገልግሎቶች ድምጸ-ከል ሊደረግባቸው ይችላል።"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ። የተደራሽነት አገልግሎቶች ድምጸ-ከል ሊደረግባቸው ይችላል።"</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"የቅጽበት መተግበሪያዎች"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"ቅጽበታዊ መተግበሪያዎች መጫን አያስፈልጋቸውም።"</string>
<string name="app_info" msgid="6856026610594615344">"የመተግበሪያ መረጃ"</string>
- <string name="go_to_web" msgid="1106022723459948514">"ወደ ድር ሂድ"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"ወደ አሳሽ ሂድ"</string>
<string name="mobile_data" msgid="7094582042819250762">"የተንቀሳቃሽ ስልክ ውሂብ"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi ጠፍቷል"</string>
<string name="bt_is_off" msgid="2640685272289706392">"ብሉቱዝ ጠፍቷል"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 48e8739..1d8cb4e 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -500,6 +500,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"بلوتوث"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"تردد ثنائي متعدد النغمات"</string>
<string name="stream_accessibility" msgid="301136219144385106">"إمكانية الوصول"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"المكالمات"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"استصدار رنين"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"اهتزاز"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"كتم الصوت"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. انقر لإلغاء التجاهل."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. انقر للتعيين على الاهتزاز. قد يتم تجاهل خدمات إمكانية الوصول."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. انقر للتجاهل. قد يتم تجاهل خدمات إمكانية الوصول."</string>
@@ -780,7 +784,7 @@
<string name="instant_apps" msgid="6647570248119804907">"التطبيقات الفورية"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"لا تتطلب التطبيقات الفورية إجراء التثبيت."</string>
<string name="app_info" msgid="6856026610594615344">"معلومات عن التطبيق"</string>
- <string name="go_to_web" msgid="1106022723459948514">"الانتقال إلى الويب"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"الانتقال إلى المتصفح"</string>
<string name="mobile_data" msgid="7094582042819250762">"بيانات الجوّال"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"تم إيقاف شبكة Wi-Fi"</string>
<string name="bt_is_off" msgid="2640685272289706392">"تم إيقاف البلوتوث."</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index de9ffe1..098b1cd 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -492,6 +492,9 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Çoxsaylı ton olan ikili tezlik"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Münasiblik"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Zəng"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrasiya"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Susdurun"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Səsli etmək üçün tıklayın."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Vibrasiyanı ayarlamaq üçün tıklayın. Əlçatımlılıq xidmətləri səssiz edilmiş ola bilər."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Səssiz etmək üçün tıklayın. Əlçatımlılıq xidmətləri səssiz edilmiş ola bilər."</string>
@@ -756,7 +759,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Ani Tətbiqlər"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Ani tətbiqlər quraşdırma tələb etmir."</string>
<string name="app_info" msgid="6856026610594615344">"Tətbiq məlumatı"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Vebə keçin"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Brauzerə daxil edin"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobil data"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi deaktivdir"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth deaktivdir"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 4e38e5d..fd42eb5 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -494,6 +494,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Višestruka frekvencija dualnog tona"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Pristupačnost"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Pozivi"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Aktiviraj zvono"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibriraj"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Isključi zvuk"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Dodirnite da biste uključili zvuk."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Dodirnite da biste podesili na vibraciju. Zvuk usluga pristupačnosti će možda biti isključen."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Dodirnite da biste isključili zvuk. Zvuk usluga pristupačnosti će možda biti isključen."</string>
@@ -762,7 +766,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant aplikacije"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Instant aplikacije ne zahtevaju instalaciju."</string>
<string name="app_info" msgid="6856026610594615344">"Informacije o aplikaciji"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Idi na veb"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Idi na pregledač"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobilni podaci"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi je isključen"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth je isključen"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index c077c03..e82dbff 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -498,6 +498,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Двухтанальны шматчастотны"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Спецыяльныя магчымасці"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Выклікі"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Званок"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Вібрацыя"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Гук выключаны"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Дакраніцеся, каб уключыць гук."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Дакраніцеся, каб уключыць вібрацыю. Можа быць адключаны гук службаў спецыяльных магчымасцей."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Дакраніцеся, каб адключыць гук. Можа быць адключаны гук службаў спецыяльных магчымасцей."</string>
@@ -770,7 +774,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Імгненныя праграмы"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Імгненныя праграмы не патрабуюць усталёўкі."</string>
<string name="app_info" msgid="6856026610594615344">"Інфармацыя пра праграму"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Перайсці ў інтэрнэт"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Перайсці ў браўзер"</string>
<string name="mobile_data" msgid="7094582042819250762">"Маб. перадача даных"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi выключаны"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth выключаны"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index c567adf..ccdc44b 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Тонално набиране"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Достъпност"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Обаждания"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Позвъняване"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Вибриране"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Без звук"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Докоснете, за да включите отново звука."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Докоснете, за да зададете вибриране. Възможно е звукът на услугите за достъпност да бъде заглушен."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Докоснете, за да заглушите звука. Възможно е звукът на услугите за достъпност да бъде заглушен."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Мигновени приложения"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"За мигновените приложения не се изисква инсталиране."</string>
<string name="app_info" msgid="6856026610594615344">"Информация за приложението"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Към мрежата"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Към браузъра"</string>
<string name="mobile_data" msgid="7094582042819250762">"Мобилни данни"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Функцията за Wi‑Fi е изключена"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Функцията за Bluetooth е изключена"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 5770779..96028f7 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -492,6 +492,11 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ব্লুটুথ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"ডুয়েল মাল্টি টোন ফ্রিকোয়েন্সি"</string>
<string name="stream_accessibility" msgid="301136219144385106">"অ্যাক্সেসযোগ্যতা"</string>
+ <!-- no translation found for ring_toggle_title (3281244519428819576) -->
+ <skip />
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"রিং"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"ভাইব্রেট"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"মিউট"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s। সশব্দ করতে আলতো চাপুন।"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s। কম্পন এ সেট করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে নিঃশব্দ করা হতে পারে।"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s। নিঃশব্দ করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে নিঃশব্দ করা হতে পারে।"</string>
@@ -756,8 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"ঝটপট অ্যাপ"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"ঝটপট অ্যাপ ইনস্টল করার প্রয়োজন হয় না।"</string>
<string name="app_info" msgid="6856026610594615344">"অ্যাপের তথ্য"</string>
- <!-- no translation found for go_to_web (2650669128861626071) -->
- <skip />
+ <string name="go_to_web" msgid="2650669128861626071">"ব্রাউজারে যান"</string>
<string name="mobile_data" msgid="7094582042819250762">"মোবাইল ডেটা"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"ওয়াই ফাই বন্ধ আছে"</string>
<string name="bt_is_off" msgid="2640685272289706392">"ব্লুটুথ বন্ধ আছে"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 530c1a8..f09bb1f 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -494,6 +494,9 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Dvostruka višemelodijska frekvencija"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Pristupačnost"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Zvono"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibriranje"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Isključi zvuk"</string>
<!-- String.format failed for translation -->
<!-- no translation found for volume_stream_content_description_unmute (4436631538779230857) -->
<skip />
@@ -764,8 +767,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant-aplikacije"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Za instant aplikacije nije potrebna instalacija"</string>
<string name="app_info" msgid="6856026610594615344">"Informacije o aplikaciji"</string>
- <!-- no translation found for go_to_web (2650669128861626071) -->
- <skip />
+ <string name="go_to_web" msgid="2650669128861626071">"Idi na preglednik"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobilni podaci"</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>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 1c5fe90..dacf9d7 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Marcatge per tons"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Accessibilitat"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Trucades"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Fes sonar"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibra"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Silencia"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Toca per activar el so."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Toca per activar la vibració. Pot ser que els serveis d\'accessibilitat se silenciïn."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Toca per silenciar el so. Pot ser que els serveis d\'accessibilitat se silenciïn."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Aplicacions instantànies"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"No cal instal·lar les aplicacions instantànies."</string>
<string name="app_info" msgid="6856026610594615344">"Informació de l\'aplicació"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Ves al web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Ves al navegador"</string>
<string name="mobile_data" msgid="7094582042819250762">"Dades mòbils"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"La Wi-Fi està desactivada"</string>
<string name="bt_is_off" msgid="2640685272289706392">"El Bluetooth està desactivat"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index b1cddfb..4166697 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -498,6 +498,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Tónová volba"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Přístupnost"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Volání"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Vyzvánění"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrace"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Ztlumení"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Klepnutím zapnete zvuk."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Klepnutím aktivujete režim vibrací. Služby přístupnosti mohou být ztlumeny."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Klepnutím vypnete zvuk. Služby přístupnosti mohou být ztlumeny."</string>
@@ -770,7 +774,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Okamžité aplikace"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Okamžité aplikace není třeba instalovat."</string>
<string name="app_info" msgid="6856026610594615344">"O aplikaci"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Přejít na web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Přejít do prohlížeče"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobilní data"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi je vypnuta"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth je vypnuto"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index e4198f3..52446f8 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Tonesignalfrekvens (DTMF)"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Hjælpefunktioner"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Opkald"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Ring"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibration"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Slå lyden fra"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tryk for at slå lyden til."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tryk for at konfigurere til at vibrere. Tilgængelighedstjenester kan blive deaktiveret."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tryk for at slå lyden fra. Lyden i tilgængelighedstjenester kan blive slået fra."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Instant apps kræver ingen installation."</string>
<string name="app_info" msgid="6856026610594615344">"Appinfo"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Gå til website"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Gå til en browser"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobildata"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi er slået fra"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth er slået fra"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index e9c2f26..83a8e33 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -496,6 +496,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Doppelton-Mehrfrequenz"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Bedienungshilfen"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Anrufe"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Klingeln lassen"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrieren"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Stummschalten"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Zum Aufheben der Stummschaltung tippen."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tippen, um Vibrieren festzulegen. Bedienungshilfen werden unter Umständen stummgeschaltet."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Zum Stummschalten tippen. Bedienungshilfen werden unter Umständen stummgeschaltet."</string>
@@ -760,7 +764,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant-Apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Bei Instant-Apps ist keine vorherige Installation erforderlich."</string>
<string name="app_info" msgid="6856026610594615344">"App-Informationen"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Web aufrufen"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Browser öffnen"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobile Daten"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"WLAN ist deaktiviert"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth ist deaktiviert"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 33df73b..9a61828 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Πολυσυχνότητα διπλού τόνου"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Προσβασιμότητα"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Κλήσεις"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Κουδούνισμα"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Δόνηση"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Σίγαση"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Πατήστε για κατάργηση σίγασης."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Πατήστε για ενεργοποιήσετε τη δόνηση. Οι υπηρεσίες προσβασιμότητας ενδέχεται να τεθούν σε σίγαση."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Πατήστε για σίγαση. Οι υπηρεσίες προσβασιμότητας ενδέχεται να τεθούν σε σίγαση."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant Εφαρμογές"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Οι Instant Εφαρμογές δεν απαιτούν εγκατάσταση."</string>
<string name="app_info" msgid="6856026610594615344">"Πληροφορίες εφαρμογής"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Μετάβαση στον ιστό"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Μετάβ. σε πρόγ. περ."</string>
<string name="mobile_data" msgid="7094582042819250762">"Δεδομένα κινητής τηλεφωνίας"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Το Wi-Fi είναι ανενεργό"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Το Bluetooth είναι ανενεργό"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index c97f7d3..d808119 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -494,6 +494,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Multifrecuencia de tono doble"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Accesibilidad"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Llamadas"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Hacer sonar"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrar"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Silenciar"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Presiona para dejar de silenciar."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Presiona para establecer el modo vibración. Es posible que los servicios de accesibilidad estén silenciados."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Presiona para silenciar. Es posible que los servicios de accesibilidad estén silenciados."</string>
@@ -758,7 +762,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Apps instantáneas"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Las Apps instantáneas no requieren instalación."</string>
<string name="app_info" msgid="6856026610594615344">"Información de apps"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Ir a la Web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Ir al navegador"</string>
<string name="mobile_data" msgid="7094582042819250762">"Datos móviles"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi desactivado"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth desactivado"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 94467d8..14c2818 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -494,6 +494,9 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Multifrecuencia de tono dual"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Accesibilidad"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Hacer sonar"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrar"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Silenciar"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Toca para activar el sonido."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Toca para poner el dispositivo en vibración. Los servicios de accesibilidad pueden silenciarse."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Toca para silenciar. Los servicios de accesibilidad pueden silenciarse."</string>
@@ -758,7 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Aplicaciones Instantáneas"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"No es necesario instalar las Aplicaciones Instantáneas."</string>
<string name="app_info" msgid="6856026610594615344">"Información de la aplicación"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Ir a la Web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Ir al navegador"</string>
<string name="mobile_data" msgid="7094582042819250762">"Datos móviles"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi desactivado"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth desactivado"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 3a4eea6..3a5af25 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -494,6 +494,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Kaks mitme tooniga sagedust"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Juurdepääsetavus"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Kõned"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Helisemine"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibreerimine"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Vaigistatud"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Puudutage vaigistuse tühistamiseks."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Puudutage värinarežiimi määramiseks. Juurdepääsetavuse teenused võidakse vaigistada."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Puudutage vaigistamiseks. Juurdepääsetavuse teenused võidakse vaigistada."</string>
@@ -758,7 +762,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Installimata avatavad rakendused"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Installimata avatavaid rakendusi pole vaja installida."</string>
<string name="app_info" msgid="6856026610594615344">"Rakenduse teave"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Avage veebis"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Ava brauser"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobiilne andmeside"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"WiFi on välja lülitatud"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth on välja lülitatud"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index b75548e5..87348de 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -494,6 +494,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth konexioa"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Tonu anitzeko maiztasun duala"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Erabilerraztasuna"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Deiak"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Jo tonua"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Dardara"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Ez jo tonua"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Sakatu audioa aktibatzeko."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Sakatu dardara ezartzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Sakatu audioa desaktibatzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string>
@@ -758,7 +762,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Zuzeneko aplikazioak"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Zuzeneko aplikazioak ez dira instalatu behar."</string>
<string name="app_info" msgid="6856026610594615344">"Aplikazioari buruzko informazioa"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Joan sarera"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Joan arakatzailera"</string>
<string name="mobile_data" msgid="7094582042819250762">"Datu mugikorrak"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi konexioa desaktibatuta dago"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth konexioa desaktibatuta dago"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index d107fea..bb77daa 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"بلوتوث"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"فرکانس دوتایی چند نوایی"</string>
<string name="stream_accessibility" msgid="301136219144385106">"دسترسپذیری"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"تماسها"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"زنگ زدن"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"لرزش"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"بیصدا"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. برای باصدا کردن ضربه بزنید."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. برای تنظیم روی لرزش ضربه بزنید. ممکن است سرویسهای دسترسپذیری بیصدا شوند."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. برای بیصدا کردن ضربه بزنید. ممکن است سرویسهای دسترسپذیری بیصدا شوند."</string>
@@ -756,8 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"برنامههای فوری"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"برنامههای فوری نیاز به نصب ندارند."</string>
<string name="app_info" msgid="6856026610594615344">"اطلاعات برنامه"</string>
- <!-- no translation found for go_to_web (2650669128861626071) -->
- <skip />
+ <string name="go_to_web" msgid="2650669128861626071">"رفتن به مرورگر"</string>
<string name="mobile_data" msgid="7094582042819250762">"داده تلفن همراه"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi خاموش است"</string>
<string name="bt_is_off" msgid="2640685272289706392">"بلوتوث خاموش است"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index ccec4a6..3bebe4b 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Äänitaajuusvalinta"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Esteettömyys"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Puhelut"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Soittoääni"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Värinä"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Äänetön"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Poista mykistys koskettamalla."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Siirry värinätilaan koskettamalla. Myös esteettömyyspalvelut saattavat mykistyä."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Mykistä koskettamalla. Myös esteettömyyspalvelut saattavat mykistyä."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Pikasovelluksia ei tarvitse asentaa."</string>
<string name="app_info" msgid="6856026610594615344">"Sovelluksen tiedot"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Avaa verkossa"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Siirry selaimeen"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobiilitiedonsiirto"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi on pois käytöstä"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth ei ole käytössä"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index bdd01c2..a2dde85 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -494,6 +494,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Fréquence double multi ton"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Accessibilité"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Appels"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Sonnerie"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibration"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Sonnerie désactivée"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Touchez pour réactiver le son."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Touchez pour activer les vibrations. Il est possible de couper le son des services d\'accessibilité."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Touchez pour couper le son. Il est possible de couper le son des services d\'accessibilité."</string>
@@ -758,7 +762,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Applications instantanées"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Les applications instantanées ne nécessitent pas d\'installation."</string>
<string name="app_info" msgid="6856026610594615344">"Détails de l\'application"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Accéder au Web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Ouvrir le navigateur"</string>
<string name="mobile_data" msgid="7094582042819250762">"Données cellulaires"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Le Wi-Fi est désactivé"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Le Bluetooth est désactivé"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index ee50e56..21952ae 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -494,6 +494,9 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"DTMF"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Accessibilité"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Sonnerie"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibreur"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Silencieux"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Appuyez pour ne plus ignorer."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Appuyez pour mettre en mode vibreur. Vous pouvez ignorer les services d\'accessibilité."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Appuyez pour ignorer. Vous pouvez ignorer les services d\'accessibilité."</string>
@@ -758,7 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Applis instantanées"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Les applis instantanées ne nécessitent pas d\'installation."</string>
<string name="app_info" msgid="6856026610594615344">"Infos sur l\'appli"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Accéder au site Web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Accéder au navigateur"</string>
<string name="mobile_data" msgid="7094582042819250762">"Données mobiles"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi désactivé"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth désactivé"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 558b98c..9fbcdec 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -280,7 +280,7 @@
<string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotación bloqueada"</string>
<string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Vertical"</string>
<string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Horizontal"</string>
- <string name="quick_settings_ime_label" msgid="7073463064369468429">"Método de entrada"</string>
+ <string name="quick_settings_ime_label" msgid="7073463064369468429">"Método de introdución de texto"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Localización"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Localización desactivada"</string>
<string name="quick_settings_media_device_label" msgid="1302906836372603762">"Dispositivo multimedia"</string>
@@ -494,6 +494,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Multifrecuencia de dobre ton"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Accesibilidade"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Chamadas"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Facer soar"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrar"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Silenciar"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Toca para activar o son."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Toca para establecer a vibración. Pódense silenciar os servizos de accesibilidade."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Toca para silenciar. Pódense silenciar os servizos de accesibilidade."</string>
@@ -758,7 +762,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Aplicacións instantáneas"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"As aplicacións instantáneas non precisan instalación."</string>
<string name="app_info" msgid="6856026610594615344">"Info. da aplicación"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Acceder á web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Ir ao navegador"</string>
<string name="mobile_data" msgid="7094582042819250762">"Datos móbiles"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"A wifi está desactivada"</string>
<string name="bt_is_off" msgid="2640685272289706392">"O Bluetooth está desactivado"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 139ab82..418f31e 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -34,17 +34,14 @@
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"નોટિફિકેશનો"</string>
<string name="battery_low_title" msgid="6456385927409742437">"બૅટરી ઓછી છે"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> બાકી"</string>
- <!-- no translation found for battery_low_percent_format_saver_started (7879389868952879166) -->
- <skip />
+ <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> બાકી. બૅટરી સેવર ચાલુ છે."</string>
<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>
- <!-- no translation found for battery_saver_confirmation_title (2052100465684817154) -->
- <skip />
+ <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"બૅટરી સેવર ચાલુ કરીએ?"</string>
<string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"ચાલુ કરો"</string>
- <!-- no translation found for battery_saver_start_action (8187820911065797519) -->
- <skip />
+ <string name="battery_saver_start_action" msgid="8187820911065797519">"બૅટરી સેવર ચાલુ કરો"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"સેટિંગ્સ"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"વાઇ-ફાઇ"</string>
<string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"સ્ક્રીનને આપમેળે ફેરવો"</string>
@@ -402,11 +399,9 @@
<string name="user_remove_user_title" msgid="4681256956076895559">"વપરાશકર્તાને દૂર કરીએ?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"આ વપરાશકર્તાની તમામ ઍપ્લિકેશનો અને ડેટા કાઢી નાખવામાં આવશે."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"દૂર કરો"</string>
- <!-- no translation found for battery_saver_notification_title (8614079794522291840) -->
- <skip />
+ <string name="battery_saver_notification_title" msgid="8614079794522291840">"બૅટરી સેવર ચાલુ છે"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"પ્રદર્શન અને બૅકગ્રાઉન્ડ ડેટા ઘટાડે છે"</string>
- <!-- no translation found for battery_saver_notification_action_text (132118784269455533) -->
- <skip />
+ <string name="battery_saver_notification_action_text" msgid="132118784269455533">"બૅટરી સેવર બંધ કરો"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> તમારી સ્ક્રીન પર જે પ્રદર્શિત થાય છે તે દરેક વસ્તુને કેપ્ચર કરવાનું પ્રારંભ કરશે."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"ફરીથી બતાવશો નહીં"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"બધુ સાફ કરો"</string>
@@ -497,6 +492,11 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"બ્લૂટૂથ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"દ્વિ બહુ ટોન આવર્તન"</string>
<string name="stream_accessibility" msgid="301136219144385106">"ઍક્સેસિબિલિટી"</string>
+ <!-- no translation found for ring_toggle_title (3281244519428819576) -->
+ <skip />
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"રિંગ કરો"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"વાઇબ્રેટ"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"મ્યૂટ કરો"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. અનમ્યૂટ કરવા માટે ટૅપ કરો."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. વાઇબ્રેટ પર સેટ કરવા માટે ટૅપ કરો. ઍક્સેસિબિલિટી સેવાઓ મ્યૂટ કરવામાં આવી શકે છે."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. મ્યૂટ કરવા માટે ટૅપ કરો. ઍક્સેસિબિલિટી સેવાઓ મ્યૂટ કરવામાં આવી શકે છે."</string>
@@ -761,7 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"ઝટપટ ઍપ્લિકેશનો"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"ઝટપટ ઍપ્લિકેશનો માટે ઇન્સ્ટૉલેશનની જરૂર નથી."</string>
<string name="app_info" msgid="6856026610594615344">"ઍપ્લિકેશન માહિતી"</string>
- <string name="go_to_web" msgid="1106022723459948514">"વેબ પર જાઓ"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"બ્રાઉઝર પર જાઓ"</string>
<string name="mobile_data" msgid="7094582042819250762">"મોબાઇલ ડેટા"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"વાઇ-ફાઇ બંધ છે"</string>
<string name="bt_is_off" msgid="2640685272289706392">"બ્લૂટૂથ બંધ છે"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 1681c2e..1acd6e7 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -492,6 +492,11 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ब्लूटूथ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"दोहरी बहु टोन आवृत्ति"</string>
<string name="stream_accessibility" msgid="301136219144385106">"सुलभता"</string>
+ <!-- no translation found for ring_toggle_title (3281244519428819576) -->
+ <skip />
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"आवाज़ चालू है"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"कंपन (वाइब्रेशन)"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"आवाज़ बंद है"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. अनम्यूट करने के लिए टैप करें."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. कंपन पर सेट करने के लिए टैप करें. सुलभता सेवाएं म्यूट हो सकती हैं."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. म्यूट करने के लिए टैप करें. सुलभता सेवाएं म्यूट हो सकती हैं."</string>
@@ -756,7 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"झटपट ऐप्स"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"झटपट ऐप्स के लिए इंस्टॉलेशन ज़रूरी नहीं है."</string>
<string name="app_info" msgid="6856026610594615344">"ऐप की जानकारी"</string>
- <string name="go_to_web" msgid="1106022723459948514">"वेब पर जाएं"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"ब्राउज़र पर जाएं"</string>
<string name="mobile_data" msgid="7094582042819250762">"मोबाइल डेटा"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"वाई-फ़ाई बंद है"</string>
<string name="bt_is_off" msgid="2640685272289706392">"ब्लूटूथ बंद है"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 699efad..7ba9111 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -494,6 +494,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"DTMF"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Pristupačnost"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Pozivi"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Zvonjenje"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibriranje"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Zvuk je isključen"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Dodirnite da biste uključili zvuk."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Dodirnite da biste postavili na vibraciju. Usluge pristupačnosti možda neće imati zvuk."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Dodirnite da biste isključili zvuk. Usluge pristupačnosti možda neće imati zvuk."</string>
@@ -762,7 +766,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant aplikacije"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Instant aplikacije nije potrebno instalirati."</string>
<string name="app_info" msgid="6856026610594615344">"Informacije o aplikaciji"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Prijeđi na web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Otvori preglednik"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobilni podaci"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi je isključen"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth je isključen"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index faa4a86..8aa2c71 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Kéthangú többfrekvenciás jelzésátvitel (DTMF)"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Kisegítő lehetőségek"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Hívások"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Csörgés"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Rezgés"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Néma"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Koppintson a némítás megszüntetéséhez."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Koppintson a rezgés beállításához. Előfordulhat, hogy a kisegítő lehetőségek szolgáltatásai le vannak némítva."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Koppintson a némításhoz. Előfordulhat, hogy a kisegítő lehetőségek szolgáltatásai le vannak némítva."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Azonnali alkalmazások"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Az azonnali alkalmazásokat nem kell telepíteni."</string>
<string name="app_info" msgid="6856026610594615344">"Alkalmazásinformáció"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Tovább az internetre"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Ugrás a böngészőbe"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobiladatok"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"A Wi-Fi ki van kapcsolva"</string>
<string name="bt_is_off" msgid="2640685272289706392">"A Bluetooth ki van kapcsolva"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 074ca49..2841074 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Կրկնակի բազմերանգ հաճախականություն"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Մատչելիություն"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Զանգեր"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Սովորական"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Թրթռազանգ"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Անձայն"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s: Հպեք՝ ձայնը միացնելու համար:"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s: Հպեք՝ թրթռումը միացնելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s: Հպեք՝ ձայնն անջատելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Ակնթարթորեն գործարկվող հավելվածներ"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Ակնթարթորեն գործարկվող հավելվածները տեղադրում չեն պահանջում։"</string>
<string name="app_info" msgid="6856026610594615344">"Հավելվածի տվյալներ"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Բացեք համացանցում"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Անցնել դիտարկիչ"</string>
<string name="mobile_data" msgid="7094582042819250762">"Բջջային ինտերնետ"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi-ն անջատված է"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth-ն անջատված է"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index b4709bb..f8604e3 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -492,6 +492,9 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Frekuensi multinada ganda"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Aksesibilitas"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Dering"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Getar"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Nonaktifkan"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Ketuk untuk menyuarakan."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Ketuk untuk menyetel agar bergetar. Layanan aksesibilitas mungkin dibisukan."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Ketuk untuk membisukan. Layanan aksesibilitas mungkin dibisukan."</string>
@@ -756,8 +759,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Aplikasi Instan"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Aplikasi instan tidak perlu diinstal."</string>
<string name="app_info" msgid="6856026610594615344">"Info aplikasi"</string>
- <!-- no translation found for go_to_web (2650669128861626071) -->
- <skip />
+ <string name="go_to_web" msgid="2650669128861626071">"Buka browser"</string>
<string name="mobile_data" msgid="7094582042819250762">"Data seluler"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi nonaktif"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth nonaktif"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index f60a766..66530bd 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Tvítóna fjöltíðni"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Aðgengi"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Símtöl"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Hringing"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Titringur"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Hljóð af"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Ýttu til að hætta að þagga."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Ýttu til að stilla á titring. Hugsanlega verður slökkt á hljóði aðgengisþjónustu."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Ýttu til að þagga. Hugsanlega verður slökkt á hljóði aðgengisþjónustu."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Skyndiforrit"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Skyndiforrit þurfa ekki uppsetningu."</string>
<string name="app_info" msgid="6856026610594615344">"Forritsupplýsingar"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Fara á vefinn"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Opna vafra"</string>
<string name="mobile_data" msgid="7094582042819250762">"Farsímagögn"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Slökkt á Wi-Fi"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Slökkt á Bluetooth"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index d4e6314..c15e4b6 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -494,6 +494,9 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Frequenza multipla dual tone"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Accessibilità"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Suoneria"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrazione"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Disattiva audio"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tocca per riattivare l\'audio."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tocca per attivare la vibrazione. L\'audio dei servizi di accessibilità può essere disattivato."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tocca per disattivare l\'audio. L\'audio dei servizi di accessibilità può essere disattivato."</string>
@@ -758,7 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"App istantanee"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Le app istantanee non richiedono l\'installazione."</string>
<string name="app_info" msgid="6856026610594615344">"Informazioni app"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Vai sul Web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Vai al browser"</string>
<string name="mobile_data" msgid="7094582042819250762">"Dati mobili"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi disattivato"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth non attivo"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index f20275d..cbbffa9 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -496,6 +496,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"טון זוגי מרובה תדרים (DTMF)"</string>
<string name="stream_accessibility" msgid="301136219144385106">"נגישות"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"שיחות"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"צלצול"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"רטט"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"השתקה"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. הקש כדי לבטל את ההשתקה."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. הקש כדי להגדיר רטט. ייתכן ששירותי הנגישות מושתקים."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. הקש כדי להשתיק. ייתכן ששירותי הנגישות מושתקים."</string>
@@ -768,7 +772,7 @@
<string name="instant_apps" msgid="6647570248119804907">"אפליקציות אינסטנט"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"אפליקציות אינסטנט לא דורשות התקנה."</string>
<string name="app_info" msgid="6856026610594615344">"פרטי אפליקציה"</string>
- <string name="go_to_web" msgid="1106022723459948514">"התחבר לאינטרנט"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"מעבר אל הדפדפן"</string>
<string name="mobile_data" msgid="7094582042819250762">"נתונים סלולריים"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi כבוי"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth כבוי"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index ac83c2d..f840225 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -494,6 +494,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"デュアルトーン マルチ周波数"</string>
<string name="stream_accessibility" msgid="301136219144385106">"ユーザー補助機能"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"通話"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"着信音"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"バイブレーション"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"ミュート"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s。タップしてミュートを解除します。"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s。タップしてバイブレーションに設定します。ユーザー補助機能サービスがミュートされる場合があります。"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s。タップしてミュートします。ユーザー補助機能サービスがミュートされる場合があります。"</string>
@@ -758,7 +762,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Instant Apps はインストールせずに利用できます。"</string>
<string name="app_info" msgid="6856026610594615344">"アプリ情報"</string>
- <string name="go_to_web" msgid="1106022723459948514">"ウェブページを開く"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"ブラウザに移動"</string>
<string name="mobile_data" msgid="7094582042819250762">"モバイルデータ"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi は OFF です"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth は OFF です"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index e871c6a..933a5e3 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -492,6 +492,9 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"ტონალური აკრეფა"</string>
<string name="stream_accessibility" msgid="301136219144385106">"მარტივი წვდომა"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"დარეკვა"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"ვიბრაცია"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"დადუმება"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. შეეხეთ დადუმების გასაუქმებლად."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. შეეხეთ ვიბრაციაზე დასაყენებლად. შეიძლება დადუმდეს მარტივი წვდომის სერვისებიც."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. შეეხეთ დასადუმებლად. შეიძლება დადუმდეს მარტივი წვდომის სერვისებიც."</string>
@@ -756,8 +759,7 @@
<string name="instant_apps" msgid="6647570248119804907">"მყისიერი აპები"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"მყისიერი აპები ინსტალაციას არ საჭიროებს."</string>
<string name="app_info" msgid="6856026610594615344">"აპის შესახებ"</string>
- <!-- no translation found for go_to_web (2650669128861626071) -->
- <skip />
+ <string name="go_to_web" msgid="2650669128861626071">"ბრაუზერზე გადასვლა"</string>
<string name="mobile_data" msgid="7094582042819250762">"მობილური ინტერნეტი"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi გამორთულია"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth გამორთულია"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index f2b4fc8..a9e6ffe 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Қос үнді көп жиілік"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Арнайы мүмкіндіктер"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Қоңыраулар"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Шылдырлау"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Діріл"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Дыбысын өшіру"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Дыбысын қосу үшін түртіңіз."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Діріл режимін орнату үшін түртіңіз. Арнайы мүмкіндік қызметтерінің дыбысы өшуі мүмкін."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Дыбысын өшіру үшін түртіңіз. Арнайы мүмкіндік қызметтерінің дыбысы өшуі мүмкін."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Лездік қолданбаларды орнатудың қажеті жоқ."</string>
<string name="app_info" msgid="6856026610594615344">"Қолданба ақпараты"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Вебке өту"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Браузерге өту"</string>
<string name="mobile_data" msgid="7094582042819250762">"Мобильдік деректер"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi өшірулі"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth өшірулі"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index ad9b057..5819f2a 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ប៊្លូធូស"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"ហ្វ្រេកង់ពហុសំឡេងទ្វេ"</string>
<string name="stream_accessibility" msgid="301136219144385106">"ភាពងាយស្រួល"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"ហៅទូរសព្ទ"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"រោទ៍"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"ញ័រ"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"បិទសំឡេង"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s។ ប៉ះដើម្បីបើកសំឡេង។"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s។ ប៉ះដើម្បីកំណត់ឲ្យញ័រ។ សេវាកម្មលទ្ធភាពប្រើប្រាស់អាចនឹងត្រូវបានបិទសំឡេង។"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s។ ប៉ះដើម្បីបិទសំឡេង។ សេវាកម្មលទ្ធភាពប្រើប្រាស់អាចនឹងត្រូវបានបិទសំឡេង។"</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"កម្មវិធីប្រើភ្លាមៗ"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"កម្មវិធីប្រើភ្លាមៗមិនតម្រូវឲ្យមានការដំឡើងទេ។"</string>
<string name="app_info" msgid="6856026610594615344">"ព័ត៌មានកម្មវិធី"</string>
- <string name="go_to_web" msgid="1106022723459948514">"ចូលទៅកាន់បណ្តាញ"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"ចូលទៅកម្មវិធីរុករកតាមអ៊ីនធឺណិត"</string>
<string name="mobile_data" msgid="7094582042819250762">"ទិន្នន័យទូរសព្ទចល័ត"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi បានបិទ"</string>
<string name="bt_is_off" msgid="2640685272289706392">"ប៊្លូធូសបានបិទ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index f4aad96..5e7e1e2 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -492,6 +492,11 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ಬ್ಲೂಟೂತ್"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"ಡ್ಯುಯಲ್ ಬಹು ಟೋನ್ ಆವರ್ತನೆ"</string>
<string name="stream_accessibility" msgid="301136219144385106">"ಪ್ರವೇಶಿಸುವಿಕೆ"</string>
+ <!-- no translation found for ring_toggle_title (3281244519428819576) -->
+ <skip />
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"ರಿಂಗ್"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"ವೈಬ್ರೇಟ್"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"ಮ್ಯೂಟ್"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. ಅನ್ಮ್ಯೂಟ್ ಮಾಡುವುದಕ್ಕಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. ಕಂಪನಕ್ಕೆ ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಪ್ರವೇಶಿಸುವಿಕೆ ಸೇವೆಗಳನ್ನು ಮ್ಯೂಟ್ ಮಾಡಬಹುದು."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. ಮ್ಯೂಟ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಪ್ರವೇಶಿಸುವಿಕೆ ಸೇವೆಗಳನ್ನು ಮ್ಯೂಟ್ ಮಾಡಬಹುದು."</string>
@@ -756,7 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"ತತ್ಕ್ಷಣ ಆಪ್ಗಳು"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"ತತ್ಕ್ಷಣ ಆಪ್ಗಳಿಗೆ ಸ್ಥಾಪನೆಯ ಅಗತ್ಯವಿಲ್ಲ."</string>
<string name="app_info" msgid="6856026610594615344">"ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ"</string>
- <string name="go_to_web" msgid="1106022723459948514">"ವೆಬ್ಗೆ ಹೋಗಿ"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"ಬ್ರೌಸರ್ಗೆ ಹೋಗಿ"</string>
<string name="mobile_data" msgid="7094582042819250762">"ಮೊಬೈಲ್ ಡೇಟಾ"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"ವೈ-ಫೈ ಆಫ್ ಆಗಿದೆ"</string>
<string name="bt_is_off" msgid="2640685272289706392">"ಬ್ಲೂಟೂತ್ ಆಫ್ ಆಗಿದೆ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 43d171c..d86c32f 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -494,6 +494,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"블루투스"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"듀얼 멀티 톤 주파수"</string>
<string name="stream_accessibility" msgid="301136219144385106">"접근성"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"통화"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"벨소리"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"진동"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"음소거"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. 탭하여 음소거를 해제하세요."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. 탭하여 진동으로 설정하세요. 접근성 서비스가 음소거될 수 있습니다."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. 탭하여 음소거로 설정하세요. 접근성 서비스가 음소거될 수 있습니다."</string>
@@ -758,7 +762,7 @@
<string name="instant_apps" msgid="6647570248119804907">"인스턴트 앱"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"인스턴트 앱은 설치가 필요하지 않습니다."</string>
<string name="app_info" msgid="6856026610594615344">"앱 정보"</string>
- <string name="go_to_web" msgid="1106022723459948514">"웹으로 이동"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"브라우저로 이동"</string>
<string name="mobile_data" msgid="7094582042819250762">"모바일 데이터"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi가 사용 중지됨"</string>
<string name="bt_is_off" msgid="2640685272289706392">"블루투스가 사용 중지됨"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 375d70d..e10e2b0 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Көп тондуу жыштык"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Атайын мүмкүнчүлүктөр"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Чалуулар"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Шыңгыратуу"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Дирилдөө"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Үнсүз"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Үнүн чыгаруу үчүн таптап коюңуз."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Дирилдөөгө коюу үчүн таптап коюңуз. Атайын мүмкүнчүлүктөр кызматынын үнүн өчүрүп койсо болот."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Үнүн өчүрүү үчүн таптап коюңуз. Атайын мүмкүнчүлүктөр кызматынын үнүн өчүрүп койсо болот."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Ыкчам ачылуучу колдонмолор"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Ыкчам ачылуучу колдонмолорду орнотуу талап кылынбайт."</string>
<string name="app_info" msgid="6856026610594615344">"Колдонмо тууралуу"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Интернетке өтүү"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Серепчиге өтүү"</string>
<string name="mobile_data" msgid="7094582042819250762">"Мобилдик Интернет"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi өчүк"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth өчүк"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 064609e..2587a20 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ບຣູທູດ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Dual multi tone frequency"</string>
<string name="stream_accessibility" msgid="301136219144385106">"ການຊ່ວຍເຂົ້າເຖິງ"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"ການໂທ"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"ເຕືອນດ້ວຍສຽງ"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"ສັ່ນເຕືອນ"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"ປິດ"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. ແຕະເພື່ອເຊົາປິດສຽງ."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. ແຕະເພື່ອຕັ້ງເປັນສັ່ນ. ບໍລິການຊ່ວຍເຂົ້າເຖິງອາດຖືກປິດສຽງໄວ້."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. ແຕະເພື່ອປິດສຽງ. ບໍລິການຊ່ວຍເຂົ້າເຖິງອາດຖືກປິດສຽງໄວ້."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"ອິນສະແຕນແອັບ"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"ອິນສະແຕນແອັບບໍ່ຈຳເປັນຕ້ອງມີການຕິດຕັ້ງ."</string>
<string name="app_info" msgid="6856026610594615344">"ຂໍ້ມູນແອັບ"</string>
- <string name="go_to_web" msgid="1106022723459948514">"ໄປທີ່ເວັບ"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"ໄປທີ່ໂປຣແກຣມທ່ອງເວັບ"</string>
<string name="mobile_data" msgid="7094582042819250762">"ອິນເຕີເນັດມືຖື"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi ປິດຢູ່"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth ປິດຢູ່"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index f44b930..1c97452 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -496,6 +496,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Dvigubas kelių tonų dažnis"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Pritaikymas neįgaliesiems"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Skambučiai"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Skambinti"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibruoti"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Nutildyti"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Palieskite, kad įjungtumėte garsą."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Palieskite, kad nustatytumėte vibravimą. Gali būti nutildytos pritaikymo neįgaliesiems paslaugos."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Palieskite, kad nutildytumėte. Gali būti nutildytos pritaikymo neįgaliesiems paslaugos."</string>
@@ -768,7 +772,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Akimirksniu įkeliamos programėlės"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Akimirksniu įkeliamų programėlių nereikia įdiegti."</string>
<string name="app_info" msgid="6856026610594615344">"Programos informacija"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Eiti į žiniatinklį"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Eiti į naršyklę"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobilieji duomenys"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"„Wi-Fi“ išjungtas"</string>
<string name="bt_is_off" msgid="2640685272289706392">"„Bluetooth“ išjungtas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 8c117b3..c14eb3f 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -494,6 +494,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Divtoņu daudzfrekvenču signalizācija"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Pieejamība"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Zvani"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Zvanīt"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrēt"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Izslēgt skaņu"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Pieskarieties, lai ieslēgtu skaņu."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Pieskarieties, lai iestatītu uz vibrozvanu. Var tikt izslēgti pieejamības pakalpojumu signāli."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Pieskarieties, lai izslēgtu skaņu. Var tikt izslēgti pieejamības pakalpojumu signāli."</string>
@@ -762,7 +766,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Tūlītējās lietotnes"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Tūlītējām lietotnēm nav nepieciešama instalēšana."</string>
<string name="app_info" msgid="6856026610594615344">"Lietotnes informācija"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Pāriet uz tīmekli"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Atvērt pārlūku"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobilie dati"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi ir izslēgts"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth ir izslēgts"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 195ddec..e30085c 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Двојна повеќетонска фреквенција"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Пристапност"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Повици"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Ѕвони"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Вибрации"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Исклучи звук"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Допрете за да вклучите звук."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Допрете за да поставите на вибрации. Можеби ќе се исклучи звукот на услугите за достапност."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Допрете за да исклучите звук. Можеби ќе се исклучи звукот на услугите за достапност."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Инстант апликации"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Инстант апликациите нема потреба да се инсталираат."</string>
<string name="app_info" msgid="6856026610594615344">"Информации за апликација"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Одете на интернет"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Одете на прелистувач"</string>
<string name="mobile_data" msgid="7094582042819250762">"Мобилен интернет"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi е исклучено"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth е исклучен"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 3eb67df..385331e 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -492,6 +492,11 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ബ്ലൂടൂത്ത്"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"ഡ്യുവൽ മൾട്ടി റ്റോൺ ഫ്രീക്വൻസി"</string>
<string name="stream_accessibility" msgid="301136219144385106">"ഉപയോഗസഹായി"</string>
+ <!-- no translation found for ring_toggle_title (3281244519428819576) -->
+ <skip />
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"റിംഗ് ചെയ്യുക"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"വൈബ്രേറ്റ് ചെയ്യുക"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"മ്യൂട്ട് ചെയ്യുക"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. അൺമ്യൂട്ടുചെയ്യുന്നതിന് ടാപ്പുചെയ്യുക."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. വൈബ്രേറ്റിലേക്ക് സജ്ജമാക്കുന്നതിന് ടാപ്പുചെയ്യുക. ഉപയോഗസഹായി സേവനങ്ങൾ മ്യൂട്ടുചെയ്യപ്പെട്ടേക്കാം."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. മ്യൂട്ടുചെയ്യുന്നതിന് ടാപ്പുചെയ്യുക. ഉപയോഗസഹായി സേവനങ്ങൾ മ്യൂട്ടുചെയ്യപ്പെട്ടേക്കാം."</string>
@@ -756,7 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"ഇൻസ്റ്റന്റ് ആപ്പ്"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"ഇൻസ്റ്റന്റ് ആപ്പിന് ഇൻസ്റ്റലേഷൻ ആവശ്യമില്ല."</string>
<string name="app_info" msgid="6856026610594615344">"ആപ്പ് വിവരം"</string>
- <string name="go_to_web" msgid="1106022723459948514">"വെബിൽ പോവുക"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"ബ്രൗസറിലേക്ക് പോവുക"</string>
<string name="mobile_data" msgid="7094582042819250762">"മൊബൈൽ ഡാറ്റ"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"വൈഫൈ ഓഫാണ്"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth ഓഫാണ്"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 7ecf683..480956c 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -490,6 +490,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Олон дууны давтамж"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Хүртээмж"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Дуудлага"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Хонх дуугаргах"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Чичиргэх"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Хаах"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Дууг нь нээхийн тулд товшино уу."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Чичиргээнд тохируулахын тулд товшино уу. Хүртээмжийн үйлчилгээний дууг хаасан."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Дууг нь хаахын тулд товшино уу. Хүртээмжийн үйлчилгээний дууг хаасан."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Шуурхай апп"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Шуурхай аппыг суулгах шаардлагагүй."</string>
<string name="app_info" msgid="6856026610594615344">"Апп-н мэдээлэл"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Вэбэд очих"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Хөтчид очих"</string>
<string name="mobile_data" msgid="7094582042819250762">"Мобайл дата"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi унтраалттай байна"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth унтраалттай байна"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index a0c9b2c..8a0d661 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -34,17 +34,14 @@
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"सूचना"</string>
<string name="battery_low_title" msgid="6456385927409742437">"बॅटरी कमी आहे"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> शिल्लक"</string>
- <!-- no translation found for battery_low_percent_format_saver_started (7879389868952879166) -->
- <skip />
+ <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> शिल्लक. बॅटरी सेव्हर चालू आहे."</string>
<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>
- <!-- no translation found for battery_saver_confirmation_title (2052100465684817154) -->
- <skip />
+ <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"बॅटरी सेव्हर सुरू करायचा का?"</string>
<string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"चालू करा"</string>
- <!-- no translation found for battery_saver_start_action (8187820911065797519) -->
- <skip />
+ <string name="battery_saver_start_action" msgid="8187820911065797519">"बॅटरी सेव्हर सुरू करा"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"सेटिंग्ज"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"वाय-फाय"</string>
<string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"स्वयं-फिरणारी स्क्रीन"</string>
@@ -402,11 +399,9 @@
<string name="user_remove_user_title" msgid="4681256956076895559">"वापरकर्त्यास काढायचे?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"या वापरकर्त्याचे सर्व अॅप्स आणि डेटा काढून टाकला जाईल."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"काढा"</string>
- <!-- no translation found for battery_saver_notification_title (8614079794522291840) -->
- <skip />
+ <string name="battery_saver_notification_title" msgid="8614079794522291840">"बॅटरी सेव्हर चालू आहे"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"कामगिरी आणि पार्श्वभूमीवरील डेटा कमी करते"</string>
- <!-- no translation found for battery_saver_notification_action_text (132118784269455533) -->
- <skip />
+ <string name="battery_saver_notification_action_text" msgid="132118784269455533">"बॅटरी सेव्हर बंद करा"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> आपल्या स्क्रीनवर प्रदर्शित होणारी प्रत्येक गोष्ट कॅप्चर करणे प्रारंभ करेल."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"पुन्हा दर्शवू नका"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"सर्व साफ करा"</string>
@@ -497,6 +492,11 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ब्लूटूथ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"दुहेरी एकाधिक टोन वारंंवारता"</string>
<string name="stream_accessibility" msgid="301136219144385106">"प्रवेशयोग्यता"</string>
+ <!-- no translation found for ring_toggle_title (3281244519428819576) -->
+ <skip />
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"रिंग करा"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"कंपन"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"म्युट करा"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. सशब्द करण्यासाठी टॅप करा."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. कंपन सेट करण्यासाठी टॅप करा. प्रवेशयोग्यता सेवा नि:शब्द केल्या जाऊ शकतात."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. नि:शब्द करण्यासाठी टॅप करा. प्रवेशक्षमता सेवा नि:शब्द केल्या जाऊ शकतात."</string>
@@ -761,7 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"इन्सटंट अॅप्स"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"इन्सटंट अॅप्सना स्थापनेची आवश्यकता नसते."</string>
<string name="app_info" msgid="6856026610594615344">"अॅप माहिती"</string>
- <string name="go_to_web" msgid="1106022723459948514">"वेबवर जा"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"ब्राउझरवर जा"</string>
<string name="mobile_data" msgid="7094582042819250762">"मोबाइल डेटा"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"वाय-फाय बंद आहे"</string>
<string name="bt_is_off" msgid="2640685272289706392">"ब्लूटूथ बंद आहे"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 78708f7..e074875 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Multifrekuensi dwinada"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Kebolehaksesan"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Panggilan"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Dering"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Getar"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Redam"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Ketik untuk menyahredam."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Ketik untuk menetapkan pada getar. Perkhidmatan kebolehaksesan mungkin diredamkan."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Ketik untuk meredam. Perkhidmatan kebolehaksesan mungkin diredamkan."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Apl Segera"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Apl segera tidak memerlukan pemasangan."</string>
<string name="app_info" msgid="6856026610594615344">"Maklumat apl"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Pergi ke web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Pergi ke penyemak imbas"</string>
<string name="mobile_data" msgid="7094582042819250762">"Data mudah alih"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi dimatikan"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth dimatikan"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index a3ec0db..22375b4 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -492,6 +492,9 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ဘလူးတုသ်"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"နှစ်လိုင်းပေါင်း အသံပေါင်းစုံ ကြိမ်နှုန်း"</string>
<string name="stream_accessibility" msgid="301136219144385106">"အများသုံးစွဲနိုင်မှု"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"အသံမြည်သည်"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"တုန်ခါသည်"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"အသံတိတ်သည်"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s။ အသံပြန်ဖွင့်ရန် တို့ပါ။"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s။ တုန်ခါမှုကို သတ်မှတ်ရန် တို့ပါ။ အများသုံးစွဲနိုင်မှု ဝန်ဆောင်မှုများကို အသံပိတ်ထားနိုင်ပါသည်။"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s။ အသံပိတ်ရန် တို့ပါ။ အများသုံးစွဲနိုင်မှု ဝန်ဆောင်မှုများကို အသံပိတ်ထားနိုင်ပါသည်။"</string>
@@ -756,7 +759,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"ချက်ခြင်းသုံးအက်ပ်များကို ထည့်သွင်းစရာမလိုပါ။"</string>
<string name="app_info" msgid="6856026610594615344">"အက်ပ်အချက်အလက်"</string>
- <string name="go_to_web" msgid="1106022723459948514">"ဝဘ်သို့ သွားရန်"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"ဘရောင်ဇာသို့ သွားပါ"</string>
<string name="mobile_data" msgid="7094582042819250762">"မိုဘိုင်းဒေတာ"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi ကို ပိတ်ထားသည်"</string>
<string name="bt_is_off" msgid="2640685272289706392">"ဘလူးတုသ်ကို ပိတ်ထားသည်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index bebfad5..661df03 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"DTMF (dual-tone multi-frequency)"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Tilgjengelighet"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Anrop"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Ring"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrer"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Ignorer"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Trykk for å slå på lyden."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Trykk for å angi vibrasjon. Lyden kan bli slått av for tilgjengelighetstjenestene."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Trykk for å slå av lyden. Lyden kan bli slått av for tilgjengelighetstjenestene."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Du trenger ikke å installere instant-apper."</string>
<string name="app_info" msgid="6856026610594615344">"Info om appen"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Gå til nettstedet"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Gå til nettleser"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobildata"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi er av"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth er av"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index bfe8f07..dce9cc4 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -492,6 +492,11 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ब्लुटुथ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"दोहोरो बहु टोनको फ्रिक्वेन्सी"</string>
<string name="stream_accessibility" msgid="301136219144385106">"पहुँच"</string>
+ <!-- no translation found for ring_toggle_title (3281244519428819576) -->
+ <skip />
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"घन्टी"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"कम्पन"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"म्युट गर्नुहोस्"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s। अनम्यूट गर्नका लागि ट्याप गर्नुहोस्।"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s। कम्पनमा सेट गर्नका लागि ट्याप गर्नुहोस्। पहुँच सम्बन्धी सेवाहरू म्यूट हुन सक्छन्।"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s। म्यूट गर्नका लागि ट्याप गर्नुहोस्। पहुँच सम्बन्धी सेवाहरू म्यूट हुन सक्छन्।"</string>
@@ -756,7 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"तात्कालिक अनुप्रयोगहरू"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"तात्कालिक अनुप्रयोगहरूलाई स्थापना गर्नु पर्दैन|"</string>
<string name="app_info" msgid="6856026610594615344">"अनुप्रयोगका बारे जानकारी"</string>
- <string name="go_to_web" msgid="1106022723459948514">"वेबमा जानुहोस्"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"ब्राउजरमा जानुहोस्"</string>
<string name="mobile_data" msgid="7094582042819250762">"मोबाइल डेटा"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi‑Fi निष्क्रिय छ"</string>
<string name="bt_is_off" msgid="2640685272289706392">"ब्लुटुथ निष्क्रिय छ"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 98ad14f..ca0fa07 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -492,6 +492,9 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Frequentie voor tweevoudige multitoon"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Toegankelijkheid"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Bellen"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Trillen"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Dempen"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tik om dempen op te heffen."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tik om in te stellen op trillen. Toegankelijkheidsservices kunnen zijn gedempt."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tik om te dempen. Toegankelijkheidsservices kunnen zijn gedempt."</string>
@@ -756,7 +759,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant-apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Instant-apps hoeven niet te worden geïnstalleerd."</string>
<string name="app_info" msgid="6856026610594615344">"App-info"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Ga naar internet"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Ga naar browser"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobiele data"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wifi is uitgeschakeld"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth is uitgeschakeld"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 8706298..43503d6 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -34,17 +34,14 @@
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"ਸੂਚਨਾਵਾਂ"</string>
<string name="battery_low_title" msgid="6456385927409742437">"ਬੈਟਰੀ ਘੱਟ ਹੈ"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> ਬਾਕੀ"</string>
- <!-- no translation found for battery_low_percent_format_saver_started (7879389868952879166) -->
- <skip />
+ <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> ਬਾਕੀ। ਬੈਟਰੀ ਸੇਵਰ ਚਾਲੂ ਹੈ।"</string>
<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>
- <!-- no translation found for battery_saver_confirmation_title (2052100465684817154) -->
- <skip />
+ <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"ਕੀ ਬੈਟਰੀ ਸੇਵਰ ਚਾਲੂ ਕਰਨਾ ਹੈ?"</string>
<string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"ਚਾਲੂ ਕਰੋ"</string>
- <!-- no translation found for battery_saver_start_action (8187820911065797519) -->
- <skip />
+ <string name="battery_saver_start_action" msgid="8187820911065797519">"ਬੈਟਰੀ ਸੇਵਰ ਚਾਲੂ ਕਰੋ"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"ਸੈਟਿੰਗਾਂ"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"ਵਾਈ-ਫਾਈ"</string>
<string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"ਸਕ੍ਰੀਨ ਆਪਣੇ-ਆਪ ਘੁੰਮਾਓ"</string>
@@ -402,11 +399,9 @@
<string name="user_remove_user_title" msgid="4681256956076895559">"ਕੀ ਵਰਤੋਂਕਾਰ ਹਟਾਉਣਾ ਹੈ?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"ਇਸ ਉਪਭੋਗਤਾ ਦੇ ਸਾਰੇ ਐਪਸ ਅਤੇ ਡਾਟਾ ਮਿਟਾ ਦਿੱਤਾ ਜਾਏਗਾ।"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"ਹਟਾਓ"</string>
- <!-- no translation found for battery_saver_notification_title (8614079794522291840) -->
- <skip />
+ <string name="battery_saver_notification_title" msgid="8614079794522291840">"ਬੈਟਰੀ ਸੇਵਰ ਚਾਲੂ ਹੈ"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"ਪ੍ਰਦਰਸ਼ਨ ਅਤੇ ਪਿਛੋਕੜ ਡਾਟਾ ਘੱਟ ਕਰਦਾ ਹੈ"</string>
- <!-- no translation found for battery_saver_notification_action_text (132118784269455533) -->
- <skip />
+ <string name="battery_saver_notification_action_text" msgid="132118784269455533">"ਬੈਟਰੀ ਸੇਵਰ ਬੰਦ ਕਰੋ"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਉਹ ਸਭ ਕੁਝ ਕੈਪਚਰ ਕਰਨਾ ਸ਼ੁਰੂ ਕਰ ਦੇਵੇਗਾ, ਜੋ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਤੇ ਡਿਸਪਲੇ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"ਦੁਬਾਰਾ ਨਾ ਦਿਖਾਓ"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
@@ -497,6 +492,11 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"ਬਲੂਟੁੱਥ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"ਦੂਹਰੀ ਮਲਟੀ ਟੋਨ ਆਵਰਤੀ"</string>
<string name="stream_accessibility" msgid="301136219144385106">"ਪਹੁੰਚਯੋਗਤਾ"</string>
+ <!-- no translation found for ring_toggle_title (3281244519428819576) -->
+ <skip />
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"ਘੰਟੀ"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"ਥਰਥਰਾਹਟ"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"ਮਿਊਟ"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s। ਅਣਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s। ਥਰਥਰਾਹਟ ਸੈੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ। ਪਹੁੰਚਯੋਗਤਾ ਸੇਵਾਵਾਂ ਮਿਊਟ ਹੋ ਸਕਦੀਆਂ ਹਨ।"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s। ਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ। ਪਹੁੰਚਯੋਗਤਾ ਸੇਵਾਵਾਂ ਮਿਊਟ ਹੋ ਸਕਦੀਆਂ ਹਨ।"</string>
@@ -761,7 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"ਤਤਕਾਲ ਐਪਾਂ"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"ਤਤਕਾਲ ਐਪਾਂ ਨੂੰ ਸਥਾਪਨਾ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ।"</string>
<string name="app_info" msgid="6856026610594615344">"ਐਪ ਜਾਣਕਾਰੀ"</string>
- <string name="go_to_web" msgid="1106022723459948514">"ਵੈੱਬ \'ਤੇ ਜਾਓ"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"ਬ੍ਰਾਊਜ਼ਰ \'ਤੇ ਜਾਓ"</string>
<string name="mobile_data" msgid="7094582042819250762">"ਮੋਬਾਈਲ ਡਾਟਾ"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"ਵਾਈ-ਫਾਈ ਬੰਦ ਹੈ"</string>
<string name="bt_is_off" msgid="2640685272289706392">"ਬਲੂਟੁੱਥ ਬੰਦ ਹੈ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 56a8e59..e567e6ba 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -496,6 +496,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"DTMF"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Ułatwienia dostępu"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Połączenia"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Dzwonek"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Wibracje"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Wyciszenie"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Kliknij, by wyłączyć wyciszenie."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Kliknij, by włączyć wibracje. Ułatwienia dostępu mogą być wyciszone."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Kliknij, by wyciszyć. Ułatwienia dostępu mogą być wyciszone."</string>
@@ -768,7 +772,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Aplikacje błyskawiczne"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Aplikacji błyskawicznych nie trzeba instalować."</string>
<string name="app_info" msgid="6856026610594615344">"O aplikacji"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Wejdź na stronę internetową"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Otwórz przeglądarkę"</string>
<string name="mobile_data" msgid="7094582042819250762">"Komórkowa transmisja danych"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi jest wyłączone"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth jest wyłączony"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index a7c3af1..61aa901 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -494,6 +494,9 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Multifrequência de dois tons"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Acessibilidade"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Tocar"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrar"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Ignorar"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Toque para ativar o som."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Toque para configurar para vibrar. É possível que os serviços de acessibilidade sejam silenciados."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Toque para silenciar. É possível que os serviços de acessibilidade sejam silenciados."</string>
@@ -758,7 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Os Instant Apps não requerem instalação."</string>
<string name="app_info" msgid="6856026610594615344">"Informações do app"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Acessar a Web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Abrir o navegador"</string>
<string name="mobile_data" msgid="7094582042819250762">"Dados móveis"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"O Wi-Fi está desativado"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth desativado"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index b5b3475..681d3e4 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Multifrequência de duas tonalidades"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Acessibilidade"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Chamadas"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Toque"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrar"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Desativar som"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Toque para reativar o som."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Toque para ativar a vibração. Os serviços de acessibilidade podem ser silenciados."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Toque para desativar o som. Os serviços de acessibilidade podem ser silenciados."</string>
@@ -756,8 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Aplicações instantâneas"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"As Aplicações instantâneas não requerem instalação."</string>
<string name="app_info" msgid="6856026610594615344">"Informações da aplicação"</string>
- <!-- no translation found for go_to_web (2650669128861626071) -->
- <skip />
+ <string name="go_to_web" msgid="2650669128861626071">"Ir para o navegador"</string>
<string name="mobile_data" msgid="7094582042819250762">"Dados móveis"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi desativado"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth desativado"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index a7c3af1..61aa901 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -494,6 +494,9 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Multifrequência de dois tons"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Acessibilidade"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Tocar"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrar"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Ignorar"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Toque para ativar o som."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Toque para configurar para vibrar. É possível que os serviços de acessibilidade sejam silenciados."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Toque para silenciar. É possível que os serviços de acessibilidade sejam silenciados."</string>
@@ -758,7 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Os Instant Apps não requerem instalação."</string>
<string name="app_info" msgid="6856026610594615344">"Informações do app"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Acessar a Web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Abrir o navegador"</string>
<string name="mobile_data" msgid="7094582042819250762">"Dados móveis"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"O Wi-Fi está desativado"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth desativado"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 4392efe..2a0744e 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -496,6 +496,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Frecvență tonuri multiple duale"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Accesibilitate"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Apeluri"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Sonerie"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrații"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Blocați"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Atingeți pentru a activa sunetul."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Atingeți pentru a seta vibrarea. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Atingeți pentru a dezactiva sunetul. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string>
@@ -764,7 +768,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Aplicații instantanee"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Aplicațiile instantanee nu necesită instalare."</string>
<string name="app_info" msgid="6856026610594615344">"Informații despre aplicație"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Accesați pe web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Accesați browserul"</string>
<string name="mobile_data" msgid="7094582042819250762">"Date mobile"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Conexiunea Wi-Fi este dezactivată"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Funcția Bluetooth este dezactivată"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index fd1d3be..ac4f5fe 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -498,6 +498,9 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Тональный набор"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Специальные возможности"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Со звуком"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Вибрация"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Без звука"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Нажмите, чтобы включить звук."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Нажмите, чтобы включить вибрацию. Специальные возможности могут прекратить работу."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Нажмите, чтобы выключить звук. Специальные возможности могут прекратить работу."</string>
@@ -770,7 +773,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Приложения с мгновенным запуском"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Приложения с мгновенным запуском не требуется устанавливать."</string>
<string name="app_info" msgid="6856026610594615344">"О приложении"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Перейти в браузер"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Перейти в браузер"</string>
<string name="mobile_data" msgid="7094582042819250762">"Моб. Интернет"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Модуль Wi-Fi отключен"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Модуль Bluetooth отключен"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index fd636ad..12f4838 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -492,6 +492,9 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"බ්ලූටූත්"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"ද්විත්ව බහු ස්වර සංඛ්යාතය"</string>
<string name="stream_accessibility" msgid="301136219144385106">"ප්රවේශ්යතාව"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"නාද කරන්න"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"කම්පනය කරන්න"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"නිහඬ කරන්න"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. නිහඬ කිරීම ඉවත් කිරීමට තට්ටු කරන්න."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. කම්පනය කිරීමට තට්ටු කරන්න. ප්රවේශ්යතා සේවා නිහඬ කළ හැකිය."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. නිහඬ කිරීමට තට්ටු කරන්න. ප්රවේශ්යතා සේවා නිහඬ කළ හැකිය."</string>
@@ -756,7 +759,7 @@
<string name="instant_apps" msgid="6647570248119804907">"ක්ෂණික යෙදුම්"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"ක්ෂණික යෙදුම් ස්ථාපනය කිරීම අවශ්ය නොවේ."</string>
<string name="app_info" msgid="6856026610594615344">"යෙදුම් තොරතුරු"</string>
- <string name="go_to_web" msgid="1106022723459948514">"වෙබය වෙත යන්න"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"බ්රවුසරය වෙත යන්න"</string>
<string name="mobile_data" msgid="7094582042819250762">"ජංගම දත්ත"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi ක්රියා විරහිතයි"</string>
<string name="bt_is_off" msgid="2640685272289706392">"බ්ලූටූත් ක්රියා විරහිතයි"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index b7f5445..694695c 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -498,6 +498,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Dvojtónová multifrekvencia"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Dostupnosť"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Hovory"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Prezvoniť"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibrovať"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Vypnúť zvuk"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Klepnutím zapnite zvuk."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Klepnutím aktivujte režim vibrovania. Služby dostupnosti je možné stlmiť."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Klepnutím vypnite zvuk. Služby dostupnosti je možné stlmiť."</string>
@@ -770,7 +774,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Okamžité aplikácie"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Okamžité aplikácie nevyžadujú inštaláciu."</string>
<string name="app_info" msgid="6856026610594615344">"Info o aplikácii"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Prejsť na internet"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Otvoriť prehliadač"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobilné dáta"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Pripojenie Wi‑Fi je vypnuté"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Rozhranie Bluetooth je vypnuté"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 5a00c53..10d0104 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -498,6 +498,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Dvojna večtonska frekvenca"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Funkcije za ljudi s posebnimi potrebami"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Klici"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Zvonjenje"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibriranje"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Utišano"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Dotaknite se, če želite vklopiti zvok."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Dotaknite se, če želite nastaviti vibriranje. V storitvah za ljudi s posebnimi potrebami bo morda izklopljen zvok."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Dotaknite se, če želite izklopiti zvok. V storitvah za ljudi s posebnimi potrebami bo morda izklopljen zvok."</string>
@@ -770,7 +774,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Nenamestljive aplikacije"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Nenamestljivih aplikacij ni treba namestiti."</string>
<string name="app_info" msgid="6856026610594615344">"Podatki o aplikaciji"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Pojdi v splet"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Odpri brskalnik"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobilni podatki"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi je izklopljen"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth je izklopljen"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 801ee90..665177f 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Frekuenca e dyfishtë me shumë tone"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Qasshmëria"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Telefonatat"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Bjeri ziles"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Dridhje"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Pa zë"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Trokit për të aktivizuar."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Trokit për ta caktuar te dridhja. Shërbimet e qasshmërisë mund të çaktivizohen."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Trokit për të çaktivizuar. Shërbimet e qasshmërisë mund të çaktivizohen."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Aplikacionet e çastit"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Aplikacionet e çastit nuk kërkojnë instalim."</string>
<string name="app_info" msgid="6856026610594615344">"Informacioni mbi aplikacionin"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Shko në ueb"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Shko te shfletuesi"</string>
<string name="mobile_data" msgid="7094582042819250762">"Të dhënat celulare"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi është joaktiv"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth-i është joaktiv"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 7a76d40..cef0f693 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -494,6 +494,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Вишеструка фреквенција дуалног тона"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Приступачност"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Позиви"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Активирај звоно"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Вибрирај"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Искључи звук"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Додирните да бисте укључили звук."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Додирните да бисте подесили на вибрацију. Звук услуга приступачности ће можда бити искључен."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Додирните да бисте искључили звук. Звук услуга приступачности ће можда бити искључен."</string>
@@ -762,7 +766,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Инстант апликације"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Инстант апликације не захтевају инсталацију."</string>
<string name="app_info" msgid="6856026610594615344">"Информације о апликацији"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Иди на веб"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Иди на прегледач"</string>
<string name="mobile_data" msgid="7094582042819250762">"Мобилни подаци"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi је искључен"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth је искључен"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 9b978e2..c8f8db4 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Tonval"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Tillgänglighet"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Samtal"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Ringsignal"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Vibration"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Dölj"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Tryck här om du vill slå på ljudet."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tryck här om du vill sätta på vibrationen. Tillgänglighetstjänster kanske inaktiveras."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Tryck här om du vill stänga av ljudet. Tillgänglighetstjänsterna kanske inaktiveras."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Snabbappar behöver inte installeras."</string>
<string name="app_info" msgid="6856026610594615344">"Info om appen"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Öppna webbplatsen"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Öppna webbläsaren"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobildata"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi är inaktiverat"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth är inaktiverat"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index aa7c3f7..8fa370d 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Masafa ya ishara ya kampuni ya simu"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Zana za walio na matatizo ya kuona au kusikia"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Simu"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Piga"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Tetema"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Zima sauti"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Gusa ili urejeshe."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Gusa ili uweke mtetemo. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Gusa ili ukomeshe. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Programu Zinazofunguka Papo Hapo"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Huhitaji kusakinisha programu zinazofunguka papo hapo."</string>
<string name="app_info" msgid="6856026610594615344">"Maelezo ya programu"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Nenda kwenye wavuti"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Tumia kivinjari"</string>
<string name="mobile_data" msgid="7094582042819250762">"Data ya mtandao wa simu"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi imezimwa"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth imezimwa"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 4278d36..6568381 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -34,17 +34,14 @@
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"அறிவிப்புகள்"</string>
<string name="battery_low_title" msgid="6456385927409742437">"பேட்டரி குறைவு"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> உள்ளது"</string>
- <!-- no translation found for battery_low_percent_format_saver_started (7879389868952879166) -->
- <skip />
+ <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> மீதமுள்ளது. பேட்டரி சேமிப்பான் ஆன் செய்யப்பட்டுள்ளது."</string>
<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>
- <!-- no translation found for battery_saver_confirmation_title (2052100465684817154) -->
- <skip />
+ <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"பேட்டரி சேமிப்பானை ஆன் செய்யவா?"</string>
<string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"இயக்கு"</string>
- <!-- no translation found for battery_saver_start_action (8187820911065797519) -->
- <skip />
+ <string name="battery_saver_start_action" msgid="8187820911065797519">"பேட்டரி சேமிப்பானை ஆன் செய்"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"அமைப்பு"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"வைஃபை"</string>
<string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"திரையைத் தானாகச் சுழற்று"</string>
@@ -402,11 +399,9 @@
<string name="user_remove_user_title" msgid="4681256956076895559">"பயனரை அகற்றவா?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"இந்தப் பயனரின் எல்லா பயன்பாடுகளும் தரவும் நீக்கப்படும்."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"அகற்று"</string>
- <!-- no translation found for battery_saver_notification_title (8614079794522291840) -->
- <skip />
+ <string name="battery_saver_notification_title" msgid="8614079794522291840">"பேட்டரி சேமிப்பான் ஆன் செய்யப்பட்டுள்ளது"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"செயல்திறனையும் பின்புலத் தரவையும் குறைக்கிறது"</string>
- <!-- no translation found for battery_saver_notification_action_text (132118784269455533) -->
- <skip />
+ <string name="battery_saver_notification_action_text" msgid="132118784269455533">"பேட்டரி சேமிப்பானை ஆஃப் செய்"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"திரையில் காட்டப்படும் அனைத்தையும் <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> படமெடுக்கும்."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"மீண்டும் காட்டாதே"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"எல்லாவற்றையும் அழி"</string>
@@ -497,6 +492,11 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"புளூடூத்"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"டூயல் டோன் மல்டி ஃப்ரீக்வென்சி"</string>
<string name="stream_accessibility" msgid="301136219144385106">"அணுகல்தன்மை"</string>
+ <!-- no translation found for ring_toggle_title (3281244519428819576) -->
+ <skip />
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"ஒலி"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"அதிர்வு"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"அமைதி"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. ஒலி இயக்க, தட்டவும்."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. அதிர்விற்கு அமைக்க, தட்டவும். அணுகல்தன்மை சேவைகள் ஒலியடக்கப்படக்கூடும்."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. ஒலியடக்க, தட்டவும். அணுகல்தன்மை சேவைகள் ஒலியடக்கப்படக்கூடும்."</string>
@@ -761,7 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"இன்ஸ்டண்ட் பயன்பாடுகள்"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"இன்ஸ்டண்ட் பயன்பாடுகளுக்கு நிறுவல் தேவையில்லை."</string>
<string name="app_info" msgid="6856026610594615344">"ஆப்ஸ் தகவல்"</string>
- <string name="go_to_web" msgid="1106022723459948514">"இணையத்திற்குச் செல்"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"உலாவிக்குச் செல்"</string>
<string name="mobile_data" msgid="7094582042819250762">"மொபைல் டேட்டா"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"வைஃபை முடக்கத்தில் உள்ளது"</string>
<string name="bt_is_off" msgid="2640685272289706392">"புளூடூத் முடக்கத்தில் உள்ளது"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 60fe59a..fa7cb09 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -34,17 +34,14 @@
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"నోటిఫికేషన్లు"</string>
<string name="battery_low_title" msgid="6456385927409742437">"బ్యాటరీ తక్కువగా ఉంది"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> మిగిలి ఉంది"</string>
- <!-- no translation found for battery_low_percent_format_saver_started (7879389868952879166) -->
- <skip />
+ <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> మిగిలి ఉంది. బ్యాటరీ సేవర్ ఆన్లో ఉంది."</string>
<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>
- <!-- no translation found for battery_saver_confirmation_title (2052100465684817154) -->
- <skip />
+ <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"బ్యాటరీ సేవర్ను ఆన్ చేయాలా?"</string>
<string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"ఆన్ చేయి"</string>
- <!-- no translation found for battery_saver_start_action (8187820911065797519) -->
- <skip />
+ <string name="battery_saver_start_action" msgid="8187820911065797519">"బ్యాటరీ సేవర్ను ఆన్ చేయండి"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"సెట్టింగ్లు"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"స్క్రీన్ను స్వయంచాలకంగా తిప్పండి"</string>
@@ -402,11 +399,9 @@
<string name="user_remove_user_title" msgid="4681256956076895559">"వినియోగదారుని తీసివేయాలా?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"ఈ వినియోగదారుకు సంబంధించిన అన్ని అనువర్తనాలు మరియు డేటా తొలగించబడతాయి."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"తీసివేయి"</string>
- <!-- no translation found for battery_saver_notification_title (8614079794522291840) -->
- <skip />
+ <string name="battery_saver_notification_title" msgid="8614079794522291840">"బ్యాటరీ సేవర్ ఆన్లో ఉంది"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"పనితీరుని మరియు నేపథ్య డేటాను తగ్గిస్తుంది"</string>
- <!-- no translation found for battery_saver_notification_action_text (132118784269455533) -->
- <skip />
+ <string name="battery_saver_notification_action_text" msgid="132118784269455533">"బ్యాటరీ సేవర్ను ఆఫ్ చేయండి"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> మీ స్క్రీన్పై కనిపించే ప్రతిదాన్ని క్యాప్చర్ చేయడం ప్రారంభిస్తుంది."</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"మళ్లీ చూపవద్దు"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"అన్నీ క్లియర్ చేయండి"</string>
@@ -497,6 +492,11 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"బ్లూటూత్"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"డ్యూయల్ మల్టీ టోన్ ఫ్రీక్వెన్సీ"</string>
<string name="stream_accessibility" msgid="301136219144385106">"యాక్సెస్ సామర్థ్యం"</string>
+ <!-- no translation found for ring_toggle_title (3281244519428819576) -->
+ <skip />
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"రింగ్"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"వైబ్రేట్"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"మ్యూట్"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. అన్మ్యూట్ చేయడానికి నొక్కండి."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. వైబ్రేషన్కు సెట్ చేయడానికి నొక్కండి. యాక్సెస్ సామర్థ్య సేవలు మ్యూట్ చేయబడవచ్చు."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. మ్యూట్ చేయడానికి నొక్కండి. యాక్సెస్ సామర్థ్య సేవలు మ్యూట్ చేయబడవచ్చు."</string>
@@ -761,7 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"తక్షణ అనువర్తనాలు"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"తక్షణ అనువర్తనాలకు ఇన్స్టాలేషన్ అవసరం లేదు."</string>
<string name="app_info" msgid="6856026610594615344">"యాప్ సమాచారం"</string>
- <string name="go_to_web" msgid="1106022723459948514">"వెబ్కు వెళ్లు"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"బ్రౌజర్కు వెళ్లండి"</string>
<string name="mobile_data" msgid="7094582042819250762">"మొబైల్ డేటా"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi ఆఫ్లో ఉంది"</string>
<string name="bt_is_off" msgid="2640685272289706392">"బ్లూటూత్ ఆఫ్లో ఉంది"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 3dc4513..d43efdd 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"บลูทูธ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"การส่งสัญญาณเสียงแบบ 2 เสียงพร้อมกัน"</string>
<string name="stream_accessibility" msgid="301136219144385106">"การเข้าถึง"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"การโทร"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"ทำให้ส่งเสียง"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"สั่น"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"ปิดเสียง"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s แตะเพื่อเปิดเสียง"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s แตะเพื่อตั้งค่าให้สั่น อาจมีการปิดเสียงบริการการเข้าถึง"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s แตะเพื่อปิดเสียง อาจมีการปิดเสียงบริการการเข้าถึง"</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant App"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Instant Apps ไม่ต้องใช้การติดตั้ง"</string>
<string name="app_info" msgid="6856026610594615344">"ข้อมูลแอป"</string>
- <string name="go_to_web" msgid="1106022723459948514">"ไปที่เว็บ"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"ไปที่เบราว์เซอร์"</string>
<string name="mobile_data" msgid="7094582042819250762">"ข้อมูลมือถือ"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi ปิดอยู่"</string>
<string name="bt_is_off" msgid="2640685272289706392">"บลูทูธปิดอยู่"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index f8f9159..4aa9ca4 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Dual multi tone frequency"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Pagiging Naa-access"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Mga Tawag"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Ipa-ring"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"I-vibrate"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"I-mute"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. I-tap upang i-unmute."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. I-tap upang itakda na mag-vibrate. Maaaring i-mute ang mga serbisyo sa Accessibility."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. I-tap upang i-mute. Maaaring i-mute ang mga serbisyo sa Accessibility."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Hindi kailangang i-install ang mga instant na app."</string>
<string name="app_info" msgid="6856026610594615344">"Impormasyon ng app"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Pumunta sa web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Pumunta sa browser"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobile data"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Naka-off ang Wi-Fi"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Naka-off ang Bluetooth"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 2499645..d028fdd 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Çift ton çoklu frekans"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Erişilebilirlik"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Çağrılar"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Zili çaldır"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Titreşim"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Sesi kapat"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Sesi açmak için dokunun."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Titreşime ayarlamak için dokunun. Erişilebilirlik hizmetlerinin sesi kapatılabilir."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Sesi kapatmak için dokunun. Erişilebilirlik hizmetlerinin sesi kapatılabilir."</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Hazır Uygulamalar"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Hazır uygulamaların yüklenmesi gerekmez."</string>
<string name="app_info" msgid="6856026610594615344">"Uygulama bilgileri"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Web\'e git"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Tarayıcıya git"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobil veriler"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Kablosuz bağlantı kapalı"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth kapalı"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 95cd2d0..8fa1b72 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -498,6 +498,9 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Двотональний багаточастотний аналоговий сигнал"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Спеціальні можливості"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Дзвінок"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Вібросигнал"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Без звуку"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Торкніться, щоб увімкнути звук."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Торкніться, щоб налаштувати вібросигнал. Спеціальні можливості може бути вимкнено."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Торкніться, щоб вимкнути звук. Спеціальні можливості може бути вимкнено."</string>
@@ -770,8 +773,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Додатки з миттєвим запуском"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Додатки з миттєвим запуском не потрібно встановлювати."</string>
<string name="app_info" msgid="6856026610594615344">"Про додаток"</string>
- <!-- no translation found for go_to_web (2650669128861626071) -->
- <skip />
+ <string name="go_to_web" msgid="2650669128861626071">"Веб-переглядач"</string>
<string name="mobile_data" msgid="7094582042819250762">"Мобільний трафік"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi вимкнено"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth вимкнено"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 04bd340..73fd5886 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -34,17 +34,14 @@
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"اطلاعات"</string>
<string name="battery_low_title" msgid="6456385927409742437">"بیٹری کم ہے"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی ہے"</string>
- <!-- no translation found for battery_low_percent_format_saver_started (7879389868952879166) -->
- <skip />
+ <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> باقی ہے۔ بیٹری سیور آن ہے۔"</string>
<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>
- <!-- no translation found for battery_saver_confirmation_title (2052100465684817154) -->
- <skip />
+ <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"بیٹری سیور آن کریں؟"</string>
<string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"آن کریں"</string>
- <!-- no translation found for battery_saver_start_action (8187820911065797519) -->
- <skip />
+ <string name="battery_saver_start_action" msgid="8187820911065797519">"بیٹری سیور آن کریں"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"ترتیبات"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"سکرین کو خودکار طور پر گھمائیں"</string>
@@ -402,11 +399,9 @@
<string name="user_remove_user_title" msgid="4681256956076895559">"صارف کو ہٹائیں؟"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"اس صارف کی سبھی ایپس اور ڈیٹا حذف کر دیا جائے گا۔"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"ہٹائیں"</string>
- <!-- no translation found for battery_saver_notification_title (8614079794522291840) -->
- <skip />
+ <string name="battery_saver_notification_title" msgid="8614079794522291840">"بیٹری سیور آن ہے"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"کارکردگی اور پس منظر کا ڈیٹا کم کر دیتا ہے"</string>
- <!-- no translation found for battery_saver_notification_action_text (132118784269455533) -->
- <skip />
+ <string name="battery_saver_notification_action_text" msgid="132118784269455533">"بیٹری سیور آف کریں"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> آپ کی اسکرین پر ڈسپلے ہونے والی ہر چیز کو کیپچر کرنا شروع کر دیگی۔"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"دوبارہ نہ دکھائیں"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"سبھی کو صاف کریں"</string>
@@ -497,6 +492,11 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"بلوٹوتھ"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"دوہری ملٹی ٹون فریکوئنسی"</string>
<string name="stream_accessibility" msgid="301136219144385106">"ایکسیسبیلٹی"</string>
+ <!-- no translation found for ring_toggle_title (3281244519428819576) -->
+ <skip />
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"رِنگ کریں"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"وائبریٹ"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"خاموش کریں"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s۔ آواز چالو کرنے کیلئے تھپتھپائیں۔"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s۔ ارتعاش پر سیٹ کرنے کیلئے تھپتھپائیں۔ ایکسیسبیلٹی سروسز شاید خاموش ہوں۔"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s۔ خاموش کرنے کیلئے تھپتھپائیں۔ ایکسیسبیلٹی سروسز شاید خاموش ہوں۔"</string>
@@ -761,7 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"فوری ایپس"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"فوری ایپس کو انسٹالیشن کی ضرورت نہیں ہے۔"</string>
<string name="app_info" msgid="6856026610594615344">"ایپ کی معلومات"</string>
- <string name="go_to_web" msgid="1106022723459948514">"ویب پر جائیں"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"براؤزر پر جائیں"</string>
<string name="mobile_data" msgid="7094582042819250762">"موبائل ڈیٹا"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi آف ہے"</string>
<string name="bt_is_off" msgid="2640685272289706392">"بلوٹوتھ آف ہے"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index e66477f..55eb4fb 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -494,6 +494,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Ikkitali ko‘pchastotali ovoz"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Maxsus imkoniyatlar"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Chaqiruvlar"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Jiringlatish"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Tebranish"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Ovozsiz"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Ovozini yoqish uchun ustiga bosing."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Tebranishni yoqish uchun ustiga bosing. Maxsus imkoniyatlar ishlamasligi mumkin."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Ovozini o‘chirish uchun ustiga bosing. Maxsus imkoniyatlar ishlamasligi mumkin."</string>
@@ -758,7 +762,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Darhol ochiladigan ilovalar"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Darhol ochiladigan ilovalarni o‘rnatish shart emas."</string>
<string name="app_info" msgid="6856026610594615344">"Ilova haqida"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Brauzerga o‘tish"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Brauzerni ochish"</string>
<string name="mobile_data" msgid="7094582042819250762">"Mobil internet"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi o‘chiq"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth o‘chiq"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 237920d..551decf 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Tần số đa chuông kép"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Trợ năng"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"Cuộc gọi"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Đổ chuông"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Rung"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Tắt tiếng"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Nhấn để bật tiếng."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Nhấn để đặt chế độ rung. Bạn có thể tắt tiếng dịch vụ trợ năng."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Nhấn để tắt tiếng. Bạn có thể tắt tiếng dịch vụ trợ năng."</string>
@@ -758,7 +762,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Ứng dụng tức thì"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Ứng dụng tức thì không yêu cầu cài đặt."</string>
<string name="app_info" msgid="6856026610594615344">"Thông tin ứng dụng"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Truy cập web"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Đi tới trình duyệt"</string>
<string name="mobile_data" msgid="7094582042819250762">"Dữ liệu di động"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi tắt"</string>
<string name="bt_is_off" msgid="2640685272289706392">"Bluetooth tắt"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index d80d1bf..2b64d86 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -492,6 +492,11 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"蓝牙"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"双音多频"</string>
<string name="stream_accessibility" msgid="301136219144385106">"无障碍"</string>
+ <!-- no translation found for ring_toggle_title (3281244519428819576) -->
+ <skip />
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"响铃"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"振动"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"静音"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s。点按即可取消静音。"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s。点按即可设为振动,但可能会同时将无障碍服务设为静音。"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s。点按即可设为静音,但可能会同时将无障碍服务设为静音。"</string>
@@ -756,7 +761,7 @@
<string name="instant_apps" msgid="6647570248119804907">"免安装应用"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"免安装应用无需安装就能使用。"</string>
<string name="app_info" msgid="6856026610594615344">"应用信息"</string>
- <string name="go_to_web" msgid="1106022723459948514">"转到网页版"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"转到浏览器"</string>
<string name="mobile_data" msgid="7094582042819250762">"移动数据"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"WLAN 已关闭"</string>
<string name="bt_is_off" msgid="2640685272289706392">"蓝牙已关闭"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 7da008d..382425d 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -494,6 +494,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"藍牙"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"雙音多頻訊號"</string>
<string name="stream_accessibility" msgid="301136219144385106">"無障礙功能"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"通話"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"鈴聲"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"震動"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"靜音"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s。輕按即可取消靜音。"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s。輕按即可設為震動。無障礙功能服務可能已經設為靜音。"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s。輕按即可設為靜音。無障礙功能服務可能已經設為靜音。"</string>
@@ -758,7 +762,7 @@
<string name="instant_apps" msgid="6647570248119804907">"即時應用程式"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"即時應用程式無需安裝即可使用。"</string>
<string name="app_info" msgid="6856026610594615344">"應用程式資料"</string>
- <string name="go_to_web" msgid="1106022723459948514">"前往網頁版"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"前往瀏覽器"</string>
<string name="mobile_data" msgid="7094582042819250762">"流動數據"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi 已關閉"</string>
<string name="bt_is_off" msgid="2640685272289706392">"藍牙已關閉"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index efb48cd..ef9ea43 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -492,6 +492,10 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"藍牙"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"雙音多頻"</string>
<string name="stream_accessibility" msgid="301136219144385106">"協助工具"</string>
+ <string name="ring_toggle_title" msgid="3281244519428819576">"通話"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"鈴聲"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"震動"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"靜音"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s。輕觸即可取消靜音。"</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s。輕觸即可設為震動,但系統可能會將無障礙服務一併設為靜音。"</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s。輕觸即可設為靜音,但系統可能會將無障礙服務一併設為靜音。"</string>
@@ -756,7 +760,7 @@
<string name="instant_apps" msgid="6647570248119804907">"免安裝應用程式"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"免安裝應用程式不必安裝就能使用。"</string>
<string name="app_info" msgid="6856026610594615344">"應用程式資訊"</string>
- <string name="go_to_web" msgid="1106022723459948514">"前往網頁版"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"前往瀏覽器"</string>
<string name="mobile_data" msgid="7094582042819250762">"行動數據"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi 已關閉"</string>
<string name="bt_is_off" msgid="2640685272289706392">"藍牙已關閉"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 5094de9..b3b26dc 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -492,6 +492,9 @@
<string name="stream_bluetooth_sco" msgid="2055645746402746292">"I-Bluetooth"</string>
<string name="stream_dtmf" msgid="2447177903892477915">"Ifrikhwensi yethoni engakuningi"</string>
<string name="stream_accessibility" msgid="301136219144385106">"Ukufinyeleleka"</string>
+ <string name="volume_ringer_status_normal" msgid="4273142424125855384">"Khalisa"</string>
+ <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"Dlidlizela"</string>
+ <string name="volume_ringer_status_silent" msgid="6896394161022916369">"Thulisa"</string>
<string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s. Thepha ukuze ususe ukuthula."</string>
<string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s. Thepha ukuze usethe ukudlidliza. Amasevisi okufinyelela angathuliswa."</string>
<string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s. Thepha ukuze uthulise. Amasevisi okufinyelela angathuliswa."</string>
@@ -756,7 +759,7 @@
<string name="instant_apps" msgid="6647570248119804907">"Izinhlelo zokusebenza ezisheshayo"</string>
<string name="instant_apps_message" msgid="8116608994995104836">"Izinhlelo zokusebenza ezisheshayo azidingi ukufakwa."</string>
<string name="app_info" msgid="6856026610594615344">"Ulwazi lohlelo lokusebenza"</string>
- <string name="go_to_web" msgid="1106022723459948514">"Iya kuwebhu"</string>
+ <string name="go_to_web" msgid="2650669128861626071">"Iya kusiphequluli"</string>
<string name="mobile_data" msgid="7094582042819250762">"Idatha yeselula"</string>
<string name="wifi_is_off" msgid="1838559392210456893">"I-Wi-Fi ivaliwe"</string>
<string name="bt_is_off" msgid="2640685272289706392">"I-Bluetooth ivaliwe"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 5e7f9c6..e313e86 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -301,6 +301,9 @@
<!-- Enable the default volume dialog -->
<bool name="enable_volume_ui">true</bool>
+ <!-- Enable the default volume level warning dialog -->
+ <bool name="enable_safety_warning">true</bool>
+
<!-- Whether to show operator name in the status bar -->
<bool name="config_showOperatorNameInStatusBar">false</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 62ab74d..fd205dd 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1248,6 +1248,7 @@
<string name="stream_tts" translatable="false">Transmitted Through Speaker</string> <!-- STREAM_TTS -->
<string name="stream_accessibility">Accessibility</string> <!-- STREAM_ACCESSIBILITY -->
+ <string name="ring_toggle_title">Calls</string>
<string name="volume_ringer_status_normal">Ring</string>
<string name="volume_ringer_status_vibrate">Vibrate</string>
<string name="volume_ringer_status_silent">Mute</string>
@@ -1268,6 +1269,14 @@
<string name="volume_dialog_accessibility_shown_message">%s volume controls shown. Swipe up to dismiss.</string>
<string name="volume_dialog_accessibility_dismissed_message">Volume controls hidden</string>
+ <string name="output_title">Media output</string>
+ <string name="output_calls_title">Phone call output</string>
+ <string name="output_none_found">No devices found</string>
+ <string name="output_none_found_service_off">No devices found. Try turning on <xliff:g id="service" example="Bluetooth">%1$s</xliff:g></string>
+ <string name="output_service_bt">Bluetooth</string>
+ <string name="output_service_wifi">Wi-Fi</string>
+ <string name="output_service_bt_wifi">Bluetooth and Wi-Fi</string>
+
<!-- Name of special SystemUI debug settings -->
<string name="system_ui_tuner">System UI Tuner</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 27bc599..9f39321 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -49,6 +49,7 @@
private boolean mIsVerifyUnlockOnly;
private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
private SecurityCallback mSecurityCallback;
+ private AlertDialog mAlertDialog;
private final KeyguardUpdateMonitor mUpdateMonitor;
@@ -95,6 +96,10 @@
@Override
public void onPause() {
+ if (mAlertDialog != null) {
+ mAlertDialog.dismiss();
+ mAlertDialog = null;
+ }
if (mCurrentSecuritySelection != SecurityMode.None) {
getSecurityView(mCurrentSecuritySelection).onPause();
}
@@ -174,16 +179,20 @@
}
private void showDialog(String title, String message) {
- final AlertDialog dialog = new AlertDialog.Builder(mContext)
+ if (mAlertDialog != null) {
+ mAlertDialog.dismiss();
+ }
+
+ mAlertDialog = new AlertDialog.Builder(mContext)
.setTitle(title)
.setMessage(message)
.setCancelable(false)
.setNeutralButton(R.string.ok, null)
.create();
if (!(mContext instanceof Activity)) {
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
}
- dialog.show();
+ mAlertDialog.show();
}
private void showTimeoutDialog(int userId, int timeoutMs) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 41b007a..2058f15 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -71,6 +71,7 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.IccCardConstants.State;
import com.android.internal.telephony.PhoneConstants;
@@ -347,6 +348,7 @@
private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray();
private static int sCurrentUser;
+ private Runnable mUpdateFingerprintListeningState = this::updateFingerprintListeningState;
public synchronized static void setCurrentUser(int currentUser) {
sCurrentUser = currentUser;
@@ -1110,7 +1112,8 @@
}
}
- private KeyguardUpdateMonitor(Context context) {
+ @VisibleForTesting
+ protected KeyguardUpdateMonitor(Context context) {
mContext = context;
mSubscriptionManager = SubscriptionManager.from(context);
mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
@@ -1666,7 +1669,8 @@
public void setSwitchingUser(boolean switching) {
mSwitchingUser = switching;
- updateFingerprintListeningState();
+ // Since this comes in on a binder thread, we need to post if first
+ mHandler.post(mUpdateFingerprintListeningState);
}
private void sendUpdates(KeyguardUpdateMonitorCallback callback) {
diff --git a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
index b194de4..eff84c6 100644
--- a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
@@ -76,6 +76,12 @@
void playTrustedSound();
/**
+ * When the bouncer is shown or hides
+ * @param shown
+ */
+ void onBouncerVisiblityChanged(boolean shown);
+
+ /**
* @return true if the screen is on
*/
boolean isScreenOn();
diff --git a/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java b/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java
index edd1748..6aa465c 100644
--- a/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java
+++ b/packages/SystemUI/src/com/android/systemui/EmulatedDisplayCutout.java
@@ -24,6 +24,7 @@
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Point;
+import android.graphics.Region;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
@@ -36,7 +37,8 @@
import android.view.WindowInsets;
import android.view.WindowManager;
-import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* Emulates a display cutout by drawing its shape in an overlay as supplied by
@@ -85,6 +87,7 @@
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
| WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
+ lp.flags2 |= WindowManager.LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA;
lp.setTitle("EmulatedDisplayCutout");
lp.gravity = Gravity.TOP;
return lp;
@@ -102,9 +105,8 @@
};
private static class CutoutView extends View {
- private Paint mPaint = new Paint();
- private Path mPath = new Path();
- private ArrayList<Point> mBoundingPolygon = new ArrayList<>();
+ private final Paint mPaint = new Paint();
+ private final Path mBounds = new Path();
CutoutView(Context context) {
super(context);
@@ -112,28 +114,22 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- insets.getDisplayCutout().getBoundingPolygon(mBoundingPolygon);
+ if (insets.getDisplayCutout() != null) {
+ insets.getDisplayCutout().getBounds().getBoundaryPath(mBounds);
+ } else {
+ mBounds.reset();
+ }
invalidate();
- return insets.consumeCutout();
+ return insets.consumeDisplayCutout();
}
@Override
protected void onDraw(Canvas canvas) {
- if (!mBoundingPolygon.isEmpty()) {
+ if (!mBounds.isEmpty()) {
mPaint.setColor(Color.DKGRAY);
mPaint.setStyle(Paint.Style.FILL);
- mPath.reset();
- for (int i = 0; i < mBoundingPolygon.size(); i++) {
- Point point = mBoundingPolygon.get(i);
- if (i == 0) {
- mPath.moveTo(point.x, point.y);
- } else {
- mPath.lineTo(point.x, point.y);
- }
- }
- mPath.close();
- canvas.drawPath(mPath, mPaint);
+ canvas.drawPath(mBounds, mPaint);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 45b11aa..3177c03 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.app.AlarmManager;
import android.content.Context;
import android.util.ArrayMap;
import android.util.Log;
@@ -26,19 +27,23 @@
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Dependency.DependencyProvider;
import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.qs.QSTileHost;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationGutsManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationLogger;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockIcon;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.qs.QSTileHost;
import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -88,10 +93,10 @@
public ScrimController createScrimController(LightBarController lightBarController,
ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
- LockscreenWallpaper lockscreenWallpaper, Consumer<Boolean> scrimVisibleListener,
- DozeParameters dozeParameters) {
+ LockscreenWallpaper lockscreenWallpaper, Consumer<Integer> scrimVisibleListener,
+ DozeParameters dozeParameters, AlarmManager alarmManager) {
return new ScrimController(lightBarController, scrimBehind, scrimInFront, headsUpScrim,
- scrimVisibleListener, dozeParameters);
+ scrimVisibleListener, dozeParameters, alarmManager);
}
public NotificationIconAreaController createNotificationIconAreaController(Context context,
@@ -113,7 +118,16 @@
Context context) {
providers.put(NotificationLockscreenUserManager.class,
() -> new NotificationLockscreenUserManager(context));
+ providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(
Dependency.get(NotificationLockscreenUserManager.class), context));
+ providers.put(NotificationRemoteInputManager.class,
+ () -> new NotificationRemoteInputManager(
+ Dependency.get(NotificationLockscreenUserManager.class), context));
+ providers.put(NotificationListener.class, () -> new NotificationListener(
+ Dependency.get(NotificationRemoteInputManager.class), context));
+ providers.put(NotificationLogger.class, () -> new NotificationLogger(
+ Dependency.get(NotificationListener.class),
+ Dependency.get(UiOffloadThread.class)));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
index cc2244a..8515bf2 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
@@ -40,12 +40,16 @@
private static final long DEFAULT_PROX_SCREEN_OFF_DELAY_MS = 10 * DateUtils.SECOND_IN_MILLIS;
private static final long DEFAULT_PROX_COOLDOWN_TRIGGER_MS = 2 * DateUtils.SECOND_IN_MILLIS;
private static final long DEFAULT_PROX_COOLDOWN_PERIOD_MS = 5 * DateUtils.SECOND_IN_MILLIS;
+ private static final long DEFAULT_WALLPAPER_VISIBILITY_MS = 60 * DateUtils.SECOND_IN_MILLIS;
+ private static final long DEFAULT_WALLPAPER_FADE_OUT_MS = 400;
static final String KEY_SCREEN_BRIGHTNESS_ARRAY = "screen_brightness_array";
static final String KEY_DIMMING_SCRIM_ARRAY = "dimming_scrim_array";
static final String KEY_PROX_SCREEN_OFF_DELAY_MS = "prox_screen_off_delay";
static final String KEY_PROX_COOLDOWN_TRIGGER_MS = "prox_cooldown_trigger";
static final String KEY_PROX_COOLDOWN_PERIOD_MS = "prox_cooldown_period";
+ static final String KEY_WALLPAPER_VISIBILITY_MS = "wallpaper_visibility_timeout";
+ static final String KEY_WALLPAPER_FADE_OUT_MS = "wallpaper_fade_out_duration";
/**
* Integer array to map ambient brightness type to real screen brightness.
@@ -89,6 +93,24 @@
*/
public long proxCooldownPeriodMs;
+ /**
+ * For how long(ms) the wallpaper should still be visible
+ * after entering AoD.
+ *
+ * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
+ * @see #KEY_WALLPAPER_VISIBILITY_MS
+ */
+ public long wallpaperVisibilityDuration;
+
+ /**
+ * Duration(ms) of the fade out animation after
+ * {@link #KEY_WALLPAPER_VISIBILITY_MS} elapses.
+ *
+ * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
+ * @see #KEY_WALLPAPER_FADE_OUT_MS
+ */
+ public long wallpaperFadeOutDuration;
+
private final KeyValueListParser mParser;
private final Context mContext;
private SettingsObserver mSettingsObserver;
@@ -138,6 +160,10 @@
DEFAULT_PROX_COOLDOWN_TRIGGER_MS);
proxCooldownPeriodMs = mParser.getLong(KEY_PROX_COOLDOWN_PERIOD_MS,
DEFAULT_PROX_COOLDOWN_PERIOD_MS);
+ wallpaperFadeOutDuration = mParser.getLong(KEY_WALLPAPER_FADE_OUT_MS,
+ DEFAULT_WALLPAPER_FADE_OUT_MS);
+ wallpaperVisibilityDuration = mParser.getLong(KEY_WALLPAPER_VISIBILITY_MS,
+ DEFAULT_WALLPAPER_VISIBILITY_MS);
screenBrightnessArray = mParser.getIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY,
resources.getIntArray(
R.array.config_doze_brightness_sensor_to_brightness));
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 6f8bcff..0f0402d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -63,9 +63,10 @@
new DozeFalsingManagerAdapter(FalsingManager.getInstance(context)),
createDozeTriggers(context, sensorManager, host, alarmManager, config, params,
handler, wakeLock, machine),
- createDozeUi(context, host, wakeLock, machine, handler, alarmManager),
+ createDozeUi(context, host, wakeLock, machine, handler, alarmManager, params),
new DozeScreenState(wrappedService, handler),
createDozeScreenBrightness(context, wrappedService, sensorManager, host, handler),
+ new DozeWallpaperState()
});
return machine;
@@ -89,8 +90,9 @@
}
private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock,
- DozeMachine machine, Handler handler, AlarmManager alarmManager) {
- return new DozeUi(context, alarmManager, machine, wakeLock, host, handler);
+ DozeMachine machine, Handler handler, AlarmManager alarmManager,
+ DozeParameters params) {
+ return new DozeUi(context, alarmManager, machine, wakeLock, host, handler, params);
}
public static DozeHost getHost(DozeService service) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 2f607ee..6a29299 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -38,6 +38,7 @@
void extendPulse();
void setAnimateWakeup(boolean animateWakeup);
+ void setAnimateScreenOff(boolean animateScreenOff);
void onDoubleTap(float x, float y);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 6650cc6..34d3928 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -24,9 +24,10 @@
import com.android.systemui.Dependency;
import com.android.systemui.plugins.DozeServicePlugin;
-import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.DozeServicePlugin.RequestDoze;
import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 851b78c..b352ec9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -23,6 +23,7 @@
import android.text.format.Formatter;
import android.util.Log;
+import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.wakelock.WakeLock;
@@ -41,18 +42,22 @@
private final WakeLock mWakeLock;
private final DozeMachine mMachine;
private final AlarmTimeout mTimeTicker;
+ private final boolean mCanAnimateWakeup;
private long mLastTimeTickElapsed = 0;
public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine,
- WakeLock wakeLock, DozeHost host, Handler handler) {
+ WakeLock wakeLock, DozeHost host, Handler handler,
+ DozeParameters params) {
mContext = context;
mMachine = machine;
mWakeLock = wakeLock;
mHost = host;
mHandler = handler;
+ mCanAnimateWakeup = !params.getDisplayNeedsBlanking();
mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler);
+ mHost.setAnimateScreenOff(params.getCanControlScreenOffAnimation());
}
private void pulseWhileDozing(int reason) {
@@ -106,7 +111,7 @@
// Keep current state.
break;
default:
- mHost.setAnimateWakeup(false);
+ mHost.setAnimateWakeup(mCanAnimateWakeup);
break;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
new file mode 100644
index 0000000..ee41001
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
@@ -0,0 +1,84 @@
+/*
+ * 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.doze;
+
+import android.app.IWallpaperManager;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+
+/**
+ * Propagates doze state to wallpaper engine.
+ */
+public class DozeWallpaperState implements DozeMachine.Part {
+
+ private static final String TAG = "DozeWallpaperState";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ @VisibleForTesting
+ final IWallpaperManager mWallpaperManagerService;
+ private boolean mIsAmbientMode;
+
+ public DozeWallpaperState() {
+ this(IWallpaperManager.Stub.asInterface(
+ ServiceManager.getService(Context.WALLPAPER_SERVICE)));
+ }
+
+ @VisibleForTesting
+ DozeWallpaperState(IWallpaperManager wallpaperManagerService) {
+ mWallpaperManagerService = wallpaperManagerService;
+ }
+
+ @Override
+ public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
+ final boolean isAmbientMode;
+ switch (newState) {
+ case DOZE_AOD:
+ case DOZE_AOD_PAUSING:
+ case DOZE_AOD_PAUSED:
+ case DOZE_REQUEST_PULSE:
+ case DOZE_PULSING:
+ case DOZE_PULSE_DONE:
+ isAmbientMode = true;
+ break;
+ default:
+ isAmbientMode = false;
+ }
+
+ if (isAmbientMode != mIsAmbientMode) {
+ mIsAmbientMode = isAmbientMode;
+ try {
+ Log.i(TAG, "AoD wallpaper state changed to: " + mIsAmbientMode);
+ mWallpaperManagerService.setInAmbientMode(mIsAmbientMode);
+ } catch (RemoteException e) {
+ // Cannot notify wallpaper manager service, but it's fine, let's just skip it.
+ Log.w(TAG, "Cannot notify state to WallpaperManagerService: " + mIsAmbientMode);
+ }
+ }
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.println("DozeWallpaperState:");
+ pw.println(" isAmbientMode: " + mIsAmbientMode);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 1faf981..c4d9cf5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -164,7 +164,6 @@
private static final int NOTIFY_SCREEN_TURNED_ON = 15;
private static final int NOTIFY_SCREEN_TURNED_OFF = 16;
private static final int NOTIFY_STARTED_GOING_TO_SLEEP = 17;
- private static final int SET_SWITCHING_USER = 18;
/**
* The default amount of time we stay awake (used for all key input)
@@ -614,6 +613,13 @@
}
@Override
+ public void onBouncerVisiblityChanged(boolean shown) {
+ synchronized (KeyguardViewMediator.this) {
+ adjustStatusBarLocked(shown);
+ }
+ }
+
+ @Override
public void playTrustedSound() {
KeyguardViewMediator.this.playTrustedSound();
}
@@ -1419,11 +1425,7 @@
}
public void setSwitchingUser(boolean switching) {
- Trace.beginSection("KeyguardViewMediator#setSwitchingUser");
- mHandler.removeMessages(SET_SWITCHING_USER);
- Message msg = mHandler.obtainMessage(SET_SWITCHING_USER, switching ? 1 : 0, 0);
- mHandler.sendMessage(msg);
- Trace.endSection();
+ KeyguardUpdateMonitor.getInstance(mContext).setSwitchingUser(switching);
}
/**
@@ -1563,11 +1565,6 @@
Log.w(TAG, "Timeout while waiting for activity drawn!");
Trace.endSection();
break;
- case SET_SWITCHING_USER:
- Trace.beginSection("KeyguardViewMediator#handleMessage SET_SWITCHING_USER");
- KeyguardUpdateMonitor.getInstance(mContext).setSwitchingUser(msg.arg1 != 0);
- Trace.endSection();
- break;
}
}
};
@@ -1872,6 +1869,10 @@
}
private void adjustStatusBarLocked() {
+ adjustStatusBarLocked(false /* forceHideHomeRecentsButtons */);
+ }
+
+ private void adjustStatusBarLocked(boolean forceHideHomeRecentsButtons) {
if (mStatusBarManager == null) {
mStatusBarManager = (StatusBarManager)
mContext.getSystemService(Context.STATUS_BAR_SERVICE);
@@ -1882,19 +1883,14 @@
// Disable aspects of the system/status/navigation bars that must not be re-enabled by
// windows that appear on top, ever
int flags = StatusBarManager.DISABLE_NONE;
- if (mShowing) {
- // Permanently disable components not available when keyguard is enabled
- // (like recents). Temporary enable/disable (e.g. the "back" button) are
- // done in KeyguardHostView.
- flags |= StatusBarManager.DISABLE_RECENT;
- }
- if (isShowingAndNotOccluded()) {
- flags |= StatusBarManager.DISABLE_HOME;
+ if (forceHideHomeRecentsButtons || isShowingAndNotOccluded()) {
+ flags |= StatusBarManager.DISABLE_HOME | StatusBarManager.DISABLE_RECENT;
}
if (DEBUG) {
Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mOccluded=" + mOccluded
- + " isSecure=" + isSecure() + " --> flags=0x" + Integer.toHexString(flags));
+ + " isSecure=" + isSecure() + " force=" + forceHideHomeRecentsButtons
+ + " --> flags=0x" + Integer.toHexString(flags));
}
mStatusBarManager.disable(flags);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 663f206..8359690 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -24,6 +24,8 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.KeyguardManager;
+import android.app.trust.TrustManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
@@ -32,6 +34,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
+import android.os.AsyncTask.Status;
import android.os.Handler;
import android.os.SystemClock;
import android.util.ArraySet;
@@ -225,6 +228,7 @@
}
};
+ private TrustManager mTrustManager;
protected Context mContext;
protected Handler mHandler;
TaskStackListenerImpl mTaskStackListener;
@@ -271,6 +275,8 @@
// Initialize the static configuration resources
mDummyStackView = new TaskStackView(mContext);
reloadResources();
+
+ mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
}
public void onBootCompleted() {
@@ -309,8 +315,7 @@
* {@link Recents#onBusEvent(ScreenPinningRequestEvent)}.
*/
public void onStartScreenPinning(Context context, int taskId) {
- SystemUIApplication app = (SystemUIApplication) context;
- StatusBar statusBar = app.getComponent(StatusBar.class);
+ final StatusBar statusBar = getStatusBar();
if (statusBar != null) {
statusBar.showScreenPinningRequest(taskId, false);
}
@@ -350,8 +355,8 @@
if (forceVisible || !ssp.isRecentsActivityVisible(isHomeStackVisible)) {
ActivityManager.RunningTaskInfo runningTask =
ActivityManagerWrapper.getInstance().getRunningTask();
- startRecentsActivity(runningTask, isHomeStackVisible.value || fromHome, animate,
- growTarget);
+ startRecentsActivityAndDismissKeyguardIfNeeded(runningTask,
+ isHomeStackVisible.value || fromHome, animate, growTarget);
}
} catch (ActivityNotFoundException e) {
Log.e(TAG, "Failed to launch RecentsActivity", e);
@@ -442,8 +447,8 @@
// Otherwise, start the recents activity
ActivityManager.RunningTaskInfo runningTask =
ActivityManagerWrapper.getInstance().getRunningTask();
- startRecentsActivity(runningTask, isHomeStackVisible.value, true /* animate */,
- growTarget);
+ startRecentsActivityAndDismissKeyguardIfNeeded(runningTask,
+ isHomeStackVisible.value, true /* animate */, growTarget);
// Only close the other system windows if we are actually showing recents
ActivityManagerWrapper.getInstance().closeSystemWindows(
@@ -462,6 +467,12 @@
return;
}
+ // Skip preloading recents when keyguard is showing
+ final StatusBar statusBar = getStatusBar();
+ if (statusBar != null && statusBar.isKeyguardShowing()) {
+ return;
+ }
+
// Preload only the raw task list into a new load plan (which will be consumed by the
// 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.
@@ -942,9 +953,27 @@
}
/**
- * Shows the recents activity
+ * Shows the recents activity after dismissing the keyguard if visible
*/
- protected void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
+ protected void startRecentsActivityAndDismissKeyguardIfNeeded(
+ final ActivityManager.RunningTaskInfo runningTask, final boolean isHomeStackVisible,
+ final boolean animate, final int growTarget) {
+ // Preload only if device for current user is unlocked
+ final StatusBar statusBar = getStatusBar();
+ if (statusBar != null && statusBar.isKeyguardShowing()) {
+ statusBar.executeRunnableDismissingKeyguard(() -> {
+ // Flush trustmanager before checking device locked per user when preloading
+ mTrustManager.reportKeyguardShowingChanged();
+ mHandler.post(() -> startRecentsActivity(runningTask, isHomeStackVisible,
+ animate, growTarget));
+ }, null, true /* dismissShade */, false /* afterKeyguardGone */,
+ true /* deferred */);
+ } else {
+ startRecentsActivity(runningTask, isHomeStackVisible, animate, growTarget);
+ }
+ }
+
+ private void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
boolean isHomeStackVisible, boolean animate, int growTarget) {
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
@@ -1033,6 +1062,10 @@
return result;
}
+ private StatusBar getStatusBar() {
+ return ((SystemUIApplication) mContext).getComponent(StatusBar.class);
+ }
+
/**
* Starts the recents activity.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 5fcd006..6db46b5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -38,7 +38,6 @@
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
@@ -58,10 +57,13 @@
import android.util.DisplayMetrics;
import android.util.Slog;
import android.view.Display;
+import android.view.DisplayListCanvas;
import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.RenderNode;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -153,7 +155,6 @@
int previewWidth = data.previewWidth;
int previewHeight = data.previewheight;
- Canvas c = new Canvas();
Paint paint = new Paint();
ColorMatrix desat = new ColorMatrix();
desat.setSaturation(0.25f);
@@ -161,23 +162,17 @@
Matrix matrix = new Matrix();
int overlayColor = 0x40FFFFFF;
- Bitmap picture = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888);
matrix.setTranslate((previewWidth - mImageWidth) / 2, (previewHeight - mImageHeight) / 2);
- c.setBitmap(picture);
- c.drawBitmap(data.image, matrix, paint);
- c.drawColor(overlayColor);
- c.setBitmap(null);
+ Bitmap picture = generateAdjustedHwBitmap(data.image, previewWidth, previewHeight, matrix,
+ paint, overlayColor);
// Note, we can't use the preview for the small icon, since it is non-square
float scale = (float) iconSize / Math.min(mImageWidth, mImageHeight);
- Bitmap icon = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
matrix.setScale(scale, scale);
matrix.postTranslate((iconSize - (scale * mImageWidth)) / 2,
(iconSize - (scale * mImageHeight)) / 2);
- c.setBitmap(icon);
- c.drawBitmap(data.image, matrix, paint);
- c.drawColor(overlayColor);
- c.setBitmap(null);
+ Bitmap icon = generateAdjustedHwBitmap(data.image, iconSize, iconSize, matrix, paint,
+ overlayColor);
// Show the intermediate notification
mTickerAddSpace = !mTickerAddSpace;
@@ -231,6 +226,22 @@
mNotificationStyle.bigLargeIcon((Bitmap) null);
}
+ /**
+ * Generates a new hardware bitmap with specified values, copying the content from the passed
+ * in bitmap.
+ */
+ private Bitmap generateAdjustedHwBitmap(Bitmap bitmap, int width, int height, Matrix matrix,
+ Paint paint, int color) {
+ RenderNode node = RenderNode.create("ScreenshotCanvas", null);
+ node.setLeftTopRightBottom(0, 0, width, height);
+ node.setClipToBounds(false);
+ DisplayListCanvas canvas = node.start(width, height);
+ canvas.drawColor(color);
+ canvas.drawBitmap(bitmap, matrix, paint);
+ node.end(canvas);
+ return ThreadedRenderer.createHardwareBitmap(node, width, height);
+ }
+
@Override
protected Void doInBackground(Void... params) {
if (isCancelled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index e04bd0e..f53eb48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -206,7 +206,7 @@
private OnClickListener mExpandClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
- if (!mShowingPublic && (!mIsLowPriority || isExpanded())
+ if (!shouldShowPublic() && (!mIsLowPriority || isExpanded())
&& mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
mGroupExpansionChanging = true;
final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
@@ -790,7 +790,7 @@
* {@link #getNotificationHeader()} in case it is a low-priority group.
*/
public NotificationHeaderView getVisibleNotificationHeader() {
- if (mIsSummaryWithChildren && !mShowingPublic) {
+ if (mIsSummaryWithChildren && !shouldShowPublic()) {
return mChildrenContainer.getVisibleHeader();
}
return getShowingLayout().getVisibleNotificationHeader();
@@ -1512,10 +1512,10 @@
}
private void updateChildrenVisibility() {
- mPrivateLayout.setVisibility(!mShowingPublic && !mIsSummaryWithChildren ? VISIBLE
+ mPrivateLayout.setVisibility(!shouldShowPublic() && !mIsSummaryWithChildren ? VISIBLE
: INVISIBLE);
if (mChildrenContainer != null) {
- mChildrenContainer.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
+ mChildrenContainer.setVisibility(!shouldShowPublic() && mIsSummaryWithChildren ? VISIBLE
: INVISIBLE);
}
// The limits might have changed if the view suddenly became a group or vice versa
@@ -1566,7 +1566,7 @@
}
public boolean isExpandable() {
- if (mIsSummaryWithChildren && !mShowingPublic) {
+ if (mIsSummaryWithChildren && !shouldShowPublic()) {
return !mChildrenExpanded;
}
return mEnableNonGroupedNotificationExpand && mExpandable;
@@ -1611,7 +1611,7 @@
*/
public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
mFalsingManager.setNotificationExpanded();
- if (mIsSummaryWithChildren && !mShowingPublic && allowChildExpansion
+ if (mIsSummaryWithChildren && !shouldShowPublic() && allowChildExpansion
&& !mChildrenContainer.showingAsLowPriority()) {
final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
mGroupManager.setGroupExpanded(mStatusBarNotification, userExpanded);
@@ -1906,7 +1906,7 @@
mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
updateChildrenVisibility();
} else {
- animateShowingPublic(delay, duration);
+ animateShowingPublic(delay, duration, mShowingPublic);
}
NotificationContentView showingLayout = getShowingLayout();
showingLayout.updateBackgroundColor(animated);
@@ -1916,13 +1916,13 @@
mShowingPublicInitialized = true;
}
- private void animateShowingPublic(long delay, long duration) {
+ private void animateShowingPublic(long delay, long duration, boolean showingPublic) {
View[] privateViews = mIsSummaryWithChildren
? new View[] {mChildrenContainer}
: new View[] {mPrivateLayout};
View[] publicViews = new View[] {mPublicLayout};
- View[] hiddenChildren = mShowingPublic ? privateViews : publicViews;
- View[] shownChildren = mShowingPublic ? publicViews : privateViews;
+ View[] hiddenChildren = showingPublic ? privateViews : publicViews;
+ View[] shownChildren = showingPublic ? publicViews : privateViews;
for (final View hiddenView : hiddenChildren) {
hiddenView.setVisibility(View.VISIBLE);
hiddenView.animate().cancel();
@@ -1959,7 +1959,11 @@
* see {@link #isClearable()}.
*/
public boolean canViewBeDismissed() {
- return isClearable() && (!mShowingPublic || !mSensitiveHiddenInGeneral);
+ return isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+ }
+
+ private boolean shouldShowPublic() {
+ return mSensitive && mHideSensitiveForIntrinsicHeight;
}
public void makeActionsVisibile() {
@@ -2005,7 +2009,7 @@
@Override
public boolean isContentExpandable() {
- if (mIsSummaryWithChildren && !mShowingPublic) {
+ if (mIsSummaryWithChildren && !shouldShowPublic()) {
return true;
}
NotificationContentView showingLayout = getShowingLayout();
@@ -2014,7 +2018,7 @@
@Override
protected View getContentView() {
- if (mIsSummaryWithChildren && !mShowingPublic) {
+ if (mIsSummaryWithChildren && !shouldShowPublic()) {
return mChildrenContainer;
}
return getShowingLayout();
@@ -2079,7 +2083,7 @@
@Override
public int getMaxContentHeight() {
- if (mIsSummaryWithChildren && !mShowingPublic) {
+ if (mIsSummaryWithChildren && !shouldShowPublic()) {
return mChildrenContainer.getMaxContentHeight();
}
NotificationContentView showingLayout = getShowingLayout();
@@ -2093,7 +2097,7 @@
} else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp
&& mHeadsUpManager.isTrackingHeadsUp()) {
return getPinnedHeadsUpHeight(false /* atLeastMinHeight */);
- } else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) {
+ } else if (mIsSummaryWithChildren && !isGroupExpanded() && !shouldShowPublic()) {
return mChildrenContainer.getMinHeight();
} else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp) {
return mHeadsUpHeight;
@@ -2104,7 +2108,7 @@
@Override
public int getCollapsedHeight() {
- if (mIsSummaryWithChildren && !mShowingPublic) {
+ if (mIsSummaryWithChildren && !shouldShowPublic()) {
return mChildrenContainer.getCollapsedHeight();
}
return getMinHeight();
@@ -2144,7 +2148,7 @@
}
public NotificationContentView getShowingLayout() {
- return mShowingPublic ? mPublicLayout : mPrivateLayout;
+ return shouldShowPublic() ? mPublicLayout : mPrivateLayout;
}
public void setLegacy(boolean legacy) {
@@ -2250,7 +2254,7 @@
if (header != null && header.isInTouchRect(x - getTranslation(), y)) {
return true;
}
- if ((!mIsSummaryWithChildren || mShowingPublic)
+ if ((!mIsSummaryWithChildren || shouldShowPublic())
&& getShowingLayout().disallowSingleClick(x, y)) {
return true;
}
@@ -2280,7 +2284,7 @@
if (canViewBeDismissed()) {
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
}
- boolean expandable = mShowingPublic;
+ boolean expandable = shouldShowPublic();
boolean isExpanded = false;
if (!expandable) {
if (mIsSummaryWithChildren) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 6bcd174..a72e8ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -36,11 +36,14 @@
public class NotificationListener extends NotificationListenerWithPlugins {
private static final String TAG = "NotificationListener";
- private final NotificationPresenter mPresenter;
+ private final NotificationRemoteInputManager mRemoteInputManager;
private final Context mContext;
- public NotificationListener(NotificationPresenter presenter, Context context) {
- mPresenter = presenter;
+ private NotificationPresenter mPresenter;
+
+ public NotificationListener(NotificationRemoteInputManager remoteInputManager,
+ Context context) {
+ mRemoteInputManager = remoteInputManager;
mContext = context;
}
@@ -69,7 +72,7 @@
mPresenter.getHandler().post(() -> {
processForRemoteInput(sbn.getNotification(), mContext);
String key = sbn.getKey();
- mPresenter.getKeysKeptForRemoteInput().remove(key);
+ mRemoteInputManager.getKeysKeptForRemoteInput().remove(key);
boolean isUpdate = mPresenter.getNotificationData().get(key) != null;
// In case we don't allow child notifications, we ignore children of
// notifications that have a summary, since` we're not going to show them
@@ -117,7 +120,9 @@
}
}
- public void register() {
+ public void setUpWithPresenter(NotificationPresenter presenter) {
+ mPresenter = presenter;
+
try {
registerAsSystemService(mContext,
new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java
new file mode 100644
index 0000000..e58d801
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLogger.java
@@ -0,0 +1,223 @@
+/*
+ * 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.statusbar;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.service.notification.NotificationListenerService;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.UiOffloadThread;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * Handles notification logging, in particular, logging which notifications are visible and which
+ * are not.
+ */
+public class NotificationLogger {
+ private static final String TAG = "NotificationLogger";
+
+ /** The minimum delay in ms between reports of notification visibility. */
+ private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
+
+ /** Keys of notifications currently visible to the user. */
+ private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications =
+ new ArraySet<>();
+ private final NotificationListenerService mNotificationListener;
+ private final UiOffloadThread mUiOffloadThread;
+
+ protected NotificationPresenter mPresenter;
+ protected Handler mHandler = new Handler();
+ protected IStatusBarService mBarService;
+ private long mLastVisibilityReportUptimeMs;
+ private NotificationStackScrollLayout mStackScroller;
+
+ protected final NotificationStackScrollLayout.OnChildLocationsChangedListener
+ mNotificationLocationsChangedListener =
+ new NotificationStackScrollLayout.OnChildLocationsChangedListener() {
+ @Override
+ public void onChildLocationsChanged(
+ NotificationStackScrollLayout stackScrollLayout) {
+ if (mHandler.hasCallbacks(mVisibilityReporter)) {
+ // Visibilities will be reported when the existing
+ // callback is executed.
+ return;
+ }
+ // Calculate when we're allowed to run the visibility
+ // reporter. Note that this timestamp might already have
+ // passed. That's OK, the callback will just be executed
+ // ASAP.
+ long nextReportUptimeMs =
+ mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS;
+ mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs);
+ }
+ };
+
+ // Tracks notifications currently visible in mNotificationStackScroller and
+ // emits visibility events via NoMan on changes.
+ protected final Runnable mVisibilityReporter = new Runnable() {
+ private final ArraySet<NotificationVisibility> mTmpNewlyVisibleNotifications =
+ new ArraySet<>();
+ private final ArraySet<NotificationVisibility> mTmpCurrentlyVisibleNotifications =
+ new ArraySet<>();
+ private final ArraySet<NotificationVisibility> mTmpNoLongerVisibleNotifications =
+ new ArraySet<>();
+
+ @Override
+ public void run() {
+ mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
+
+ // 1. Loop over mNotificationData entries:
+ // A. Keep list of visible notifications.
+ // B. Keep list of previously hidden, now visible notifications.
+ // 2. Compute no-longer visible notifications by removing currently
+ // visible notifications from the set of previously visible
+ // notifications.
+ // 3. Report newly visible and no-longer visible notifications.
+ // 4. Keep currently visible notifications for next report.
+ ArrayList<NotificationData.Entry> activeNotifications = mPresenter.
+ getNotificationData().getActiveNotifications();
+ int N = activeNotifications.size();
+ for (int i = 0; i < N; i++) {
+ NotificationData.Entry entry = activeNotifications.get(i);
+ String key = entry.notification.getKey();
+ boolean isVisible = mStackScroller.isInVisibleLocation(entry.row);
+ NotificationVisibility visObj = NotificationVisibility.obtain(key, i, isVisible);
+ boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj);
+ if (isVisible) {
+ // Build new set of visible notifications.
+ mTmpCurrentlyVisibleNotifications.add(visObj);
+ if (!previouslyVisible) {
+ mTmpNewlyVisibleNotifications.add(visObj);
+ }
+ } else {
+ // release object
+ visObj.recycle();
+ }
+ }
+ mTmpNoLongerVisibleNotifications.addAll(mCurrentlyVisibleNotifications);
+ mTmpNoLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications);
+
+ logNotificationVisibilityChanges(
+ mTmpNewlyVisibleNotifications, mTmpNoLongerVisibleNotifications);
+
+ recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
+ mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications);
+
+ recycleAllVisibilityObjects(mTmpNoLongerVisibleNotifications);
+ mTmpCurrentlyVisibleNotifications.clear();
+ mTmpNewlyVisibleNotifications.clear();
+ mTmpNoLongerVisibleNotifications.clear();
+ }
+ };
+
+ public NotificationLogger(NotificationListenerService notificationListener,
+ UiOffloadThread uiOffloadThread) {
+ mNotificationListener = notificationListener;
+ mUiOffloadThread = uiOffloadThread;
+ mBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ }
+
+ // TODO: Remove dependency on NotificationStackScrollLayout.
+ public void setUpWithPresenter(NotificationPresenter presenter,
+ NotificationStackScrollLayout stackScroller) {
+ mPresenter = presenter;
+ mStackScroller = stackScroller;
+ }
+
+ public void stopNotificationLogging() {
+ // Report all notifications as invisible and turn down the
+ // reporter.
+ if (!mCurrentlyVisibleNotifications.isEmpty()) {
+ logNotificationVisibilityChanges(
+ Collections.emptyList(), mCurrentlyVisibleNotifications);
+ recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
+ }
+ mHandler.removeCallbacks(mVisibilityReporter);
+ mStackScroller.setChildLocationsChangedListener(null);
+ }
+
+ public void startNotificationLogging() {
+ mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
+ // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't
+ // cause the scroller to emit child location events. Hence generate
+ // one ourselves to guarantee that we're reporting visible
+ // notifications.
+ // (Note that in cases where the scroller does emit events, this
+ // additional event doesn't break anything.)
+ mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller);
+ }
+
+ private void logNotificationVisibilityChanges(
+ Collection<NotificationVisibility> newlyVisible,
+ Collection<NotificationVisibility> noLongerVisible) {
+ if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
+ return;
+ }
+ NotificationVisibility[] newlyVisibleAr =
+ newlyVisible.toArray(new NotificationVisibility[newlyVisible.size()]);
+ NotificationVisibility[] noLongerVisibleAr =
+ noLongerVisible.toArray(new NotificationVisibility[noLongerVisible.size()]);
+ mUiOffloadThread.submit(() -> {
+ try {
+ mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
+ } catch (RemoteException e) {
+ // Ignore.
+ }
+
+ final int N = newlyVisible.size();
+ if (N > 0) {
+ String[] newlyVisibleKeyAr = new String[N];
+ for (int i = 0; i < N; i++) {
+ newlyVisibleKeyAr[i] = newlyVisibleAr[i].key;
+ }
+
+ // TODO: Call NotificationEntryManager to do this, once it exists.
+ // TODO: Consider not catching all runtime exceptions here.
+ try {
+ mNotificationListener.setNotificationsShown(newlyVisibleKeyAr);
+ } catch (RuntimeException e) {
+ Log.d(TAG, "failed setNotificationsShown: ", e);
+ }
+ }
+ });
+ }
+
+ private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
+ final int N = array.size();
+ for (int i = 0 ; i < N; i++) {
+ array.valueAt(i).recycle();
+ }
+ array.clear();
+ }
+
+ @VisibleForTesting
+ public Runnable getVisibilityReporter() {
+ return mVisibilityReporter;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index 4eca241..33c7253 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -18,6 +18,7 @@
import android.content.Intent;
import android.os.Handler;
import android.service.notification.NotificationListenerService;
+import android.view.View;
import java.util.Set;
@@ -29,7 +30,7 @@
* want to perform some action before doing so).
*/
public interface NotificationPresenter extends NotificationUpdateHandler,
- NotificationData.Environment {
+ NotificationData.Environment, NotificationRemoteInputManager.Callback {
/**
* Returns true if the presenter is not visible. For example, it may not be necessary to do
@@ -81,14 +82,6 @@
void onWorkChallengeChanged();
/**
- * Notifications in this set are kept around when they were canceled in response to a remote
- * input interaction. This allows us to show what you replied and allows you to continue typing
- * into it.
- */
- // TODO: Create NotificationEntryManager and move this method to there.
- Set<String> getKeysKeptForRemoteInput();
-
- /**
* Called when the current user changes.
* @param newUserId new user id
*/
@@ -98,4 +91,20 @@
* Gets the NotificationLockscreenUserManager for this Presenter.
*/
NotificationLockscreenUserManager getNotificationLockscreenUserManager();
+
+ /**
+ * Wakes the device up if dozing.
+ *
+ * @param time the time when the request to wake up was issued
+ * @param where which view caused this wake up request
+ */
+ void wakeUpIfDozing(long time, View where);
+
+ /**
+ * True if the device currently requires a PIN, pattern, or password to unlock.
+ *
+ * @param userId user id to query about
+ * @return true iff the device is locked
+ */
+ boolean isDeviceLocked(int userId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
new file mode 100644
index 0000000..7827f62
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -0,0 +1,441 @@
+/*
+ * 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.statusbar;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+
+import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserManager;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.policy.RemoteInputView;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Set;
+
+/**
+ * Class for handling remote input state over a set of notifications. This class handles things
+ * like keeping notifications temporarily that were cancelled as a response to a remote input
+ * interaction, keeping track of notifications to remove when NotificationPresenter is collapsed,
+ * and handling clicks on remote views.
+ */
+public class NotificationRemoteInputManager implements Dumpable {
+ public static final boolean ENABLE_REMOTE_INPUT =
+ SystemProperties.getBoolean("debug.enable_remote_input", true);
+ public static final boolean FORCE_REMOTE_INPUT_HISTORY =
+ SystemProperties.getBoolean("debug.force_remoteinput_history", true);
+ private static final boolean DEBUG = false;
+ private static final String TAG = "NotificationRemoteInputManager";
+
+ /**
+ * How long to wait before auto-dismissing a notification that was kept for remote input, and
+ * has now sent a remote input. We auto-dismiss, because the app may not see a reason to cancel
+ * these given that they technically don't exist anymore. We wait a bit in case the app issues
+ * an update.
+ */
+ private static final int REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY = 200;
+
+ protected final ArraySet<NotificationData.Entry> mRemoteInputEntriesToRemoveOnCollapse =
+ new ArraySet<>();
+ protected final NotificationLockscreenUserManager mLockscreenUserManager;
+
+ /**
+ * Notifications with keys in this set are not actually around anymore. We kept them around
+ * when they were canceled in response to a remote input interaction. This allows us to show
+ * what you replied and allows you to continue typing into it.
+ */
+ protected final ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
+ protected final Context mContext;
+ private final UserManager mUserManager;
+
+ protected RemoteInputController mRemoteInputController;
+ protected NotificationPresenter mPresenter;
+ protected IStatusBarService mBarService;
+ protected Callback mCallback;
+
+ private final RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
+
+ @Override
+ public boolean onClickHandler(
+ final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
+ mPresenter.wakeUpIfDozing(SystemClock.uptimeMillis(), view);
+
+ if (handleRemoteInput(view, pendingIntent)) {
+ return true;
+ }
+
+ if (DEBUG) {
+ Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
+ }
+ logActionClick(view);
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ try {
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+ return mCallback.handleRemoteViewClick(view, pendingIntent, fillInIntent,
+ () -> superOnClickHandler(view, pendingIntent, fillInIntent));
+ }
+
+ private void logActionClick(View view) {
+ ViewParent parent = view.getParent();
+ String key = getNotificationKeyForParent(parent);
+ if (key == null) {
+ Log.w(TAG, "Couldn't determine notification for click.");
+ return;
+ }
+ int index = -1;
+ // If this is a default template, determine the index of the button.
+ if (view.getId() == com.android.internal.R.id.action0 &&
+ parent != null && parent instanceof ViewGroup) {
+ ViewGroup actionGroup = (ViewGroup) parent;
+ index = actionGroup.indexOfChild(view);
+ }
+ try {
+ mBarService.onNotificationActionClick(key, index);
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ }
+
+ private String getNotificationKeyForParent(ViewParent parent) {
+ while (parent != null) {
+ if (parent instanceof ExpandableNotificationRow) {
+ return ((ExpandableNotificationRow) parent)
+ .getStatusBarNotification().getKey();
+ }
+ parent = parent.getParent();
+ }
+ return null;
+ }
+
+ private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
+ Intent fillInIntent) {
+ return super.onClickHandler(view, pendingIntent, fillInIntent,
+ WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+ }
+
+ private boolean handleRemoteInput(View view, PendingIntent pendingIntent) {
+ if (mCallback.shouldHandleRemoteInput(view, pendingIntent)) {
+ return true;
+ }
+
+ Object tag = view.getTag(com.android.internal.R.id.remote_input_tag);
+ RemoteInput[] inputs = null;
+ if (tag instanceof RemoteInput[]) {
+ inputs = (RemoteInput[]) tag;
+ }
+
+ if (inputs == null) {
+ return false;
+ }
+
+ RemoteInput input = null;
+
+ for (RemoteInput i : inputs) {
+ if (i.getAllowFreeFormInput()) {
+ input = i;
+ }
+ }
+
+ if (input == null) {
+ return false;
+ }
+
+ ViewParent p = view.getParent();
+ RemoteInputView riv = null;
+ while (p != null) {
+ if (p instanceof View) {
+ View pv = (View) p;
+ if (pv.isRootNamespace()) {
+ riv = findRemoteInputView(pv);
+ break;
+ }
+ }
+ p = p.getParent();
+ }
+ ExpandableNotificationRow row = null;
+ while (p != null) {
+ if (p instanceof ExpandableNotificationRow) {
+ row = (ExpandableNotificationRow) p;
+ break;
+ }
+ p = p.getParent();
+ }
+
+ if (row == null) {
+ return false;
+ }
+
+ row.setUserExpanded(true);
+
+ if (!mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) {
+ final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
+ if (mLockscreenUserManager.isLockscreenPublicMode(userId)) {
+ mCallback.onLockedRemoteInput(row, view);
+ return true;
+ }
+ if (mUserManager.getUserInfo(userId).isManagedProfile()
+ && mPresenter.isDeviceLocked(userId)) {
+ mCallback.onLockedWorkRemoteInput(userId, row, view);
+ return true;
+ }
+ }
+
+ if (riv == null) {
+ riv = findRemoteInputView(row.getPrivateLayout().getExpandedChild());
+ if (riv == null) {
+ return false;
+ }
+ if (!row.getPrivateLayout().getExpandedChild().isShown()) {
+ mCallback.onMakeExpandedVisibleForRemoteInput(row, view);
+ return true;
+ }
+ }
+
+ int width = view.getWidth();
+ if (view instanceof TextView) {
+ // Center the reveal on the text which might be off-center from the TextView
+ TextView tv = (TextView) view;
+ if (tv.getLayout() != null) {
+ int innerWidth = (int) tv.getLayout().getLineWidth(0);
+ innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
+ width = Math.min(width, innerWidth);
+ }
+ }
+ int cx = view.getLeft() + width / 2;
+ int cy = view.getTop() + view.getHeight() / 2;
+ int w = riv.getWidth();
+ int h = riv.getHeight();
+ int r = Math.max(
+ Math.max(cx + cy, cx + (h - cy)),
+ Math.max((w - cx) + cy, (w - cx) + (h - cy)));
+
+ riv.setRevealParameters(cx, cy, r);
+ riv.setPendingIntent(pendingIntent);
+ riv.setRemoteInput(inputs, input);
+ riv.focusAnimated();
+
+ return true;
+ }
+
+ private RemoteInputView findRemoteInputView(View v) {
+ if (v == null) {
+ return null;
+ }
+ return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG);
+ }
+ };
+
+ public NotificationRemoteInputManager(NotificationLockscreenUserManager lockscreenUserManager,
+ Context context) {
+ mLockscreenUserManager = lockscreenUserManager;
+ mContext = context;
+ mBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ }
+
+ public void setUpWithPresenter(NotificationPresenter presenter,
+ Callback callback,
+ RemoteInputController.Delegate delegate) {
+ mPresenter = presenter;
+ mCallback = callback;
+ mRemoteInputController = new RemoteInputController(delegate);
+ mRemoteInputController.addCallback(new RemoteInputController.Callback() {
+ @Override
+ public void onRemoteInputSent(NotificationData.Entry entry) {
+ if (FORCE_REMOTE_INPUT_HISTORY && mKeysKeptForRemoteInput.contains(entry.key)) {
+ mPresenter.removeNotification(entry.key, null);
+ } else if (mRemoteInputEntriesToRemoveOnCollapse.contains(entry)) {
+ // We're currently holding onto this notification, but from the apps point of
+ // view it is already canceled, so we'll need to cancel it on the apps behalf
+ // after sending - unless the app posts an update in the mean time, so wait a
+ // bit.
+ mPresenter.getHandler().postDelayed(() -> {
+ if (mRemoteInputEntriesToRemoveOnCollapse.remove(entry)) {
+ mPresenter.removeNotification(entry.key, null);
+ }
+ }, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
+ }
+ }
+ });
+
+ }
+
+ public RemoteInputController getController() {
+ return mRemoteInputController;
+ }
+
+ public void onUpdateNotification(NotificationData.Entry entry) {
+ mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
+ }
+
+ /**
+ * Returns true if NotificationRemoteInputManager wants to keep this notification around.
+ *
+ * @param entry notification being removed
+ */
+ public boolean onRemoveNotification(NotificationData.Entry entry) {
+ if (entry != null && mRemoteInputController.isRemoteInputActive(entry)
+ && (entry.row != null && !entry.row.isDismissed())) {
+ mRemoteInputEntriesToRemoveOnCollapse.add(entry);
+ return true;
+ }
+ return false;
+ }
+
+ public void onPerformRemoveNotification(StatusBarNotification n,
+ NotificationData.Entry entry) {
+ if (mRemoteInputController.isRemoteInputActive(entry)) {
+ mRemoteInputController.removeRemoteInput(entry, null);
+ }
+ if (FORCE_REMOTE_INPUT_HISTORY
+ && mKeysKeptForRemoteInput.contains(n.getKey())) {
+ mKeysKeptForRemoteInput.remove(n.getKey());
+ }
+ }
+
+ public void removeRemoteInputEntriesKeptUntilCollapsed() {
+ for (int i = 0; i < mRemoteInputEntriesToRemoveOnCollapse.size(); i++) {
+ NotificationData.Entry entry = mRemoteInputEntriesToRemoveOnCollapse.valueAt(i);
+ mRemoteInputController.removeRemoteInput(entry, null);
+ mPresenter.removeNotification(entry.key, mPresenter.getLatestRankingMap());
+ }
+ mRemoteInputEntriesToRemoveOnCollapse.clear();
+ }
+
+ public void checkRemoteInputOutside(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
+ && event.getX() == 0 && event.getY() == 0 // a touch outside both bars
+ && mRemoteInputController.isRemoteInputActive()) {
+ mRemoteInputController.closeRemoteInputs();
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("NotificationRemoteInputManager state:");
+ pw.print(" mRemoteInputEntriesToRemoveOnCollapse: ");
+ pw.println(mRemoteInputEntriesToRemoveOnCollapse);
+ pw.print(" mKeysKeptForRemoteInput: ");
+ pw.println(mKeysKeptForRemoteInput);
+ }
+
+ public void bindRow(ExpandableNotificationRow row) {
+ row.setRemoteInputController(mRemoteInputController);
+ row.setRemoteViewClickHandler(mOnClickHandler);
+ }
+
+ public Set<String> getKeysKeptForRemoteInput() {
+ return mKeysKeptForRemoteInput;
+ }
+
+ @VisibleForTesting
+ public Set<NotificationData.Entry> getRemoteInputEntriesToRemoveOnCollapse() {
+ return mRemoteInputEntriesToRemoveOnCollapse;
+ }
+
+ /**
+ * Callback for various remote input related events, or for providing information that
+ * NotificationRemoteInputManager needs to know to decide what to do.
+ */
+ public interface Callback {
+
+ /**
+ * Called when remote input was activated but the device is locked.
+ *
+ * @param row
+ * @param clicked
+ */
+ void onLockedRemoteInput(ExpandableNotificationRow row, View clicked);
+
+ /**
+ * Called when remote input was activated but the device is locked and in a managed profile.
+ *
+ * @param userId
+ * @param row
+ * @param clicked
+ */
+ void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row, View clicked);
+
+ /**
+ * Called when a row should be made expanded for the purposes of remote input.
+ *
+ * @param row
+ * @param clickedView
+ */
+ void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView);
+
+ /**
+ * Return whether or not remote input should be handled for this view.
+ *
+ * @param view
+ * @param pendingIntent
+ * @return true iff the remote input should be handled
+ */
+ boolean shouldHandleRemoteInput(View view, PendingIntent pendingIntent);
+
+ /**
+ * Performs any special handling for a remote view click. The default behaviour can be
+ * called through the defaultHandler parameter.
+ *
+ * @param view
+ * @param pendingIntent
+ * @param fillInIntent
+ * @param defaultHandler
+ * @return true iff the click was handled
+ */
+ boolean handleRemoteViewClick(View view, PendingIntent pendingIntent, Intent fillInIntent,
+ ClickHandler defaultHandler);
+ }
+
+ /**
+ * Helper interface meant for passing the default on click behaviour to NotificationPresenter,
+ * so it may do its own handling before invoking the default behaviour.
+ */
+ public interface ClickHandler {
+ /**
+ * Tries to handle a click on a remote view.
+ *
+ * @return true iff the click was handled
+ */
+ boolean handleClick();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 3f57c2f..6d85fb3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -26,6 +26,7 @@
import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.systemui.R;
+import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import java.io.PrintWriter;
@@ -37,10 +38,12 @@
private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
private static IntInOutMatcher sPickupSubtypePerformsProxMatcher;
+ private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
public DozeParameters(Context context) {
mContext = context;
mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
+ mAlwaysOnPolicy = new AlwaysOnDisplayPolicy(context);
}
public void dump(PrintWriter pw) {
@@ -83,6 +86,11 @@
return getPulseInDuration() + getPulseVisibleDuration() + getPulseOutDuration();
}
+ public float getScreenBrightnessDoze() {
+ return mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_screenBrightnessDoze) / 255f;
+ }
+
public int getPulseInDuration() {
return getInt("doze.pulse.duration.in", R.integer.doze_pulse_duration_in);
}
@@ -115,10 +123,52 @@
return getInt("doze.pickup.vibration.threshold", R.integer.doze_pickup_vibration_threshold);
}
+ /**
+ * For how long a wallpaper can be visible in AoD before it fades aways.
+ * @return duration in millis.
+ */
+ public long getWallpaperAodDuration() {
+ return mAlwaysOnPolicy.wallpaperVisibilityDuration;
+ }
+
+ /**
+ * How long it takes for the wallpaper fade away (Animation duration.)
+ * @return duration in millis.
+ */
+ public long getWallpaperFadeOutDuration() {
+ return mAlwaysOnPolicy.wallpaperFadeOutDuration;
+ }
+
+ /**
+ * Checks if always on is available and enabled for the current user.
+ * @return {@code true} if enabled and available.
+ */
public boolean getAlwaysOn() {
return mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT);
}
+ /**
+ * Some screens need to be completely black before changing the display power mode,
+ * unexpected behavior might happen if this parameter isn't respected.
+ *
+ * @return {@code true} if screen needs to be completely black before a power transition.
+ */
+ public boolean getDisplayNeedsBlanking() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_displayBlanksAfterDoze);
+ }
+
+ /**
+ * Whether we can implement our own screen off animation or if we need
+ * to rely on DisplayPowerManager to dim the display.
+ *
+ * @return {@code true} if SystemUI can control the screen off animation.
+ */
+ public boolean getCanControlScreenOffAnimation() {
+ return !mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_dozeAfterScreenOff);
+ }
+
private boolean getBoolean(String propName, int resId) {
return SystemProperties.getBoolean(propName, mContext.getResources().getBoolean(resId));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index c281379..b71ebfd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone;
-import android.app.ActivityManager;
import android.content.Context;
import android.os.Handler;
import android.os.UserHandle;
@@ -122,6 +121,8 @@
// Split up the work over multiple frames.
DejankUtils.postAfterTraversal(mShowRunnable);
+
+ mCallback.onBouncerVisiblityChanged(true /* shown */);
}
private final Runnable mShowRunnable = new Runnable() {
@@ -182,6 +183,7 @@
mDismissCallbackRegistry.notifyDismissCancelled();
}
mFalsingManager.onBouncerHidden();
+ mCallback.onBouncerVisiblityChanged(false /* shown */);
cancelShowRunnable();
if (mKeyguardView != null) {
mKeyguardView.cancelDismissAction();
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 61b007f..695168e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -69,6 +69,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
import com.android.systemui.Dependency;
+import com.android.systemui.OverviewProxyService;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.assist.AssistManager;
@@ -125,6 +126,8 @@
private int mSystemUiVisibility;
private LightBarController mLightBarController;
+ private OverviewProxyService mOverviewProxyService;
+
public boolean mHomeBlockedThisTouch;
// ----- Fragment Lifecycle Callbacks -----
@@ -152,6 +155,7 @@
mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
}
mAssistManager = Dependency.get(AssistManager.class);
+ mOverviewProxyService = Dependency.get(OverviewProxyService.class);
try {
WindowManagerGlobal.getWindowManagerService()
@@ -364,7 +368,8 @@
private boolean shouldDisableNavbarGestures() {
return !mStatusBar.isDeviceProvisioned()
- || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0;
+ || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0
+ || mOverviewProxyService.getProxy() != null;
}
private void repositionNavigationBar() {
@@ -449,6 +454,7 @@
MetricsLogger.action(getContext(), MetricsEvent.ACTION_ASSIST_LONG_PRESS);
mAssistManager.startAssist(new Bundle() /* args */);
mStatusBar.awakenDreams();
+
if (mNavigationBarView != null) {
mNavigationBarView.abortCurrentGesture();
}
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 4e7f205..2796f0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -98,6 +98,7 @@
private GestureHelper mGestureHelper;
private DeadZone mDeadZone;
private final NavigationBarTransitions mBarTransitions;
+ private final OverviewProxyService mOverviewProxyService;
// workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288)
final static boolean WORKAROUND_INVALID_LAYOUT = true;
@@ -226,6 +227,7 @@
mButtonDispatchers.put(R.id.ime_switcher, new ButtonDispatcher(R.id.ime_switcher));
mButtonDispatchers.put(R.id.accessibility_button,
new ButtonDispatcher(R.id.accessibility_button));
+ mOverviewProxyService = Dependency.get(OverviewProxyService.class);
}
public BarTransitions getBarTransitions() {
@@ -464,6 +466,10 @@
disableBack = false;
disableRecent = false;
}
+ if (mOverviewProxyService.getProxy() != null) {
+ // When overview is connected to the launcher service, disable the recents button
+ disableRecent = true;
+ }
ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
if (navButtons != null) {
@@ -779,7 +785,7 @@
onPluginDisconnected(null); // Create default gesture helper
Dependency.get(PluginManager.class).addPluginListener(this,
NavGesture.class, false /* Only one */);
- Dependency.get(OverviewProxyService.class).addCallback(mOverviewProxyListener);
+ mOverviewProxyService.addCallback(mOverviewProxyListener);
}
@Override
@@ -789,7 +795,7 @@
if (mGestureHelper != null) {
mGestureHelper.destroy();
}
- Dependency.get(OverviewProxyService.class).removeCallback(mOverviewProxyListener);
+ mOverviewProxyService.removeCallback(mOverviewProxyListener);
}
@Override
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 05fcbb0..14329b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -20,6 +20,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
+import android.app.AlarmManager;
import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Color;
@@ -51,6 +52,7 @@
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.stack.ViewState;
+import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -73,6 +75,19 @@
= new PathInterpolator(0f, 0, 0.7f, 1f);
public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR_LOCKED
= new PathInterpolator(0.3f, 0f, 0.8f, 1f);
+
+ /**
+ * When both scrims have 0 alpha.
+ */
+ public static final int VISIBILITY_FULLY_TRANSPARENT = 0;
+ /**
+ * When scrims aren't transparent (alpha 0) but also not opaque (alpha 1.)
+ */
+ public static final int VISIBILITY_SEMI_TRANSPARENT = 1;
+ /**
+ * When at least 1 scrim is fully opaque (alpha set to 1.)
+ */
+ public static final int VISIBILITY_FULLY_OPAQUE = 2;
/**
* Default alpha value for most scrims.
*/
@@ -111,6 +126,7 @@
private final UnlockMethodCache mUnlockMethodCache;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final DozeParameters mDozeParameters;
+ private final AlarmTimeout mTimeTicker;
private final SysuiColorExtractor mColorExtractor;
private GradientColors mLockColors;
@@ -138,23 +154,25 @@
private float mCurrentBehindAlpha = NOT_INITIALIZED;
private int mCurrentInFrontTint;
private int mCurrentBehindTint;
+ private boolean mWallpaperVisibilityTimedOut;
private int mPinnedHeadsUpCount;
private float mTopHeadsUpDragAmount;
private View mDraggedHeadsUpView;
private boolean mKeyguardFadingOutInProgress;
private ValueAnimator mKeyguardFadeoutAnimation;
- private boolean mScrimsVisible;
- private final Consumer<Boolean> mScrimVisibleListener;
+ private int mScrimsVisibility;
+ private final Consumer<Integer> mScrimVisibleListener;
private boolean mBlankScreen;
private boolean mScreenBlankingCallbackCalled;
private Callback mCallback;
+ private boolean mWallpaperSupportsAmbientMode;
private final WakeLock mWakeLock;
private boolean mWakeLockHeld;
public ScrimController(LightBarController lightBarController, ScrimView scrimBehind,
- ScrimView scrimInFront, View headsUpScrim, Consumer<Boolean> scrimVisibleListener,
- DozeParameters dozeParameters) {
+ ScrimView scrimInFront, View headsUpScrim, Consumer<Integer> scrimVisibleListener,
+ DozeParameters dozeParameters, AlarmManager alarmManager) {
mScrimBehind = scrimBehind;
mScrimInFront = scrimInFront;
mHeadsUpScrim = headsUpScrim;
@@ -164,6 +182,8 @@
mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
mLightBarController = lightBarController;
mScrimBehindAlphaResValue = mContext.getResources().getFloat(R.dimen.scrim_behind_alpha);
+ mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout,
+ "hide_aod_wallpaper", new Handler());
mWakeLock = createWakeLock();
// Scrim alpha is initially set to the value on the resource but might be changed
// to make sure that text on top of it is legible.
@@ -195,6 +215,10 @@
public void transitionTo(ScrimState state, Callback callback) {
if (state == mState) {
+ // Call the callback anyway, unless it's already enqueued
+ if (callback != null && mCallback != callback) {
+ callback.onFinished();
+ }
return;
} else if (DEBUG) {
Log.d(TAG, "State changed to: " + state);
@@ -231,14 +255,24 @@
mKeyguardFadeoutAnimation.cancel();
}
- // Do not let the device sleep until we're done with all animations
- if (!mWakeLockHeld) {
- if (mWakeLock != null) {
- mWakeLockHeld = true;
- mWakeLock.acquire();
- } else {
- Log.w(TAG, "Cannot hold wake lock, it has not been set yet");
+ // The device might sleep if it's entering AOD, we need to make sure that
+ // the animation plays properly until the last frame.
+ // It's important to avoid holding the wakelock unless necessary because
+ // WakeLock#aqcuire will trigger an IPC and will cause jank.
+ if (mState == ScrimState.AOD) {
+ holdWakeLock();
+ }
+
+ // AOD wallpapers should fade away after a while
+ if (mWallpaperSupportsAmbientMode && mDozeParameters.getAlwaysOn()
+ && (mState == ScrimState.AOD || mState == ScrimState.PULSING)) {
+ if (!mWallpaperVisibilityTimedOut) {
+ mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
+ AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
}
+ } else {
+ mTimeTicker.cancel();
+ mWallpaperVisibilityTimedOut = false;
}
if (!mKeyguardUpdateMonitor.needsSlowUnlockTransition()) {
@@ -275,6 +309,30 @@
mTracking = false;
}
+ @VisibleForTesting
+ protected void onHideWallpaperTimeout() {
+ if (mState != ScrimState.AOD && mState != ScrimState.PULSING) {
+ return;
+ }
+
+ holdWakeLock();
+ mWallpaperVisibilityTimedOut = true;
+ mAnimateChange = true;
+ mAnimationDuration = mDozeParameters.getWallpaperFadeOutDuration();
+ scheduleUpdate();
+ }
+
+ private void holdWakeLock() {
+ if (!mWakeLockHeld) {
+ if (mWakeLock != null) {
+ mWakeLockHeld = true;
+ mWakeLock.acquire();
+ } else {
+ Log.w(TAG, "Cannot hold wake lock, it has not been set yet");
+ }
+ }
+ }
+
/**
* Current state of the shade expansion when pulling it from the top.
* This value is 1 when on top of the keyguard and goes to 0 as the user drags up.
@@ -387,6 +445,14 @@
mLightBarController.setScrimColor(mScrimInFront.getColors());
}
+ // We want to override the back scrim opacity for AOD and PULSING
+ // when it's time to fade the wallpaper away.
+ boolean overrideBackScrimAlpha = (mState == ScrimState.PULSING || mState == ScrimState.AOD)
+ && mWallpaperVisibilityTimedOut;
+ if (overrideBackScrimAlpha) {
+ mCurrentBehindAlpha = 1;
+ }
+
setScrimInFrontAlpha(mCurrentInFrontAlpha);
setScrimBehindAlpha(mCurrentBehindAlpha);
@@ -394,12 +460,18 @@
}
private void dispatchScrimsVisible() {
- boolean scrimsVisible = mScrimBehind.getViewAlpha() > 0 || mScrimInFront.getViewAlpha() > 0;
+ final int currentScrimVisibility;
+ if (mScrimInFront.getViewAlpha() == 1 || mScrimBehind.getViewAlpha() == 1) {
+ currentScrimVisibility = VISIBILITY_FULLY_OPAQUE;
+ } else if (mScrimInFront.getViewAlpha() == 0 && mScrimBehind.getViewAlpha() == 0) {
+ currentScrimVisibility = VISIBILITY_FULLY_TRANSPARENT;
+ } else {
+ currentScrimVisibility = VISIBILITY_SEMI_TRANSPARENT;
+ }
- if (mScrimsVisible != scrimsVisible) {
- mScrimsVisible = scrimsVisible;
-
- mScrimVisibleListener.accept(scrimsVisible);
+ if (mScrimsVisibility != currentScrimVisibility) {
+ mScrimsVisibility = currentScrimVisibility;
+ mScrimVisibleListener.accept(currentScrimVisibility);
}
}
@@ -807,6 +879,14 @@
pw.print(" mTracking="); pw.println(mTracking);
}
+ public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
+ mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode;
+ ScrimState[] states = ScrimState.values();
+ for (int i = 0; i < states.length; i++) {
+ states[i].setWallpaperSupportsAmbientMode(wallpaperSupportsAmbientMode);
+ }
+ }
+
public interface Callback {
default void onStart() {
}
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 0db98f3..fa2c1b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -19,7 +19,9 @@
import android.graphics.Color;
import android.os.Trace;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.statusbar.ScrimView;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
/**
* Possible states of the ScrimController state machine.
@@ -38,12 +40,18 @@
@Override
public void prepare(ScrimState previousState) {
- // DisplayPowerManager will blank the screen, we'll just
- // set our scrim to black in this frame to avoid flickering and
- // fade it out afterwards.
- mBlankScreen = previousState == ScrimState.AOD;
+ mBlankScreen = false;
if (previousState == ScrimState.AOD) {
- updateScrimColor(mScrimInFront, 1, Color.BLACK);
+ mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
+ if (mDisplayRequiresBlanking) {
+ // DisplayPowerManager will blank the screen, we'll just
+ // 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;
}
mCurrentBehindAlpha = mScrimBehindAlphaKeyguard;
mCurrentInFrontAlpha = 0;
@@ -78,18 +86,20 @@
AOD {
@Override
public void prepare(ScrimState previousState) {
- if (previousState == ScrimState.PULSING) {
+ if (previousState == ScrimState.PULSING && !mCanControlScreenOff) {
updateScrimColor(mScrimInFront, 1, Color.BLACK);
}
final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
- mBlankScreen = previousState == ScrimState.PULSING;
- mCurrentBehindAlpha = 1;
+ final boolean wasPulsing = previousState == ScrimState.PULSING;
+ mBlankScreen = wasPulsing && !mCanControlScreenOff;
+ mCurrentBehindAlpha = mWallpaperSupportsAmbientMode
+ && !mKeyguardUpdateMonitor.hasLockscreenWallpaper() ? 0f : 1f;
mCurrentInFrontAlpha = alwaysOnEnabled ? mAodFrontScrimAlpha : 1f;
mCurrentInFrontTint = Color.BLACK;
mCurrentBehindTint = Color.BLACK;
// DisplayPowerManager will blank the screen for us, we just need
// to set our state.
- mAnimateChange = false;
+ mAnimateChange = mCanControlScreenOff;
}
},
@@ -99,12 +109,15 @@
PULSING {
@Override
public void prepare(ScrimState previousState) {
- mCurrentBehindAlpha = 1;
mCurrentInFrontAlpha = 0;
mCurrentInFrontTint = Color.BLACK;
+ mCurrentBehindAlpha = mWallpaperSupportsAmbientMode
+ && !mKeyguardUpdateMonitor.hasLockscreenWallpaper() ? 0f : 1f;
mCurrentBehindTint = Color.BLACK;
- mBlankScreen = true;
- updateScrimColor(mScrimInFront, 1, Color.BLACK);
+ mBlankScreen = mDisplayRequiresBlanking;
+ if (mDisplayRequiresBlanking) {
+ updateScrimColor(mScrimInFront, 1, Color.BLACK);
+ }
}
},
@@ -147,11 +160,18 @@
ScrimView mScrimInFront;
ScrimView mScrimBehind;
DozeParameters mDozeParameters;
+ boolean mDisplayRequiresBlanking;
+ boolean mCanControlScreenOff;
+ boolean mWallpaperSupportsAmbientMode;
+ KeyguardUpdateMonitor mKeyguardUpdateMonitor;
public void init(ScrimView scrimInFront, ScrimView scrimBehind, DozeParameters dozeParameters) {
mScrimInFront = scrimInFront;
mScrimBehind = scrimBehind;
mDozeParameters = dozeParameters;
+ mDisplayRequiresBlanking = dozeParameters.getDisplayNeedsBlanking();
+ mCanControlScreenOff = dozeParameters.getCanControlScreenOffAnimation();
+ mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(scrimInFront.getContext());
}
public void prepare(ScrimState previousState) {
@@ -205,4 +225,8 @@
public void setScrimBehindAlphaKeyguard(float scrimBehindAlphaKeyguard) {
mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
}
+
+ public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
+ mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode;
+ }
}
\ No newline at end of file
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 c61b7e8..c5349d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -28,6 +28,9 @@
.NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
import static com.android.systemui.statusbar.NotificationMediaManager.DEBUG_MEDIA;
+import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
+import static com.android.systemui.statusbar.NotificationRemoteInputManager
+ .FORCE_REMOTE_INPUT_HISTORY;
import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -43,14 +46,15 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.AlarmManager;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.RemoteInput;
import android.app.StatusBarManager;
import android.app.TaskStackBuilder;
import android.app.WallpaperColors;
+import android.app.WallpaperInfo;
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
@@ -126,7 +130,6 @@
import android.view.animation.AccelerateInterpolator;
import android.widget.DateTimeView;
import android.widget.ImageView;
-import android.widget.RemoteViews;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
@@ -135,7 +138,6 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.NotificationMessagingUtil;
import com.android.internal.widget.LockPatternUtils;
@@ -202,8 +204,10 @@
import com.android.systemui.statusbar.NotificationInfo;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationLogger;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.ScrimView;
@@ -230,13 +234,10 @@
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.PreviewInflater;
-import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout
- .OnChildLocationsChangedListener;
import com.android.systemui.util.NotificationChannels;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.volume.VolumeComponent;
@@ -246,11 +247,9 @@
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.Stack;
public class StatusBar extends SystemUI implements DemoMode,
@@ -262,12 +261,8 @@
ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
public static final boolean MULTIUSER_DEBUG = false;
- public static final boolean ENABLE_REMOTE_INPUT =
- SystemProperties.getBoolean("debug.enable_remote_input", true);
public static final boolean ENABLE_CHILD_NOTIFICATIONS
= SystemProperties.getBoolean("debug.child_notifs", true);
- public static final boolean FORCE_REMOTE_INPUT_HISTORY =
- SystemProperties.getBoolean("debug.force_remoteinput_history", true);
protected static final int MSG_HIDE_RECENT_APPS = 1020;
protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
@@ -320,9 +315,6 @@
View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
private static final long AUTOHIDE_TIMEOUT_MS = 2250;
- /** The minimum delay in ms between reports of notification visibility. */
- private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
-
/**
* The delay to reset the hint text when the hint animation is finished running.
*/
@@ -345,14 +337,6 @@
private static final boolean ENABLE_LOCKSCREEN_WALLPAPER = true;
/**
- * How long to wait before auto-dismissing a notification that was kept for remote input, and
- * has now sent a remote input. We auto-dismiss, because the app may not see a reason to cancel
- * these given that they technically don't exist anymore. We wait a bit in case the app issues
- * an update.
- */
- private static final int REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY = 200;
-
- /**
* Never let the alpha become zero for surfaces that draw with SRC - otherwise the RenderNode
* won't draw anything and uninitialized memory will show through
* if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in
@@ -442,6 +426,7 @@
private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
private NotificationGutsManager mGutsManager;
+ protected NotificationLogger mNotificationLogger;
// for disabling the status bar
private int mDisabled1 = 0;
@@ -540,11 +525,24 @@
private NotificationMediaManager mMediaManager;
protected NotificationLockscreenUserManager mLockscreenUserManager;
+ protected NotificationRemoteInputManager mRemoteInputManager;
- /** Keys of notifications currently visible to the user. */
- private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications =
- new ArraySet<>();
- private long mLastVisibilityReportUptimeMs;
+ private BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ WallpaperManager wallpaperManager = context.getSystemService(WallpaperManager.class);
+ if (wallpaperManager == null) {
+ Log.w(TAG, "WallpaperManager not available");
+ return;
+ }
+ WallpaperInfo info = wallpaperManager.getWallpaperInfo();
+ final boolean supportsAmbientMode = info != null &&
+ info.getSupportsAmbientMode();
+
+ mStatusBarWindowManager.setWallpaperSupportsAmbientMode(supportsAmbientMode);
+ mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
+ }
+ };
private Runnable mLaunchTransitionEndRunnable;
protected boolean mLaunchTransitionFadingAway;
@@ -567,83 +565,6 @@
private boolean mWereIconsJustHidden;
private boolean mBouncerWasShowingWhenHidden;
- private final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
- new OnChildLocationsChangedListener() {
- @Override
- public void onChildLocationsChanged(
- NotificationStackScrollLayout stackScrollLayout) {
- if (mHandler.hasCallbacks(mVisibilityReporter)) {
- // Visibilities will be reported when the existing
- // callback is executed.
- return;
- }
- // Calculate when we're allowed to run the visibility
- // reporter. Note that this timestamp might already have
- // passed. That's OK, the callback will just be executed
- // ASAP.
- long nextReportUptimeMs =
- mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS;
- mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs);
- }
- };
-
- // Tracks notifications currently visible in mNotificationStackScroller and
- // emits visibility events via NoMan on changes.
- protected final Runnable mVisibilityReporter = new Runnable() {
- private final ArraySet<NotificationVisibility> mTmpNewlyVisibleNotifications =
- new ArraySet<>();
- private final ArraySet<NotificationVisibility> mTmpCurrentlyVisibleNotifications =
- new ArraySet<>();
- private final ArraySet<NotificationVisibility> mTmpNoLongerVisibleNotifications =
- new ArraySet<>();
-
- @Override
- public void run() {
- mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
-
- // 1. Loop over mNotificationData entries:
- // A. Keep list of visible notifications.
- // B. Keep list of previously hidden, now visible notifications.
- // 2. Compute no-longer visible notifications by removing currently
- // visible notifications from the set of previously visible
- // notifications.
- // 3. Report newly visible and no-longer visible notifications.
- // 4. Keep currently visible notifications for next report.
- ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
- int N = activeNotifications.size();
- for (int i = 0; i < N; i++) {
- Entry entry = activeNotifications.get(i);
- String key = entry.notification.getKey();
- boolean isVisible = mStackScroller.isInVisibleLocation(entry.row);
- NotificationVisibility visObj = NotificationVisibility.obtain(key, i, isVisible);
- boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj);
- if (isVisible) {
- // Build new set of visible notifications.
- mTmpCurrentlyVisibleNotifications.add(visObj);
- if (!previouslyVisible) {
- mTmpNewlyVisibleNotifications.add(visObj);
- }
- } else {
- // release object
- visObj.recycle();
- }
- }
- mTmpNoLongerVisibleNotifications.addAll(mCurrentlyVisibleNotifications);
- mTmpNoLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications);
-
- logNotificationVisibilityChanges(
- mTmpNewlyVisibleNotifications, mTmpNoLongerVisibleNotifications);
-
- recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
- mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications);
-
- recycleAllVisibilityObjects(mTmpNoLongerVisibleNotifications);
- mTmpCurrentlyVisibleNotifications.clear();
- mTmpNewlyVisibleNotifications.clear();
- mTmpNoLongerVisibleNotifications.clear();
- }
- };
-
// Notifies StatusBarKeyguardViewManager every time the keyguard transition is over,
// this animation is tied to the scrim for historic reasons.
// TODO: notify when keyguard has faded away instead of the scrim.
@@ -656,7 +577,9 @@
+ "mStatusBarKeyguardViewManager was null");
return;
}
- mStatusBarKeyguardViewManager.onKeyguardFadedAway();
+ if (mKeyguardFadingAway) {
+ mStatusBarKeyguardViewManager.onKeyguardFadedAway();
+ }
}
@Override
@@ -688,14 +611,6 @@
private ScreenLifecycle mScreenLifecycle;
@VisibleForTesting WakefulnessLifecycle mWakefulnessLifecycle;
- private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
- final int N = array.size();
- for (int i = 0 ; i < N; i++) {
- array.valueAt(i).recycle();
- }
- array.clear();
- }
-
private final View.OnClickListener mGoToLockedShadeListener = v -> {
if (mState == StatusBarState.KEYGUARD) {
wakeUpIfDozing(SystemClock.uptimeMillis(), v);
@@ -723,6 +638,9 @@
@Override
public void start() {
+ mGroupManager = Dependency.get(NotificationGroupManager.class);
+ mNotificationLogger = Dependency.get(NotificationLogger.class);
+ mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
mNetworkController = Dependency.get(NetworkController.class);
mUserSwitcherController = Dependency.get(UserSwitcherController.class);
mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
@@ -777,7 +695,6 @@
mRecents = getComponent(Recents.class);
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
mLockPatternUtils = new LockPatternUtils(mContext);
@@ -803,6 +720,11 @@
createAndAddWindows();
+ // Make sure we always have the most current wallpaper info.
+ IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
+ mContext.registerReceiver(mWallpaperChangedReceiver, wallpaperChangedFilter);
+ mWallpaperChangedReceiver.onReceive(mContext, null);
+
mCommandQueue.disable(switches[0], switches[6], false /* animate */);
setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
fullscreenStackBounds, dockedStackBounds);
@@ -817,8 +739,8 @@
}
// Set up the initial notification state.
- mNotificationListener = new NotificationListener(this, mContext);
- mNotificationListener.register();
+ mNotificationListener = Dependency.get(NotificationListener.class);
+ mNotificationListener.setUpWithPresenter(this);
if (DEBUG) {
Log.d(TAG, String.format(
@@ -903,6 +825,7 @@
// if we're here we're dead
}
});
+ mNotificationLogger.setUpWithPresenter(this, mStackScroller);
mNotificationPanel.setStatusBar(this);
mNotificationPanel.setGroupManager(mGroupManager);
mAboveShelfObserver = new AboveShelfObserver(mStackScroller);
@@ -1020,9 +943,9 @@
scrimBehind, scrimInFront, headsUpScrim, mLockscreenWallpaper,
scrimsVisible -> {
if (mStatusBarWindowManager != null) {
- mStatusBarWindowManager.setScrimsVisible(scrimsVisible);
+ mStatusBarWindowManager.setScrimsVisibility(scrimsVisible);
}
- }, new DozeParameters(mContext));
+ }, new DozeParameters(mContext), mContext.getSystemService(AlarmManager.class));
if (mScrimSrcModeEnabled) {
Runnable runnable = () -> {
boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE;
@@ -1159,7 +1082,7 @@
protected View.OnTouchListener getStatusBarWindowTouchListener() {
return (v, event) -> {
checkUserAutohide(event);
- checkRemoteInputOutside(event);
+ mRemoteInputManager.checkRemoteInputOutside(event);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (mExpandedVisible) {
animateCollapsePanels();
@@ -1432,31 +1355,7 @@
mKeyguardIndicationController
.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
- mRemoteInputController.addCallback(mStatusBarKeyguardViewManager);
-
- mRemoteInputController.addCallback(new RemoteInputController.Callback() {
- @Override
- public void onRemoteInputSent(Entry entry) {
- if (FORCE_REMOTE_INPUT_HISTORY && mKeysKeptForRemoteInput.contains(entry.key)) {
- removeNotification(entry.key, null);
- } else if (mRemoteInputEntriesToRemoveOnCollapse.contains(entry)) {
- // We're currently holding onto this notification, but from the apps point of
- // view it is already canceled, so we'll need to cancel it on the apps behalf
- // after sending - unless the app posts an update in the mean time, so wait a
- // bit.
- mHandler.postDelayed(() -> {
- if (mRemoteInputEntriesToRemoveOnCollapse.remove(entry)) {
- removeNotification(entry.key, null);
- }
- }, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
- }
- try {
- mBarService.onNotificationDirectReplied(entry.key);
- } catch (RemoteException e) {
- // system process is dead if we're here.
- }
- }
- });
+ mRemoteInputManager.getController().addCallback(mStatusBarKeyguardViewManager);
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
mLightBarController.setFingerprintUnlockController(mFingerprintUnlockController);
@@ -1633,7 +1532,7 @@
// sending look longer than it takes.
// Also we should not defer the removal if reordering isn't allowed since otherwise
// some notifications can't disappear before the panel is closed.
- boolean ignoreEarliestRemovalTime = mRemoteInputController.isSpinning(key)
+ boolean ignoreEarliestRemovalTime = mRemoteInputManager.getController().isSpinning(key)
&& !FORCE_REMOTE_INPUT_HISTORY
|| !mVisualStabilityManager.isReorderingAllowed();
deferRemoval = !mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
@@ -1641,7 +1540,7 @@
mMediaManager.onNotificationRemoved(key);
Entry entry = mNotificationData.get(key);
- if (FORCE_REMOTE_INPUT_HISTORY && mRemoteInputController.isSpinning(key)
+ if (FORCE_REMOTE_INPUT_HISTORY && mRemoteInputManager.getController().isSpinning(key)
&& entry.row != null && !entry.row.isDismissed()) {
StatusBarNotification sbn = entry.notification;
@@ -1679,7 +1578,7 @@
}
if (updated) {
Log.w(TAG, "Keeping notification around after sending remote input "+ entry.key);
- mKeysKeptForRemoteInput.add(entry.key);
+ mRemoteInputManager.getKeysKeptForRemoteInput().add(entry.key);
return;
}
}
@@ -1689,12 +1588,11 @@
return;
}
- if (entry != null && mRemoteInputController.isRemoteInputActive(entry)
- && (entry.row != null && !entry.row.isDismissed())) {
+ if (mRemoteInputManager.onRemoveNotification(entry)) {
mLatestRankingMap = ranking;
- mRemoteInputEntriesToRemoveOnCollapse.add(entry);
return;
}
+
if (entry != null && mGutsManager.getExposedGuts() != null
&& mGutsManager.getExposedGuts() == entry.row.getGuts()
&& entry.row.getGuts() != null && !entry.row.getGuts().isLeavebehind()) {
@@ -1768,9 +1666,7 @@
protected void performRemoveNotification(StatusBarNotification n) {
Entry entry = mNotificationData.get(n.getKey());
- if (mRemoteInputController.isRemoteInputActive(entry)) {
- mRemoteInputController.removeRemoteInput(entry, null);
- }
+ mRemoteInputManager.onPerformRemoveNotification(n, entry);
// start old BaseStatusBar.performRemoveNotification.
final String pkg = n.getPackageName();
final String tag = n.getTag();
@@ -1783,12 +1679,7 @@
} else if (mStackScroller.hasPulsingNotifications()) {
dismissalSurface = NotificationStats.DISMISSAL_AOD;
}
- mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(),
- dismissalSurface);
- if (FORCE_REMOTE_INPUT_HISTORY
- && mKeysKeptForRemoteInput.contains(n.getKey())) {
- mKeysKeptForRemoteInput.remove(n.getKey());
- }
+ mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(), dismissalSurface);
removeNotification(n.getKey(), null);
} catch (RemoteException ex) {
@@ -2507,7 +2398,7 @@
mStatusBarWindowManager.setHeadsUpShowing(false);
mHeadsUpManager.setHeadsUpGoingAway(false);
}
- removeRemoteInputEntriesKeptUntilCollapsed();
+ mRemoteInputManager.removeRemoteInputEntriesKeptUntilCollapsed();
});
}
}
@@ -2586,19 +2477,10 @@
}
if (!isExpanded) {
- removeRemoteInputEntriesKeptUntilCollapsed();
+ mRemoteInputManager.removeRemoteInputEntriesKeptUntilCollapsed();
}
}
- private void removeRemoteInputEntriesKeptUntilCollapsed() {
- for (int i = 0; i < mRemoteInputEntriesToRemoveOnCollapse.size(); i++) {
- Entry entry = mRemoteInputEntriesToRemoveOnCollapse.valueAt(i);
- mRemoteInputController.removeRemoteInput(entry, null);
- removeNotification(entry.key, mLatestRankingMap);
- }
- mRemoteInputEntriesToRemoveOnCollapse.clear();
- }
-
public NotificationStackScrollLayout getNotificationScrollLayout() {
return mStackScroller;
}
@@ -3182,19 +3064,12 @@
if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0 // a transient bar is revealed
&& event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
&& event.getX() == 0 && event.getY() == 0 // a touch outside both bars
- && !mRemoteInputController.isRemoteInputActive()) { // not due to typing in IME
+ && !mRemoteInputManager.getController()
+ .isRemoteInputActive()) { // not due to typing in IME
userAutohide();
}
}
- private void checkRemoteInputOutside(MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
- && event.getX() == 0 && event.getY() == 0 // a touch outside both bars
- && mRemoteInputController.isRemoteInputActive()) {
- mRemoteInputController.closeRemoteInputs();
- }
- }
-
private void userAutohide() {
cancelAutohide();
mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear
@@ -3375,18 +3250,18 @@
private void addStatusBarWindow() {
makeStatusBarView();
mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
- mRemoteInputController = new RemoteInputController(new RemoteInputController.Delegate() {
- public void setRemoteInputActive(NotificationData.Entry entry,
- boolean remoteInputActive) {
- mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
- }
- public void lockScrollTo(NotificationData.Entry entry) {
- mStackScroller.lockScrollTo(entry.row);
- }
- public void requestDisallowLongPressAndDismiss() {
- mStackScroller.requestDisallowLongPress();
- mStackScroller.requestDisallowDismiss();
- }
+ mRemoteInputManager.setUpWithPresenter(this, this, new RemoteInputController.Delegate() {
+ public void setRemoteInputActive(NotificationData.Entry entry,
+ boolean remoteInputActive) {
+ mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
+ }
+ public void lockScrollTo(NotificationData.Entry entry) {
+ mStackScroller.lockScrollTo(entry.row);
+ }
+ public void requestDisallowLongPressAndDismiss() {
+ mStackScroller.requestDisallowLongPress();
+ mStackScroller.requestDisallowDismiss();
+ }
});
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}
@@ -3506,8 +3381,8 @@
String action = intent.getAction();
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
KeyboardShortcuts.dismiss();
- if (mRemoteInputController != null) {
- mRemoteInputController.closeRemoteInputs();
+ if (mRemoteInputManager.getController() != null) {
+ mRemoteInputManager.getController().closeRemoteInputs();
}
if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) {
int flags = CommandQueue.FLAG_EXCLUDE_NONE;
@@ -3673,9 +3548,9 @@
protected void handleVisibleToUserChanged(boolean visibleToUser) {
if (visibleToUser) {
handleVisibleToUserChangedImpl(visibleToUser);
- startNotificationLogging();
+ mNotificationLogger.startNotificationLogging();
} else {
- stopNotificationLogging();
+ mNotificationLogger.stopNotificationLogging();
handleVisibleToUserChangedImpl(visibleToUser);
}
}
@@ -3727,60 +3602,6 @@
}
- private void stopNotificationLogging() {
- // Report all notifications as invisible and turn down the
- // reporter.
- if (!mCurrentlyVisibleNotifications.isEmpty()) {
- logNotificationVisibilityChanges(
- Collections.emptyList(), mCurrentlyVisibleNotifications);
- recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
- }
- mHandler.removeCallbacks(mVisibilityReporter);
- mStackScroller.setChildLocationsChangedListener(null);
- }
-
- private void startNotificationLogging() {
- mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
- // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't
- // cause the scroller to emit child location events. Hence generate
- // one ourselves to guarantee that we're reporting visible
- // notifications.
- // (Note that in cases where the scroller does emit events, this
- // additional event doesn't break anything.)
- mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller);
- }
-
- private void logNotificationVisibilityChanges(
- Collection<NotificationVisibility> newlyVisible,
- Collection<NotificationVisibility> noLongerVisible) {
- if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
- return;
- }
- NotificationVisibility[] newlyVisibleAr =
- newlyVisible.toArray(new NotificationVisibility[newlyVisible.size()]);
- NotificationVisibility[] noLongerVisibleAr =
- noLongerVisible.toArray(new NotificationVisibility[noLongerVisible.size()]);
- mUiOffloadThread.submit(() -> {
- try {
- mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
- } catch (RemoteException e) {
- // Ignore.
- }
-
- final int N = newlyVisible.size();
- if (N > 0) {
- String[] newlyVisibleKeyAr = new String[N];
- for (int i = 0; i < N; i++) {
- newlyVisibleKeyAr[i] = newlyVisibleAr[i].key;
- }
-
- setNotificationsShown(newlyVisibleKeyAr);
- }
- });
- }
-
- // State logging
-
private void logStateToEventlog() {
boolean isShowing = mStatusBarKeyguardViewManager.isShowing();
boolean isOccluded = mStatusBarKeyguardViewManager.isOccluded();
@@ -4407,7 +4228,8 @@
private void updateDozingState() {
Trace.traceCounter(Trace.TRACE_TAG_APP, "dozing", mDozing ? 1 : 0);
Trace.beginSection("StatusBar#updateDozingState");
- boolean animate = !mDozing && mDozeServiceHost.shouldAnimateWakeup();
+ boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup())
+ || (mDozing && mDozeServiceHost.shouldAnimateScreenOff());
mNotificationPanel.setDozing(mDozing, animate);
mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation);
mDozeScrimController.setDozing(mDozing);
@@ -4544,7 +4366,7 @@
clearNotificationEffects();
}
if (state == StatusBarState.KEYGUARD) {
- removeRemoteInputEntriesKeptUntilCollapsed();
+ mRemoteInputManager.removeRemoteInputEntriesKeptUntilCollapsed();
maybeEscalateHeadsUp();
}
mState = state;
@@ -4748,13 +4570,15 @@
dismissKeyguardThenExecute(dismissAction, true /* afterKeyguardGone */);
}
- protected void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) {
+ @Override
+ public void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) {
mLeaveOpenOnKeyguardHide = true;
showBouncer();
mPendingRemoteInputView = clicked;
}
- protected void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row,
+ @Override
+ public void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row,
View clickedView) {
if (isKeyguardShowing()) {
onLockedRemoteInput(row, clickedView);
@@ -4764,6 +4588,47 @@
}
}
+ @Override
+ public boolean shouldHandleRemoteInput(View view, PendingIntent pendingIntent) {
+ // Skip remote input as doing so will expand the notification shade.
+ return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0;
+ }
+
+ @Override
+ public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
+ Intent fillInIntent, NotificationRemoteInputManager.ClickHandler defaultHandler) {
+ final boolean isActivity = pendingIntent.isActivity();
+ if (isActivity) {
+ final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
+ mContext, pendingIntent.getIntent(), mLockscreenUserManager.getCurrentUserId());
+ dismissKeyguardThenExecute(() -> {
+ try {
+ ActivityManager.getService().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+
+ boolean handled = defaultHandler.handleClick();
+
+ // close the shade if it was open
+ if (handled && !mNotificationPanel.isFullyCollapsed()) {
+ animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
+ visibilityChanged(false);
+ mAssistManager.hideAssist();
+
+ // Wait for activity start.
+ return true;
+ } else {
+ return false;
+ }
+
+ }, afterKeyguardGone);
+ return true;
+ } else {
+ return defaultHandler.handleClick();
+ }
+ }
+
protected boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender,
String notificationKey) {
// Clear pending remote view, as we do not want to trigger pending remote input view when
@@ -4800,7 +4665,8 @@
// End old BaseStatusBar.startWorkChallengeIfNecessary.
}
- protected void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row,
+ @Override
+ public void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row,
View clicked) {
// Collapse notification and show work challenge
animateCollapsePanels();
@@ -5026,6 +4892,7 @@
return !mNotificationData.getActiveNotifications().isEmpty();
}
+ @Override
public void wakeUpIfDozing(long time, View where) {
if (mDozing) {
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -5040,6 +4907,11 @@
}
@Override
+ public boolean isDeviceLocked(int userId) {
+ return mKeyguardManager.isDeviceLocked(userId);
+ }
+
+ @Override
public void appTransitionCancelled() {
EventBus.getDefault().send(new AppTransitionFinishedEvent());
}
@@ -5146,7 +5018,15 @@
Trace.endSection();
}
- public void updateScrimController() {
+ @VisibleForTesting
+ void updateScrimController() {
+ Trace.beginSection("StatusBar#updateScrimController");
+
+ // We don't want to end up in KEYGUARD state when we're unlocking with
+ // fingerprint from doze. We should cross fade directly from black.
+ final boolean wakeAndUnlocking = mFingerprintUnlockController.getMode()
+ == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+
if (mBouncerShowing) {
mScrimController.transitionTo(ScrimState.BOUNCER);
} else if (mLaunchCameraOnScreenTurningOn || isInLaunchTransition()) {
@@ -5157,11 +5037,12 @@
// Handled in DozeScrimController#setPulsing
} else if (mDozing) {
mScrimController.transitionTo(ScrimState.AOD);
- } else if (mIsKeyguard) {
+ } else if (mIsKeyguard && !wakeAndUnlocking) {
mScrimController.transitionTo(ScrimState.KEYGUARD);
} else {
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
}
+ Trace.endSection();
}
public boolean isKeyguardShowing() {
@@ -5175,6 +5056,7 @@
private final class DozeServiceHost implements DozeHost {
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
private boolean mAnimateWakeup;
+ private boolean mAnimateScreenOff;
private boolean mIgnoreTouchWhilePulsing;
@Override
@@ -5322,6 +5204,11 @@
}
@Override
+ public void setAnimateScreenOff(boolean animateScreenOff) {
+ mAnimateScreenOff = animateScreenOff;
+ }
+
+ @Override
public void onDoubleTap(float screenX, float screenY) {
if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null
&& mAmbientIndicationContainer.getVisibility() == View.VISIBLE) {
@@ -5365,6 +5252,10 @@
private boolean shouldAnimateWakeup() {
return mAnimateWakeup;
}
+
+ public boolean shouldAnimateScreenOff() {
+ return mAnimateScreenOff;
+ }
}
public boolean shouldIgnoreTouch() {
@@ -5380,9 +5271,8 @@
protected NotificationData mNotificationData;
protected NotificationStackScrollLayout mStackScroller;
- protected final NotificationGroupManager mGroupManager = new NotificationGroupManager();
+ protected NotificationGroupManager mGroupManager;
- protected RemoteInputController mRemoteInputController;
// for heads up notifications
protected HeadsUpManager mHeadsUpManager;
@@ -5399,14 +5289,6 @@
protected boolean mVisible;
protected final ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
- protected final ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>();
-
- /**
- * Notifications with keys in this set are not actually around anymore. We kept them around
- * when they were canceled in response to a remote input interaction. This allows us to show
- * what you replied and allows you to continue typing into it.
- */
- protected final ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
// mScreenOnFromKeyguard && mVisible.
private boolean mVisibleToUser;
@@ -5418,8 +5300,6 @@
protected PowerManager mPowerManager;
protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private UserManager mUserManager;
-
protected KeyguardManager mKeyguardManager;
private LockPatternUtils mLockPatternUtils;
private DeviceProvisionedController mDeviceProvisionedController
@@ -5473,212 +5353,6 @@
}
};
- private final RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
-
- @Override
- public boolean onClickHandler(
- final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
- wakeUpIfDozing(SystemClock.uptimeMillis(), view);
-
- if (handleRemoteInput(view, pendingIntent)) {
- return true;
- }
-
- if (DEBUG) {
- Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
- }
- logActionClick(view);
- // The intent we are sending is for the application, which
- // won't have permission to immediately start an activity after
- // the user switches to home. We know it is safe to do at this
- // point, so make sure new activity switches are now allowed.
- try {
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
- final boolean isActivity = pendingIntent.isActivity();
- if (isActivity) {
- final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
- mContext, pendingIntent.getIntent(),
- mLockscreenUserManager.getCurrentUserId());
- dismissKeyguardThenExecute(() -> {
- try {
- ActivityManager.getService().resumeAppSwitches();
- } catch (RemoteException e) {
- }
-
- boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
-
- // close the shade if it was open
- if (handled && !mNotificationPanel.isFullyCollapsed()) {
- animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
- visibilityChanged(false);
- mAssistManager.hideAssist();
-
- // Wait for activity start.
- return true;
- } else {
- return false;
- }
-
- }, afterKeyguardGone);
- return true;
- } else {
- return superOnClickHandler(view, pendingIntent, fillInIntent);
- }
- }
-
- private void logActionClick(View view) {
- ViewParent parent = view.getParent();
- String key = getNotificationKeyForParent(parent);
- if (key == null) {
- Log.w(TAG, "Couldn't determine notification for click.");
- return;
- }
- int index = -1;
- // If this is a default template, determine the index of the button.
- if (view.getId() == com.android.internal.R.id.action0 &&
- parent != null && parent instanceof ViewGroup) {
- ViewGroup actionGroup = (ViewGroup) parent;
- index = actionGroup.indexOfChild(view);
- }
- try {
- mBarService.onNotificationActionClick(key, index);
- } catch (RemoteException e) {
- // Ignore
- }
- }
-
- private String getNotificationKeyForParent(ViewParent parent) {
- while (parent != null) {
- if (parent instanceof ExpandableNotificationRow) {
- return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey();
- }
- parent = parent.getParent();
- }
- return null;
- }
-
- private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
- Intent fillInIntent) {
- return super.onClickHandler(view, pendingIntent, fillInIntent,
- WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
- }
-
- private boolean handleRemoteInput(View view, PendingIntent pendingIntent) {
- if ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
- // Skip remote input as doing so will expand the notification shade.
- return true;
- }
-
- Object tag = view.getTag(com.android.internal.R.id.remote_input_tag);
- RemoteInput[] inputs = null;
- if (tag instanceof RemoteInput[]) {
- inputs = (RemoteInput[]) tag;
- }
-
- if (inputs == null) {
- return false;
- }
-
- RemoteInput input = null;
-
- for (RemoteInput i : inputs) {
- if (i.getAllowFreeFormInput()) {
- input = i;
- }
- }
-
- if (input == null) {
- return false;
- }
-
- ViewParent p = view.getParent();
- RemoteInputView riv = null;
- while (p != null) {
- if (p instanceof View) {
- View pv = (View) p;
- if (pv.isRootNamespace()) {
- riv = findRemoteInputView(pv);
- break;
- }
- }
- p = p.getParent();
- }
- ExpandableNotificationRow row = null;
- while (p != null) {
- if (p instanceof ExpandableNotificationRow) {
- row = (ExpandableNotificationRow) p;
- break;
- }
- p = p.getParent();
- }
-
- if (row == null) {
- return false;
- }
-
- row.setUserExpanded(true);
-
- if (!mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) {
- final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
- if (mLockscreenUserManager.isLockscreenPublicMode(userId)) {
- onLockedRemoteInput(row, view);
- return true;
- }
- if (mUserManager.getUserInfo(userId).isManagedProfile()
- && mKeyguardManager.isDeviceLocked(userId)) {
- onLockedWorkRemoteInput(userId, row, view);
- return true;
- }
- }
-
- if (riv == null) {
- riv = findRemoteInputView(row.getPrivateLayout().getExpandedChild());
- if (riv == null) {
- return false;
- }
- if (!row.getPrivateLayout().getExpandedChild().isShown()) {
- onMakeExpandedVisibleForRemoteInput(row, view);
- return true;
- }
- }
-
- int width = view.getWidth();
- if (view instanceof TextView) {
- // Center the reveal on the text which might be off-center from the TextView
- TextView tv = (TextView) view;
- if (tv.getLayout() != null) {
- int innerWidth = (int) tv.getLayout().getLineWidth(0);
- innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
- width = Math.min(width, innerWidth);
- }
- }
- int cx = view.getLeft() + width / 2;
- int cy = view.getTop() + view.getHeight() / 2;
- int w = riv.getWidth();
- int h = riv.getHeight();
- int r = Math.max(
- Math.max(cx + cy, cx + (h - cy)),
- Math.max((w - cx) + cy, (w - cx) + (h - cy)));
-
- riv.setRevealParameters(cx, cy, r);
- riv.setPendingIntent(pendingIntent);
- riv.setRemoteInput(inputs, input);
- riv.focusAnimated();
-
- return true;
- }
-
- private RemoteInputView findRemoteInputView(View v) {
- if (v == null) {
- return null;
- }
- return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG);
- }
- };
-
private final BroadcastReceiver mBannerActionBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -5765,12 +5439,8 @@
}
protected void setNotificationShown(StatusBarNotification n) {
- setNotificationsShown(new String[]{n.getKey()});
- }
-
- protected void setNotificationsShown(String[] keys) {
try {
- mNotificationListener.setNotificationsShown(keys);
+ mNotificationListener.setNotificationsShown(new String[]{n.getKey()});
} catch (RuntimeException e) {
Log.d(TAG, "failed setNotificationsShown: ", e);
}
@@ -5923,12 +5593,11 @@
row.setGroupManager(mGroupManager);
row.setHeadsUpManager(mHeadsUpManager);
row.setAboveShelfChangedListener(mAboveShelfObserver);
- row.setRemoteInputController(mRemoteInputController);
row.setOnExpandClickListener(this);
- row.setRemoteViewClickHandler(mOnClickHandler);
row.setInflationCallback(this);
row.setSecureStateProvider(this::isKeyguardCurrentlySecure);
row.setLongPressListener(getNotificationLongClicker());
+ mRemoteInputManager.bindRow(row);
// Get the app name.
// Note that Notification.Builder#bindHeaderAppName has similar logic
@@ -6390,7 +6059,8 @@
return;
}
mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
- mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
+ mRemoteInputManager.onUpdateNotification(entry);
+
if (key.equals(mGutsManager.getKeyToRemoveOnGutsClosed())) {
mGutsManager.setKeyToRemoveOnGutsClosed(null);
Log.w(TAG, "Notification that was kept for guts was updated. " + key);
@@ -6635,11 +6305,6 @@
return mLatestRankingMap;
}
- @Override
- public Set<String> getKeysKeptForRemoteInput() {
- return mKeysKeptForRemoteInput;
- }
-
private final NotificationInfo.CheckSaveListener mCheckSaveListener =
(Runnable saveImportance, StatusBarNotification sbn) -> {
// If the user has security enabled, show challenge if the setting is changed.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index dacd3d9..8504d8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -380,8 +380,6 @@
mStatusBar.fadeKeyguardWhilePulsing();
wakeAndUnlockDejank();
} else {
- mFingerprintUnlockController.startKeyguardFadingAway();
- mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
boolean staying = mStatusBar.hideKeyguard();
if (!staying) {
mStatusBarWindowManager.setKeyguardFadingAway(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index ed96b41..c30f633 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
+
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.content.Context;
@@ -52,6 +54,7 @@
private final Context mContext;
private final WindowManager mWindowManager;
private final IActivityManager mActivityManager;
+ private final DozeParameters mDozeParameters;
private View mStatusBarView;
private WindowManager.LayoutParams mLp;
private WindowManager.LayoutParams mLpChanged;
@@ -68,8 +71,8 @@
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mActivityManager = ActivityManager.getService();
mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
- mScreenBrightnessDoze = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_screenBrightnessDoze) / 255f;
+ mDozeParameters = new DozeParameters(mContext);
+ mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
}
private boolean shouldEnableKeyguardScreenRotation() {
@@ -134,7 +137,11 @@
mLpChanged.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
}
- if (state.keyguardShowing && !state.backdropShowing && !state.dozing) {
+ final boolean showWallpaperOnAod = mDozeParameters.getAlwaysOn() &&
+ state.wallpaperSupportsAmbientMode &&
+ state.scrimsVisibility != ScrimController.VISIBILITY_FULLY_OPAQUE;
+ if (state.keyguardShowing && !state.backdropShowing &&
+ (!state.dozing || showWallpaperOnAod)) {
mLpChanged.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
} else {
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
@@ -156,7 +163,7 @@
private void applyFocusableFlag(State state) {
boolean panelFocusable = state.statusBarFocusable && state.panelExpanded;
if (state.bouncerShowing && (state.keyguardOccluded || state.keyguardNeedsInput)
- || StatusBar.ENABLE_REMOTE_INPUT && state.remoteInputActive) {
+ || ENABLE_REMOTE_INPUT && state.remoteInputActive) {
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
} else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
@@ -186,7 +193,8 @@
private boolean isExpanded(State state) {
return !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
|| state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
- || state.headsUpShowing || state.scrimsVisible);
+ || state.headsUpShowing
+ || state.scrimsVisibility != ScrimController.VISIBILITY_FULLY_TRANSPARENT);
}
private void applyFitsSystemWindows(State state) {
@@ -334,8 +342,8 @@
apply(mCurrentState);
}
- public void setScrimsVisible(boolean scrimsVisible) {
- mCurrentState.scrimsVisible = scrimsVisible;
+ public void setScrimsVisibility(int scrimsVisibility) {
+ mCurrentState.scrimsVisibility = scrimsVisibility;
apply(mCurrentState);
}
@@ -344,6 +352,11 @@
apply(mCurrentState);
}
+ public void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {
+ mCurrentState.wallpaperSupportsAmbientMode = supportsAmbientMode;
+ apply(mCurrentState);
+ }
+
/**
* @param state The {@link StatusBarState} of the status bar.
*/
@@ -431,6 +444,7 @@
boolean forceDozeBrightness;
boolean forceUserActivity;
boolean backdropShowing;
+ boolean wallpaperSupportsAmbientMode;
/**
* The {@link StatusBar} state from the status bar.
@@ -440,7 +454,7 @@
boolean remoteInputActive;
boolean forcePluginOpen;
boolean dozing;
- boolean scrimsVisible;
+ int scrimsVisibility;
private boolean isKeyguardShowingAndNotOccluded() {
return keyguardShowing && !keyguardOccluded;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 0f2a2c8..fe39a89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -3681,6 +3681,7 @@
mHideSensitiveNeedsAnimation = true;
mNeedsAnimation = true;
}
+ updateContentHeight();
requestChildrenUpdate();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index f78a718..236c348 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -40,7 +40,7 @@
public class StackStateAnimator {
public static final int ANIMATION_DURATION_STANDARD = 360;
- public static final int ANIMATION_DURATION_WAKEUP = 200;
+ public static final int ANIMATION_DURATION_WAKEUP = 500;
public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448;
public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
index 49a12f4..e1376ca 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Events.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -80,6 +80,7 @@
public static final int DISMISS_REASON_SETTINGS_CLICKED = 5;
public static final int DISMISS_REASON_DONE_CLICKED = 6;
public static final int DISMISS_STREAM_GONE = 7;
+ public static final int DISMISS_REASON_OUTPUT_CHOOSER = 8;
public static final String[] DISMISS_REASONS = {
"unknown",
"touch_outside",
@@ -88,7 +89,8 @@
"screen_off",
"settings_clicked",
"done_clicked",
- "a11y_stream_changed"
+ "a11y_stream_changed",
+ "output_chooser"
};
public static final int SHOW_REASON_UNKNOWN = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
new file mode 100644
index 0000000..f8843a9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2015 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 android.support.v7.media.MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTED;
+import static android.support.v7.media.MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTING;
+import static android.support.v7.media.MediaRouter.UNSELECT_REASON_DISCONNECTED;
+
+import static com.android.settingslib.bluetooth.Utils.getBtClassDrawableWithDescription;
+
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.support.v7.media.MediaControlIntent;
+import android.support.v7.media.MediaRouteSelector;
+import android.support.v7.media.MediaRouter;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.settingslib.Utils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.BluetoothController;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class OutputChooserDialog extends SystemUIDialog
+ implements DialogInterface.OnDismissListener, OutputChooserLayout.Callback {
+
+ private static final String TAG = Util.logTag(OutputChooserDialog.class);
+ private static final int MAX_DEVICES = 10;
+
+ private static final long UPDATE_DELAY_MS = 300L;
+ static final int MSG_UPDATE_ITEMS = 1;
+
+ private final Context mContext;
+ private final BluetoothController mController;
+ private final WifiManager mWifiManager;
+ private OutputChooserLayout mView;
+ private final MediaRouter mRouter;
+ private final MediaRouterCallback mRouterCallback;
+ private long mLastUpdateTime;
+
+ private final MediaRouteSelector mRouteSelector;
+ private Drawable mDefaultIcon;
+ private Drawable mTvIcon;
+ private Drawable mSpeakerIcon;
+ private Drawable mSpeakerGroupIcon;
+
+ public OutputChooserDialog(Context context) {
+ super(context);
+ mContext = context;
+ mController = Dependency.get(BluetoothController.class);
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ mRouter = MediaRouter.getInstance(context);
+ mRouterCallback = new MediaRouterCallback();
+ mRouteSelector = new MediaRouteSelector.Builder()
+ .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
+ .build();
+
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ context.registerReceiver(mReceiver, filter);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.output_chooser);
+ setCanceledOnTouchOutside(true);
+ setOnDismissListener(this::onDismiss);
+ setTitle(R.string.output_title);
+
+ mView = findViewById(R.id.output_chooser);
+ mView.setCallback(this);
+
+ mDefaultIcon = mContext.getDrawable(R.drawable.ic_cast);
+ mTvIcon = mContext.getDrawable(R.drawable.ic_tv);
+ mSpeakerIcon = mContext.getDrawable(R.drawable.ic_speaker);
+ mSpeakerGroupIcon = mContext.getDrawable(R.drawable.ic_speaker_group);
+
+ final boolean wifiOff = !mWifiManager.isWifiEnabled();
+ final boolean btOff = !mController.isBluetoothEnabled();
+ if (wifiOff || btOff) {
+ mView.setEmptyState(getDisabledServicesMessage(wifiOff, btOff));
+ }
+ }
+
+ protected void cleanUp() {}
+
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mRouter.addCallback(mRouteSelector, mRouterCallback,
+ MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
+ mController.addCallback(mCallback);
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ mRouter.removeCallback(mRouterCallback);
+ mController.removeCallback(mCallback);
+ super.onDetachedFromWindow();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface unused) {
+ mContext.unregisterReceiver(mReceiver);
+ cleanUp();
+ }
+
+ @Override
+ public void onDetailItemClick(OutputChooserLayout.Item item) {
+ if (item == null || item.tag == null) return;
+ if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) {
+ final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
+ if (device != null && device.getMaxConnectionState()
+ == BluetoothProfile.STATE_DISCONNECTED) {
+ mController.connect(device);
+ }
+ } else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) {
+ final MediaRouter.RouteInfo route = (MediaRouter.RouteInfo) item.tag;
+ if (route.isEnabled()) {
+ route.select();
+ }
+ }
+ }
+
+ @Override
+ public void onDetailItemDisconnect(OutputChooserLayout.Item item) {
+ if (item == null || item.tag == null) return;
+ if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) {
+ final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
+ if (device != null) {
+ mController.disconnect(device);
+ }
+ } else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) {
+ mRouter.unselect(UNSELECT_REASON_DISCONNECTED);
+ }
+ }
+
+ private void updateItems() {
+ if (SystemClock.uptimeMillis() - mLastUpdateTime < UPDATE_DELAY_MS) {
+ mHandler.removeMessages(MSG_UPDATE_ITEMS);
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ITEMS),
+ mLastUpdateTime + UPDATE_DELAY_MS);
+ return;
+ }
+ mLastUpdateTime = SystemClock.uptimeMillis();
+ if (mView == null) return;
+ ArrayList<OutputChooserLayout.Item> items = new ArrayList<>();
+
+ // Add bluetooth devices
+ addBluetoothDevices(items);
+
+ // Add remote displays
+ addRemoteDisplayRoutes(items);
+
+ Collections.sort(items, ItemComparator.sInstance);
+
+ if (items.size() == 0) {
+ String emptyMessage = mContext.getString(R.string.output_none_found);
+ final boolean wifiOff = !mWifiManager.isWifiEnabled();
+ final boolean btOff = !mController.isBluetoothEnabled();
+ if (wifiOff || btOff) {
+ emptyMessage = getDisabledServicesMessage(wifiOff, btOff);
+ }
+ mView.setEmptyState(emptyMessage);
+ }
+
+ mView.setItems(items.toArray(new OutputChooserLayout.Item[items.size()]));
+ }
+
+ private String getDisabledServicesMessage(boolean wifiOff, boolean btOff) {
+ return mContext.getString(R.string.output_none_found_service_off,
+ wifiOff && btOff ? mContext.getString(R.string.output_service_bt_wifi)
+ : wifiOff ? mContext.getString(R.string.output_service_wifi)
+ : mContext.getString(R.string.output_service_bt));
+ }
+
+ private void addBluetoothDevices(List<OutputChooserLayout.Item> items) {
+ final Collection<CachedBluetoothDevice> devices = mController.getDevices();
+ if (devices != null) {
+ int connectedDevices = 0;
+ int count = 0;
+ for (CachedBluetoothDevice device : devices) {
+ if (mController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
+ final int majorClass = device.getBtClass().getMajorDeviceClass();
+ if (majorClass != BluetoothClass.Device.Major.AUDIO_VIDEO
+ && majorClass != BluetoothClass.Device.Major.UNCATEGORIZED) {
+ continue;
+ }
+ final OutputChooserLayout.Item item = new OutputChooserLayout.Item();
+ item.iconResId = R.drawable.ic_qs_bluetooth_on;
+ item.line1 = device.getName();
+ item.tag = device;
+ item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_BT;
+ int state = device.getMaxConnectionState();
+ if (state == BluetoothProfile.STATE_CONNECTED) {
+ item.iconResId = R.drawable.ic_qs_bluetooth_connected;
+ int batteryLevel = device.getBatteryLevel();
+ if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
+ Pair<Drawable, String> pair =
+ getBtClassDrawableWithDescription(getContext(), device);
+ item.icon = pair.first;
+ item.line2 = mContext.getString(
+ R.string.quick_settings_connected_battery_level,
+ Utils.formatPercentage(batteryLevel));
+ } else {
+ item.line2 = mContext.getString(R.string.quick_settings_connected);
+ }
+ item.canDisconnect = true;
+ items.add(connectedDevices, item);
+ connectedDevices++;
+ } else if (state == BluetoothProfile.STATE_CONNECTING) {
+ item.iconResId = R.drawable.ic_qs_bluetooth_connecting;
+ item.line2 = mContext.getString(R.string.quick_settings_connecting);
+ items.add(connectedDevices, item);
+ } else {
+ items.add(item);
+ }
+ if (++count == MAX_DEVICES) {
+ break;
+ }
+ }
+ }
+ }
+
+ private void addRemoteDisplayRoutes(List<OutputChooserLayout.Item> items) {
+ List<MediaRouter.RouteInfo> routes = mRouter.getRoutes();
+ for(MediaRouter.RouteInfo route : routes) {
+ if (route.isDefaultOrBluetooth() || !route.isEnabled()
+ || !route.matchesSelector(mRouteSelector)) {
+ continue;
+ }
+ final OutputChooserLayout.Item item = new OutputChooserLayout.Item();
+ item.icon = getIconDrawable(route);
+ item.line1 = route.getName();
+ item.tag = route;
+ item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER;
+ if (route.getConnectionState() == CONNECTION_STATE_CONNECTING) {
+ mContext.getString(R.string.quick_settings_connecting);
+ } else {
+ item.line2 = route.getDescription();
+ }
+
+ if (route.getConnectionState() == CONNECTION_STATE_CONNECTED) {
+ item.canDisconnect = true;
+ }
+ items.add(item);
+ }
+ }
+
+ private Drawable getIconDrawable(MediaRouter.RouteInfo route) {
+ Uri iconUri = route.getIconUri();
+ if (iconUri != null) {
+ try {
+ InputStream is = getContext().getContentResolver().openInputStream(iconUri);
+ Drawable drawable = Drawable.createFromStream(is, null);
+ if (drawable != null) {
+ return drawable;
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to load " + iconUri, e);
+ // Falls back.
+ }
+ }
+ return getDefaultIconDrawable(route);
+ }
+
+ private Drawable getDefaultIconDrawable(MediaRouter.RouteInfo route) {
+ // If the type of the receiver device is specified, use it.
+ switch (route.getDeviceType()) {
+ case MediaRouter.RouteInfo.DEVICE_TYPE_TV:
+ return mTvIcon;
+ case MediaRouter.RouteInfo.DEVICE_TYPE_SPEAKER:
+ return mSpeakerIcon;
+ }
+
+ // Otherwise, make the best guess based on other route information.
+ if (route instanceof MediaRouter.RouteGroup) {
+ // Only speakers can be grouped for now.
+ return mSpeakerGroupIcon;
+ }
+ return mDefaultIcon;
+ }
+
+ private final class MediaRouterCallback extends MediaRouter.Callback {
+ @Override
+ public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
+ updateItems();
+ }
+
+ @Override
+ public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
+ updateItems();
+ }
+
+ @Override
+ public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
+ updateItems();
+ }
+
+ @Override
+ public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
+ dismiss();
+ }
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
+ if (D.BUG) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
+ cancel();
+ cleanUp();
+ }
+ }
+ };
+
+ private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
+ @Override
+ public void onBluetoothStateChange(boolean enabled) {
+ updateItems();
+ }
+
+ @Override
+ public void onBluetoothDevicesChanged() {
+ updateItems();
+ }
+ };
+
+ static final class ItemComparator implements Comparator<OutputChooserLayout.Item> {
+ public static final ItemComparator sInstance = new ItemComparator();
+
+ @Override
+ public int compare(OutputChooserLayout.Item lhs, OutputChooserLayout.Item rhs) {
+ // Connected item(s) first
+ if (lhs.canDisconnect != rhs.canDisconnect) {
+ return Boolean.compare(rhs.canDisconnect, lhs.canDisconnect);
+ }
+ // Bluetooth items before media routes
+ if (lhs.deviceType != rhs.deviceType) {
+ return Integer.compare(lhs.deviceType, rhs.deviceType);
+ }
+ // then by name
+ return lhs.line1.toString().compareToIgnoreCase(rhs.line1.toString());
+ }
+ }
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_UPDATE_ITEMS:
+ updateItems();
+ break;
+ }
+ }
+ };
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java
new file mode 100644
index 0000000..22ced60
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java
@@ -0,0 +1,258 @@
+/*
+ * 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 android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.systemui.FontSizeUtils;
+import com.android.systemui.R;
+import com.android.systemui.qs.AutoSizingList;
+
+/**
+ * Limited height list of devices.
+ */
+public class OutputChooserLayout extends FrameLayout {
+ private static final String TAG = "OutputChooserLayout";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final int mQsDetailIconOverlaySize;
+ private final Context mContext;
+ private final H mHandler = new H();
+ private final Adapter mAdapter = new Adapter();
+
+ private String mTag;
+ private Callback mCallback;
+ private boolean mItemsVisible = true;
+ private AutoSizingList mItemList;
+ private View mEmpty;
+ private TextView mEmptyText;
+
+ private Item[] mItems;
+
+ public OutputChooserLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ mTag = TAG;
+ mQsDetailIconOverlaySize = (int) getResources().getDimension(
+ R.dimen.qs_detail_icon_overlay_size);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mItemList = findViewById(android.R.id.list);
+ mItemList.setVisibility(GONE);
+ mItemList.setAdapter(mAdapter);
+ mEmpty = findViewById(android.R.id.empty);
+ mEmpty.setVisibility(GONE);
+ mEmptyText = mEmpty.findViewById(android.R.id.title);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ FontSizeUtils.updateFontSize(mEmptyText, R.dimen.qs_detail_empty_text_size);
+ int count = mItemList.getChildCount();
+ for (int i = 0; i < count; i++) {
+ View item = mItemList.getChildAt(i);
+ FontSizeUtils.updateFontSize(item, android.R.id.title,
+ R.dimen.qs_detail_item_primary_text_size);
+ FontSizeUtils.updateFontSize(item, android.R.id.summary,
+ R.dimen.qs_detail_item_secondary_text_size);
+ }
+ }
+
+ public void setEmptyState(String text) {
+ mEmpty.post(() -> {
+ mEmptyText.setText(text);
+ });
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (DEBUG) Log.d(mTag, "onAttachedToWindow");
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
+ mCallback = null;
+ }
+
+ public void setCallback(Callback callback) {
+ mHandler.removeMessages(H.SET_CALLBACK);
+ mHandler.obtainMessage(H.SET_CALLBACK, callback).sendToTarget();
+ }
+
+ public void setItems(Item[] items) {
+ mHandler.removeMessages(H.SET_ITEMS);
+ mHandler.obtainMessage(H.SET_ITEMS, items).sendToTarget();
+ }
+
+ public void setItemsVisible(boolean visible) {
+ mHandler.removeMessages(H.SET_ITEMS_VISIBLE);
+ mHandler.obtainMessage(H.SET_ITEMS_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
+ }
+
+ private void handleSetCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ private void handleSetItems(Item[] items) {
+ final int itemCount = items != null ? items.length : 0;
+ mEmpty.setVisibility(itemCount == 0 ? VISIBLE : GONE);
+ mItemList.setVisibility(itemCount == 0 ? GONE : VISIBLE);
+ mItems = items;
+ mAdapter.notifyDataSetChanged();
+ }
+
+ private void handleSetItemsVisible(boolean visible) {
+ if (mItemsVisible == visible) return;
+ mItemsVisible = visible;
+ for (int i = 0; i < mItemList.getChildCount(); i++) {
+ mItemList.getChildAt(i).setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
+ }
+ }
+
+ private class Adapter extends BaseAdapter {
+
+ @Override
+ public int getCount() {
+ return mItems != null ? mItems.length : 0;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mItems[position];
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return 0;
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ final Item item = mItems[position];
+ if (view == null) {
+ view = LayoutInflater.from(mContext).inflate(R.layout.output_chooser_item, parent,
+ false);
+ }
+ view.setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
+ final ImageView iv = view.findViewById(android.R.id.icon);
+ if (item.icon != null) {
+ iv.setImageDrawable(item.icon);
+ } else {
+ iv.setImageResource(item.iconResId);
+ }
+ iv.getOverlay().clear();
+ if (item.overlay != null) {
+ item.overlay.setBounds(0, 0, mQsDetailIconOverlaySize, mQsDetailIconOverlaySize);
+ iv.getOverlay().add(item.overlay);
+ }
+ final TextView title = view.findViewById(android.R.id.title);
+ title.setText(item.line1);
+ final TextView summary = view.findViewById(android.R.id.summary);
+ final boolean twoLines = !TextUtils.isEmpty(item.line2);
+ title.setMaxLines(twoLines ? 1 : 2);
+ summary.setVisibility(twoLines ? VISIBLE : GONE);
+ summary.setText(twoLines ? item.line2 : null);
+ view.setOnClickListener(v -> {
+ if (mCallback != null) {
+ mCallback.onDetailItemClick(item);
+ }
+ });
+
+ final ImageView icon2 = view.findViewById(android.R.id.icon2);
+ if (item.canDisconnect) {
+ icon2.setImageResource(R.drawable.ic_qs_cancel);
+ icon2.setVisibility(VISIBLE);
+ icon2.setClickable(true);
+ icon2.setOnClickListener(v -> {
+ if (mCallback != null) {
+ mCallback.onDetailItemDisconnect(item);
+ }
+ });
+ } else if (item.icon2 != -1) {
+ icon2.setVisibility(VISIBLE);
+ icon2.setImageResource(item.icon2);
+ icon2.setClickable(false);
+ } else {
+ icon2.setVisibility(GONE);
+ }
+
+ return view;
+ }
+ };
+
+ private class H extends Handler {
+ private static final int SET_ITEMS = 1;
+ private static final int SET_CALLBACK = 2;
+ private static final int SET_ITEMS_VISIBLE = 3;
+
+ public H() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == SET_ITEMS) {
+ handleSetItems((Item[]) msg.obj);
+ } else if (msg.what == SET_CALLBACK) {
+ handleSetCallback((OutputChooserLayout.Callback) msg.obj);
+ } else if (msg.what == SET_ITEMS_VISIBLE) {
+ handleSetItemsVisible(msg.arg1 != 0);
+ }
+ }
+ }
+
+ public static class Item {
+ public static int DEVICE_TYPE_BT = 1;
+ public static int DEVICE_TYPE_MEDIA_ROUTER = 2;
+ public int iconResId;
+ public Drawable icon;
+ public Drawable overlay;
+ public CharSequence line1;
+ public CharSequence line2;
+ public Object tag;
+ public boolean canDisconnect;
+ public int icon2 = -1;
+ public int deviceType = 0;
+ }
+
+ public interface Callback {
+ void onDetailItemClick(Item item);
+ void onDetailItemDisconnect(Item item);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index 4dff9bd..bc98140 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -134,6 +134,10 @@
mController.setVolumePolicy(mVolumePolicy);
}
+ void setEnableDialogs(boolean volumeUi, boolean safetyWarning) {
+ mController.setEnableDialogs(volumeUi, safetyWarning);
+ }
+
@Override
public void onUserActivity() {
final KeyguardViewMediator kvm = mSysui.getComponent(KeyguardViewMediator.class);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 2754026..2129790 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -103,6 +103,8 @@
private final Vibrator mVibrator;
private final boolean mHasVibrator;
private boolean mShowA11yStream;
+ private boolean mShowVolumeDialog;
+ private boolean mShowSafetyWarning;
private boolean mDestroyed;
private VolumePolicy mVolumePolicy;
@@ -282,6 +284,11 @@
mWorker.obtainMessage(W.SET_ACTIVE_STREAM, stream, 0).sendToTarget();
}
+ public void setEnableDialogs(boolean volumeUi, boolean safetyWarning) {
+ mShowVolumeDialog = volumeUi;
+ mShowSafetyWarning = safetyWarning;
+ }
+
public void vibrate() {
if (mHasVibrator) {
mVibrator.vibrate(VIBRATE_HINT_DURATION);
@@ -311,7 +318,9 @@
}
private void onShowSafetyWarningW(int flags) {
- mCallbacks.onShowSafetyWarning(flags);
+ if (mShowSafetyWarning) {
+ mCallbacks.onShowSafetyWarning(flags);
+ }
}
private void onAccessibilityModeChanged(Boolean showA11yStream) {
@@ -343,7 +352,8 @@
&& mStatusBar.getWakefulnessState() != WakefulnessLifecycle.WAKEFULNESS_ASLEEP
&& mStatusBar.getWakefulnessState() != WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP
&& mStatusBar.isDeviceInteractive()
- && (flags & AudioManager.FLAG_SHOW_UI) != 0;
+ && (flags & AudioManager.FLAG_SHOW_UI) != 0
+ && mShowVolumeDialog;
}
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 7959b72..d7c8010 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -19,6 +19,7 @@
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
+import static com.android.systemui.volume.Events.DISMISS_REASON_OUTPUT_CHOOSER;
import static com.android.systemui.volume.Events.DISMISS_REASON_TOUCH_OUTSIDE;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -37,7 +38,6 @@
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.TransitionDrawable;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.os.Debug;
@@ -106,6 +106,7 @@
private ViewGroup mDialogRowsView;
private ImageButton mExpandButton;
private ImageButton mRingerIcon;
+ private ImageButton mOutputChooser;
private TextView mRingerStatus;
private final List<VolumeRow> mRows = new ArrayList<>();
private ConfigurableTexts mConfigurableTexts;
@@ -113,6 +114,7 @@
private final KeyguardManager mKeyguard;
private final AccessibilityManager mAccessibilityMgr;
private final Object mSafetyWarningLock = new Object();
+ private final Object mOutputChooserLock = new Object();
private final Accessibility mAccessibility = new Accessibility();
private final ColorStateList mActiveSliderTint;
private final ColorStateList mInactiveSliderTint;
@@ -128,6 +130,7 @@
private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
private State mState;
private SafetyWarningDialog mSafetyWarning;
+ private OutputChooserDialog mOutputChooserDialog;
private boolean mHovering = false;
public VolumeDialogImpl(Context context) {
@@ -213,6 +216,9 @@
mExpandButton.setVisibility(
AudioSystem.isSingleVolume(mContext) ? View.GONE : View.VISIBLE);
+ mOutputChooser = mDialogView.findViewById(R.id.output_chooser);
+ mOutputChooser.setOnClickListener(mClickOutputChooser);
+
if (mRows.isEmpty()) {
addRow(AudioManager.STREAM_MUSIC,
R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true);
@@ -913,6 +919,23 @@
rescheduleTimeoutH();
}
+ private void showOutputChooserH() {
+ synchronized (mOutputChooserLock) {
+ if (mOutputChooserDialog != null) {
+ return;
+ }
+ mOutputChooserDialog = new OutputChooserDialog(mContext) {
+ @Override
+ protected void cleanUp() {
+ synchronized (mOutputChooserLock) {
+ mOutputChooserDialog = null;
+ }
+ }
+ };
+ mOutputChooserDialog.show();
+ }
+ }
+
private String getStreamLabelH(StreamState ss) {
if (ss.remoteLabel != null) {
return ss.remoteLabel;
@@ -935,6 +958,15 @@
}
};
+ private final OnClickListener mClickOutputChooser = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // TODO: log
+ dismissH(DISMISS_REASON_OUTPUT_CHOOSER);
+ showOutputChooserH();
+ }
+ };
+
private final VolumeDialogController.Callbacks mControllerCallbackH
= new VolumeDialogController.Callbacks() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index 02969e4..6f65b08 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -40,9 +40,13 @@
@Override
public void start() {
- mEnabled = mContext.getResources().getBoolean(R.bool.enable_volume_ui);
+ boolean enableVolumeUi = mContext.getResources().getBoolean(R.bool.enable_volume_ui);
+ boolean enableSafetyWarning =
+ mContext.getResources().getBoolean(R.bool.enable_safety_warning);
+ mEnabled = enableVolumeUi || enableSafetyWarning;
if (!mEnabled) return;
mVolumeComponent = new VolumeDialogComponent(this, mContext, null);
+ mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning);
putComponent(VolumeComponent.class, getVolumeComponent());
setDefaultVolumeController();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
index 1c104cf..3e6bd7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
@@ -28,6 +28,7 @@
Callback callback;
boolean pulseExtended;
boolean animateWakeup;
+ boolean animateScreenOff;
boolean dozing;
float doubleTapX;
float doubleTapY;
@@ -103,6 +104,11 @@
}
@Override
+ public void setAnimateScreenOff(boolean animateScreenOff) {
+ this.animateScreenOff = animateScreenOff;
+ }
+
+ @Override
public void onDoubleTap(float x, float y) {
doubleTapX = y;
doubleTapY = y;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index 19a30cc..75ade9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.doze;
+import static com.android.systemui.doze.DozeMachine.State.DOZE;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
import static com.android.systemui.doze.DozeMachine.State.INITIALIZED;
@@ -28,19 +29,20 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.AlarmManager;
-import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
-import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.wakelock.WakeLockFake;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,8 +68,11 @@
mWakeLock = new WakeLockFake();
mHost = new DozeHostFake();
mHandler = mHandlerThread.getThreadHandler();
+ DozeParameters params = mock(DozeParameters.class);
+ when(params.getCanControlScreenOffAnimation()).thenReturn(true);
+ when(params.getDisplayNeedsBlanking()).thenReturn(false);
- mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler);
+ mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler, params);
}
@After
@@ -89,4 +94,20 @@
verify(mAlarmManager).setExact(anyInt(), anyLong(), eq("doze_time_tick"), any(), any());
}
+
+ @Test
+ public void propagatesAnimateScreenOff() {
+ Assert.assertTrue("animateScreenOff should be true", mHost.animateScreenOff);
+
+ DozeParameters params = mock(DozeParameters.class);
+ new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler, params);
+ Assert.assertFalse("animateScreenOff should be false", mHost.animateScreenOff);
+ }
+
+ @Test
+ public void transitionSetsAnimateWakeup() {
+ mHost.animateWakeup = false;
+ mDozeUi.transitionTo(UNINITIALIZED, DOZE);
+ Assert.assertTrue("animateScreenOff should be true", mHost.animateWakeup);
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
new file mode 100644
index 0000000..8e7f83d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.doze;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.IWallpaperManager;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.support.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@SmallTest
+public class DozeWallpaperStateTest extends SysuiTestCase {
+
+ @Test
+ public void testDreamNotification() throws RemoteException {
+ IWallpaperManager wallpaperManagerService = mock(IWallpaperManager.class);
+ DozeWallpaperState dozeWallpaperState = new DozeWallpaperState(wallpaperManagerService);
+ dozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
+ DozeMachine.State.DOZE_AOD);
+ verify(wallpaperManagerService).setInAmbientMode(eq(true));
+ dozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
+ verify(wallpaperManagerService).setInAmbientMode(eq(false));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
index 58ff46a..544585a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
@@ -50,7 +50,7 @@
@Test
public void testGroupSummaryNotShowingIconWhenPublic() {
mGroup.setSensitive(true, true);
- mGroup.setHideSensitive(true, false, 0, 0);
+ mGroup.setHideSensitiveForIntrinsicHeight(true);
Assert.assertTrue(mGroup.isSummaryWithChildren());
Assert.assertFalse(mGroup.isShowingIcon());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 6ecfe3e..ccc3006 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -56,6 +56,7 @@
private NotificationListenerService.RankingMap mRanking;
private Set<String> mKeysKeptForRemoteInput;
private NotificationData mNotificationData;
+ private NotificationRemoteInputManager mRemoteInputManager;
@Before
public void setUp() {
@@ -63,15 +64,18 @@
mPresenter = mock(NotificationPresenter.class);
mNotificationData = mock(NotificationData.class);
mRanking = mock(NotificationListenerService.RankingMap.class);
+ mRemoteInputManager = mock(NotificationRemoteInputManager.class);
mKeysKeptForRemoteInput = new HashSet<>();
when(mPresenter.getHandler()).thenReturn(mHandler);
when(mPresenter.getNotificationData()).thenReturn(mNotificationData);
- when(mPresenter.getKeysKeptForRemoteInput()).thenReturn(mKeysKeptForRemoteInput);
+ when(mRemoteInputManager.getKeysKeptForRemoteInput()).thenReturn(mKeysKeptForRemoteInput);
- mListener = new NotificationListener(mPresenter, mContext);
+ mListener = new NotificationListener(mRemoteInputManager, mContext);
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
new Notification(), UserHandle.CURRENT, null, 0);
+
+ mListener.setUpWithPresenter(mPresenter);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java
new file mode 100644
index 0000000..142ce63
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLoggerTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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.statusbar;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.UiOffloadThread;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+
+import com.google.android.collect.Lists;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationLoggerTest extends SysuiTestCase {
+ private static final String TEST_PACKAGE_NAME = "test";
+ private static final int TEST_UID = 0;
+
+ @Mock private NotificationPresenter mPresenter;
+ @Mock private NotificationListener mListener;
+ @Mock private NotificationStackScrollLayout mStackScroller;
+ @Mock private IStatusBarService mBarService;
+ @Mock private NotificationData mNotificationData;
+ @Mock private ExpandableNotificationRow mRow;
+
+ private NotificationData.Entry mEntry;
+ private StatusBarNotification mSbn;
+ private TestableNotificationLogger mLogger;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mPresenter.getNotificationData()).thenReturn(mNotificationData);
+
+ mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
+ 0, new Notification(), UserHandle.CURRENT, null, 0);
+ mEntry = new NotificationData.Entry(mSbn);
+ mEntry.row = mRow;
+
+ mLogger = new TestableNotificationLogger(mListener, mDependency.get(UiOffloadThread.class),
+ mBarService);
+ mLogger.setUpWithPresenter(mPresenter, mStackScroller);
+ }
+
+ @Test
+ public void testOnChildLocationsChangedReportsVisibilityChanged() throws Exception {
+ when(mStackScroller.isInVisibleLocation(any())).thenReturn(true);
+ when(mNotificationData.getActiveNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged(mStackScroller);
+ waitForIdleSync(mLogger.getHandlerForTest());
+ waitForUiOffloadThread();
+
+ NotificationVisibility[] newlyVisibleKeys = {
+ NotificationVisibility.obtain(mEntry.key, 0, true)
+ };
+ NotificationVisibility[] noLongerVisibleKeys = {};
+ verify(mBarService).onNotificationVisibilityChanged(newlyVisibleKeys, noLongerVisibleKeys);
+
+ // |mEntry| won't change visibility, so it shouldn't be reported again:
+ Mockito.reset(mBarService);
+ mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged(mStackScroller);
+ waitForIdleSync(mLogger.getHandlerForTest());
+ waitForUiOffloadThread();
+
+ verify(mBarService, never()).onNotificationVisibilityChanged(any(), any());
+ }
+
+ @Test
+ public void testStoppingNotificationLoggingReportsCurrentNotifications()
+ throws Exception {
+ when(mStackScroller.isInVisibleLocation(any())).thenReturn(true);
+ when(mNotificationData.getActiveNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged(mStackScroller);
+ waitForIdleSync(mLogger.getHandlerForTest());
+ waitForUiOffloadThread();
+ Mockito.reset(mBarService);
+
+ mLogger.stopNotificationLogging();
+ waitForUiOffloadThread();
+ // The visibility objects are recycled by NotificationLogger, so we can't use specific
+ // matchers here.
+ verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any());
+ }
+
+ private class TestableNotificationLogger extends NotificationLogger {
+
+ public TestableNotificationLogger(
+ NotificationListenerService notificationListener,
+ UiOffloadThread uiOffloadThread,
+ IStatusBarService barService) {
+ super(notificationListener, uiOffloadThread);
+ mBarService = barService;
+ // Make this on the main thread so we can wait for it during tests.
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ public NotificationStackScrollLayout.OnChildLocationsChangedListener
+ getChildLocationsChangedListenerForTest() {
+ return mNotificationLocationsChangedListener;
+ }
+
+ public Handler getHandlerForTest() {
+ return mHandler;
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
new file mode 100644
index 0000000..b881c09
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -0,0 +1,118 @@
+package com.android.systemui.statusbar;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+
+import com.google.android.collect.Sets;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationRemoteInputManagerTest extends SysuiTestCase {
+ private static final String TEST_PACKAGE_NAME = "test";
+ private static final int TEST_UID = 0;
+
+ private Handler mHandler;
+ private TestableNotificationRemoteInputManager mRemoteInputManager;
+ private StatusBarNotification mSbn;
+ private NotificationData.Entry mEntry;
+
+ @Mock private NotificationPresenter mPresenter;
+ @Mock private RemoteInputController.Delegate mDelegate;
+ @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+ @Mock private NotificationRemoteInputManager.Callback mCallback;
+ @Mock private RemoteInputController mController;
+ @Mock private NotificationListenerService.RankingMap mRanking;
+ @Mock private ExpandableNotificationRow mRow;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mHandler = new Handler(Looper.getMainLooper());
+
+ when(mPresenter.getHandler()).thenReturn(mHandler);
+ when(mPresenter.getLatestRankingMap()).thenReturn(mRanking);
+
+ mRemoteInputManager = new TestableNotificationRemoteInputManager(mLockscreenUserManager,
+ mContext);
+ mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
+ 0, new Notification(), UserHandle.CURRENT, null, 0);
+ mEntry = new NotificationData.Entry(mSbn);
+ mEntry.row = mRow;
+
+ mRemoteInputManager.setUpWithPresenterForTest(mPresenter, mCallback, mDelegate,
+ mController);
+ }
+
+ @Test
+ public void testOnRemoveNotificationNotKept() {
+ assertFalse(mRemoteInputManager.onRemoveNotification(mEntry));
+ assertTrue(mRemoteInputManager.getRemoteInputEntriesToRemoveOnCollapse().isEmpty());
+ }
+
+ @Test
+ public void testOnRemoveNotificationKept() {
+ when(mController.isRemoteInputActive(mEntry)).thenReturn(true);
+ assertTrue(mRemoteInputManager.onRemoveNotification(mEntry));
+ assertTrue(mRemoteInputManager.getRemoteInputEntriesToRemoveOnCollapse().equals(
+ Sets.newArraySet(mEntry)));
+ }
+
+ @Test
+ public void testPerformOnRemoveNotification() {
+ when(mController.isRemoteInputActive(mEntry)).thenReturn(true);
+ mRemoteInputManager.getKeysKeptForRemoteInput().add(mEntry.key);
+ mRemoteInputManager.onPerformRemoveNotification(mSbn, mEntry);
+
+ verify(mController).removeRemoteInput(mEntry, null);
+ assertTrue(mRemoteInputManager.getKeysKeptForRemoteInput().isEmpty());
+ }
+
+ @Test
+ public void testRemoveRemoteInputEntriesKeptUntilCollapsed() {
+ mRemoteInputManager.getRemoteInputEntriesToRemoveOnCollapse().add(mEntry);
+ mRemoteInputManager.removeRemoteInputEntriesKeptUntilCollapsed();
+
+ assertTrue(mRemoteInputManager.getRemoteInputEntriesToRemoveOnCollapse().isEmpty());
+ verify(mController).removeRemoteInput(mEntry, null);
+ verify(mPresenter).removeNotification(mEntry.key, mRanking);
+ }
+
+ private class TestableNotificationRemoteInputManager extends NotificationRemoteInputManager {
+
+ public TestableNotificationRemoteInputManager(
+ NotificationLockscreenUserManager lockscreenUserManager, Context context) {
+ super(lockscreenUserManager, context);
+ }
+
+ public void setUpWithPresenterForTest(NotificationPresenter presenter,
+ Callback callback,
+ RemoteInputController.Delegate delegate,
+ RemoteInputController controller) {
+ super.setUpWithPresenter(presenter, callback, delegate);
+ mRemoteInputController = controller;
+ }
+ }
+}
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 b9f695b..6d2691c 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
@@ -16,13 +16,22 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.phone.ScrimController.VISIBILITY_FULLY_OPAQUE;
+import static com.android.systemui.statusbar.phone.ScrimController.VISIBILITY_FULLY_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.ScrimController.VISIBILITY_SEMI_TRANSPARENT;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.animation.Animator;
+import android.app.AlarmManager;
import android.graphics.Color;
import android.os.Handler;
import android.os.Looper;
@@ -31,6 +40,7 @@
import android.testing.TestableLooper;
import android.view.View;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.util.wakelock.WakeLock;
@@ -52,12 +62,13 @@
private ScrimView mScrimBehind;
private ScrimView mScrimInFront;
private View mHeadsUpScrim;
- private Consumer<Boolean> mScrimVisibilityCallback;
- private Boolean mScrimVisibile;
+ private Consumer<Integer> mScrimVisibilityCallback;
+ private int mScrimVisibility;
private LightBarController mLightBarController;
private DozeParameters mDozeParamenters;
private WakeLock mWakeLock;
private boolean mAlwaysOnEnabled;
+ private AlarmManager mAlarmManager;
@Before
public void setup() {
@@ -66,12 +77,15 @@
mScrimInFront = new ScrimView(getContext());
mHeadsUpScrim = mock(View.class);
mWakeLock = mock(WakeLock.class);
+ mAlarmManager = mock(AlarmManager.class);
mAlwaysOnEnabled = true;
- mScrimVisibilityCallback = (Boolean visible) -> mScrimVisibile = visible;
+ mScrimVisibilityCallback = (Integer visible) -> mScrimVisibility = visible;
mDozeParamenters = mock(DozeParameters.class);
when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
+ when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true);
mScrimController = new SynchronousScrimController(mLightBarController, mScrimBehind,
- mScrimInFront, mHeadsUpScrim, mScrimVisibilityCallback, mDozeParamenters);
+ mScrimInFront, mHeadsUpScrim, mScrimVisibilityCallback, mDozeParamenters,
+ mAlarmManager);
}
@Test
@@ -86,29 +100,70 @@
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be visible without tint
- assertScrimVisibility(false /* front */, true /* behind */);
+ assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
assertScrimTint(mScrimBehind, false /* tinted */);
}
@Test
- public void transitionToAod() {
+ public void transitionToAod_withRegularWallpaper() {
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be visible with tint
- assertScrimVisibility(false /* front */, true /* behind */);
+ assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+ assertScrimTint(mScrimBehind, true /* tinted */);
+ assertScrimTint(mScrimInFront, true /* tinted */);
+ }
+
+ @Test
+ public void transitionToAod_withAodWallpaper() {
+ mScrimController.setWallpaperSupportsAmbientMode(true);
+ mScrimController.transitionTo(ScrimState.AOD);
+ mScrimController.finishAnimationsImmediately();
+ // Front scrim should be transparent
+ // Back scrim should be transparent
+ assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
+
+ // Move on to PULSING and check if the back scrim is still transparent
+ mScrimController.transitionTo(ScrimState.PULSING);
+ mScrimController.finishAnimationsImmediately();
+ assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
+ }
+
+ @Test
+ public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() {
+ ScrimState.AOD.mKeyguardUpdateMonitor = new KeyguardUpdateMonitor(getContext()) {
+ @Override
+ public boolean hasLockscreenWallpaper() {
+ return true;
+ }
+ };
+ mScrimController.setWallpaperSupportsAmbientMode(true);
+ mScrimController.transitionTo(ScrimState.AOD);
+ mScrimController.finishAnimationsImmediately();
+ // Front scrim should be transparent
+ // Back scrim should be visible with tint
+ assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
assertScrimTint(mScrimBehind, true /* tinted */);
assertScrimTint(mScrimInFront, true /* tinted */);
}
@Test
public void transitionToPulsing() {
+ // Pre-condition
+ // Need to go to AoD first because PULSING doesn't change
+ // the back scrim opacity - otherwise it would hide AoD wallpapers.
+ mScrimController.setWallpaperSupportsAmbientMode(false);
+ mScrimController.transitionTo(ScrimState.AOD);
+ mScrimController.finishAnimationsImmediately();
+ assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+
mScrimController.transitionTo(ScrimState.PULSING);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be visible with tint
// Pulse callback should have been invoked
- assertScrimVisibility(false /* front */, true /* behind */);
+ assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
assertScrimTint(mScrimBehind, true /* tinted */);
}
@@ -118,7 +173,7 @@
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be visible without tint
- assertScrimVisibility(true /* front */, true /* behind */);
+ assertScrimVisibility(VISIBILITY_SEMI_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
assertScrimTint(mScrimBehind, false /* tinted */);
}
@@ -128,13 +183,13 @@
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be transparent
- assertScrimVisibility(false /* front */, false /* behind */);
+ assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
assertScrimTint(mScrimBehind, false /* tinted */);
assertScrimTint(mScrimInFront, false /* tinted */);
// Back scrim should be visible after start dragging
mScrimController.setPanelExpansion(0.5f);
- assertScrimVisibility(false /* front */, true /* behind */);
+ assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
}
@Test
@@ -150,7 +205,7 @@
// Front scrim should be transparent
// Back scrim should be transparent
// Neither scrims should be tinted anymore after the animation.
- assertScrimVisibility(false /* front */, false /* behind */);
+ assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_TRANSPARENT);
assertScrimTint(mScrimInFront, false /* tinted */);
assertScrimTint(mScrimBehind, false /* tinted */);
}
@@ -168,8 +223,8 @@
Assert.assertTrue("Scrim should be visible during transition. Alpha: "
+ mScrimInFront.getViewAlpha(), mScrimInFront.getViewAlpha() > 0);
assertScrimTint(mScrimInFront, true /* tinted */);
- Assert.assertTrue("Scrim should be visible during transition.",
- mScrimVisibile);
+ Assert.assertSame("Scrim should be visible during transition.",
+ mScrimVisibility, VISIBILITY_FULLY_OPAQUE);
}
});
mScrimController.finishAnimationsImmediately();
@@ -221,12 +276,51 @@
}
@Test
- public void testHoldsWakeLock() {
+ public void testHoldsWakeLock_whenAOD() {
mScrimController.transitionTo(ScrimState.AOD);
- verify(mWakeLock, times(1)).acquire();
+ verify(mWakeLock).acquire();
verify(mWakeLock, never()).release();
mScrimController.finishAnimationsImmediately();
- verify(mWakeLock, times(1)).release();
+ verify(mWakeLock).release();
+ }
+
+ @Test
+ public void testDoesNotHoldWakeLock_whenUnlocking() {
+ mScrimController.transitionTo(ScrimState.UNLOCKED);
+ mScrimController.finishAnimationsImmediately();
+ verifyZeroInteractions(mWakeLock);
+ }
+
+ @Test
+ public void testCallbackInvokedOnSameStateTransition() {
+ mScrimController.transitionTo(ScrimState.UNLOCKED);
+ mScrimController.finishAnimationsImmediately();
+ ScrimController.Callback callback = mock(ScrimController.Callback.class);
+ mScrimController.transitionTo(ScrimState.UNLOCKED, callback);
+ verify(callback).onFinished();
+ }
+
+ @Test
+ public void testHoldsAodWallpaperAnimationLock() {
+ // Pre-conditions
+ mScrimController.transitionTo(ScrimState.AOD);
+ mScrimController.finishAnimationsImmediately();
+ reset(mWakeLock);
+
+ mScrimController.onHideWallpaperTimeout();
+ verify(mWakeLock).acquire();
+ verify(mWakeLock, never()).release();
+ mScrimController.finishAnimationsImmediately();
+ verify(mWakeLock).release();
+ }
+
+ @Test
+ public void testWillHideAoDWallpaper() {
+ mScrimController.setWallpaperSupportsAmbientMode(true);
+ mScrimController.transitionTo(ScrimState.AOD);
+ verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
+ mScrimController.transitionTo(ScrimState.KEYGUARD);
+ verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
}
private void assertScrimTint(ScrimView scrimView, boolean tinted) {
@@ -237,12 +331,23 @@
tinted, viewIsTinted);
}
- private void assertScrimVisibility(boolean inFront, boolean behind) {
+ private void assertScrimVisibility(int inFront, int behind) {
+ boolean inFrontVisible = inFront != ScrimController.VISIBILITY_FULLY_TRANSPARENT;
+ boolean behindVisible = behind != ScrimController.VISIBILITY_FULLY_TRANSPARENT;
Assert.assertEquals("Unexpected front scrim visibility. Alpha is "
- + mScrimInFront.getViewAlpha(), inFront, mScrimInFront.getViewAlpha() > 0);
+ + mScrimInFront.getViewAlpha(), inFrontVisible, mScrimInFront.getViewAlpha() > 0);
Assert.assertEquals("Unexpected back scrim visibility. Alpha is "
- + mScrimBehind.getViewAlpha(), behind, mScrimBehind.getViewAlpha() > 0);
- Assert.assertEquals("Invalid visibility.", inFront || behind, mScrimVisibile);
+ + mScrimBehind.getViewAlpha(), behindVisible, mScrimBehind.getViewAlpha() > 0);
+
+ final int visibility;
+ if (inFront == VISIBILITY_FULLY_OPAQUE || behind == VISIBILITY_FULLY_OPAQUE) {
+ visibility = VISIBILITY_FULLY_OPAQUE;
+ } else if (inFront > VISIBILITY_FULLY_TRANSPARENT || behind > VISIBILITY_FULLY_TRANSPARENT) {
+ visibility = VISIBILITY_SEMI_TRANSPARENT;
+ } else {
+ visibility = VISIBILITY_FULLY_TRANSPARENT;
+ }
+ Assert.assertEquals("Invalid visibility.", visibility, mScrimVisibility);
}
/**
@@ -254,9 +359,10 @@
public SynchronousScrimController(LightBarController lightBarController,
ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
- Consumer<Boolean> scrimVisibleListener, DozeParameters dozeParameters) {
+ Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
+ AlarmManager alarmManager) {
super(lightBarController, scrimBehind, scrimInFront, headsUpScrim,
- scrimVisibleListener, dozeParameters);
+ scrimVisibleListener, dozeParameters, alarmManager);
mHandler = new FakeHandler(Looper.myLooper());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 5ff90d91..0732866 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -26,6 +26,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -42,7 +43,6 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IPowerManager;
-import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -51,7 +51,6 @@
import android.support.test.metricshelper.MetricsAsserts;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.testing.TestableLooper.MessageHandler;
import android.testing.TestableLooper.RunWithLooper;
import android.util.DisplayMetrics;
import android.util.SparseArray;
@@ -64,6 +63,7 @@
import com.android.keyguard.KeyguardHostView.OnDismissAction;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.UiOffloadThread;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -72,7 +72,9 @@
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationData.Entry;
+import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationLogger;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -106,7 +108,10 @@
NotificationPanelView mNotificationPanelView;
ScrimController mScrimController;
IStatusBarService mBarService;
+ NotificationListener mNotificationListener;
+ NotificationLogger mNotificationLogger;
ArrayList<Entry> mNotificationList;
+ FingerprintUnlockController mFingerprintUnlockController;
private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
@Before
@@ -133,6 +138,7 @@
when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0));
mNotificationList = mock(ArrayList.class);
mScrimController = mock(ScrimController.class);
+ mFingerprintUnlockController = mock(FingerprintUnlockController.class);
IPowerManager powerManagerService = mock(IPowerManager.class);
HandlerThread handlerThread = new HandlerThread("TestThread");
handlerThread.start();
@@ -140,12 +146,16 @@
new Handler(handlerThread.getLooper()));
when(powerManagerService.isInteractive()).thenReturn(true);
mBarService = mock(IStatusBarService.class);
+ mNotificationListener = mock(NotificationListener.class);
+ mNotificationLogger = new NotificationLogger(mNotificationListener, mDependency.get(
+ UiOffloadThread.class));
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView,
- mBarService, mScrimController);
+ mBarService, mNotificationListener, mNotificationLogger, mScrimController,
+ mFingerprintUnlockController);
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mContext.getComponents();
doAnswer(invocation -> {
@@ -160,15 +170,14 @@
return null;
}).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
+ mNotificationLogger.setUpWithPresenter(mStatusBar, mStackScroller);
+
when(mStackScroller.getActivatedChild()).thenReturn(null);
- TestableLooper.get(this).setMessageHandler(new MessageHandler() {
- @Override
- public boolean onMessageHandled(Message m) {
- if (m.getCallback() == mStatusBar.mVisibilityReporter) {
- return false;
- }
- return true;
+ TestableLooper.get(this).setMessageHandler(m -> {
+ if (m.getCallback() == mStatusBar.mNotificationLogger.getVisibilityReporter()) {
+ return false;
}
+ return true;
});
}
@@ -538,18 +547,28 @@
@Test
public void testFingerprintNotification_UpdatesScrims() {
mStatusBar.mStatusBarWindowManager = mock(StatusBarWindowManager.class);
- mStatusBar.mFingerprintUnlockController = mock(FingerprintUnlockController.class);
mStatusBar.mDozeScrimController = mock(DozeScrimController.class);
mStatusBar.notifyFpAuthModeChanged();
verify(mScrimController).transitionTo(any(), any());
}
+ @Test
+ public void testFingerprintUnlock_UpdatesScrims() {
+ // Simulate unlocking from AoD with fingerprint.
+ when(mFingerprintUnlockController.getMode())
+ .thenReturn(FingerprintUnlockController.MODE_WAKE_AND_UNLOCK);
+ mStatusBar.updateScrimController();
+ verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
+ }
+
static class TestableStatusBar extends StatusBar {
public TestableStatusBar(StatusBarKeyguardViewManager man,
UnlockMethodCache unlock, KeyguardIndicationController key,
NotificationStackScrollLayout stack, HeadsUpManager hum, NotificationData nd,
PowerManager pm, SystemServicesProxy ssp, NotificationPanelView panelView,
- IStatusBarService barService, ScrimController scrimController) {
+ IStatusBarService barService, NotificationListener notificationListener,
+ NotificationLogger notificationLogger, ScrimController scrimController,
+ FingerprintUnlockController fingerprintUnlockController) {
mStatusBarKeyguardViewManager = man;
mUnlockMethodCache = unlock;
mKeyguardIndicationController = key;
@@ -561,8 +580,11 @@
mSystemServicesProxy = ssp;
mNotificationPanel = panelView;
mBarService = barService;
+ mNotificationListener = notificationListener;
+ mNotificationLogger = notificationLogger;
mWakefulnessLifecycle = createAwakeWakefulnessLifecycle();
mScrimController = scrimController;
+ mFingerprintUnlockController = fingerprintUnlockController;
}
private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
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 06568f7..401fd68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -45,6 +45,7 @@
mCallback = mock(VolumeDialogControllerImpl.C.class);
mStatusBar = mock(StatusBar.class);
mVolumeController = new TestableVolumeDialogControllerImpl(mContext, mCallback, mStatusBar);
+ mVolumeController.setEnableDialogs(true, true);
}
@Test
diff --git a/packages/VpnDialogs/res/values-ne/strings.xml b/packages/VpnDialogs/res/values-ne/strings.xml
index c19ae52..5019a06 100644
--- a/packages/VpnDialogs/res/values-ne/strings.xml
+++ b/packages/VpnDialogs/res/values-ne/strings.xml
@@ -25,8 +25,8 @@
<string name="data_received" msgid="4062776929376067820">"प्राप्त भयो:"</string>
<string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> बाइटहरू / <xliff:g id="NUMBER_1">%2$s</xliff:g> प्याकेटहरू"</string>
<string name="always_on_disconnected_title" msgid="1906740176262776166">"सधैँ-सक्रिय रहने VPN सेवामा जडान गर्न सकिँदैन"</string>
- <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> लाई सधैँ जडान भइरहनेगरि सेट अप गरिएको छ तर यसलाई अहिले नै जडान गर्न मिल्दैन। तपाईंको फोन <xliff:g id="VPN_APP_1">%1$s</xliff:g> मा पुन: जडान नहुँदासम्म यसले कुनै सार्वजनिक नेटवर्क प्रयोग गर्नेछ।"</string>
- <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> लाई सधैँ पनि जडान भइरहनेगरि सेट अप गरिएको छ तर यसलाई अहिले नै जडान गर्न मिल्दैन। VPN पुन: जडान नहुँदासम्म तपाईंसँग कुनै पनि इन्टरनेट रहनेछैन।"</string>
+ <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> लाई सधैँ जडान भइरहनेगरि सेटअप गरिएको छ तर यसलाई अहिले नै जडान गर्न मिल्दैन। तपाईंको फोन <xliff:g id="VPN_APP_1">%1$s</xliff:g> मा पुन: जडान नहुँदासम्म यसले कुनै सार्वजनिक नेटवर्क प्रयोग गर्नेछ।"</string>
+ <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> लाई सधैँ पनि जडान भइरहनेगरि सेटअप गरिएको छ तर यसलाई अहिले नै जडान गर्न मिल्दैन। VPN पुन: जडान नहुँदासम्म तपाईंसँग कुनै पनि इन्टरनेट रहनेछैन।"</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN सम्बन्धी सेटिङहरू परिवर्तन गर्नुहोस्"</string>
<string name="configure" msgid="4905518375574791375">"कन्फिगर गर्नुहोस्"</string>
diff --git a/services/Android.bp b/services/Android.bp
index 5e75c37..d125adc 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -14,7 +14,7 @@
// The convention is to name each service module 'services.$(module_name)'
static_libs: [
- "services.core.priorityboosted",
+ "services.core",
"services.accessibility",
"services.appwidget",
"services.autofill",
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
similarity index 98%
rename from services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
rename to services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 01679dd..3d7d6b7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -71,7 +71,7 @@
* This class represents an accessibility client - either an AccessibilityService or a UiAutomation.
* It is responsible for behavior common to both types of clients.
*/
-abstract class AccessibilityClientConnection extends IAccessibilityServiceConnection.Stub
+abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServiceConnection.Stub
implements ServiceConnection, IBinder.DeathRecipient, KeyEventDispatcher.KeyEventFilter,
FingerprintGestureDispatcher.FingerprintGestureClient {
private static final boolean DEBUG = false;
@@ -238,7 +238,7 @@
int flags);
}
- public AccessibilityClientConnection(Context context, ComponentName componentName,
+ public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName,
AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
Object lock, SecurityPolicy securityPolicy, SystemSupport systemSupport,
WindowManagerInternal windowManagerInternal,
@@ -339,6 +339,11 @@
}
}
+ int getRelevantEventTypes() {
+ return (mUsesAccessibilityCache ? AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK : 0)
+ | mEventTypes;
+ }
+
@Override
public void setServiceInfo(AccessibilityServiceInfo info) {
final long identity = Binder.clearCallingIdentity();
@@ -954,13 +959,15 @@
public void notifyAccessibilityEvent(AccessibilityEvent event) {
synchronized (mLock) {
+ final int eventType = event.getEventType();
+
final boolean serviceWantsEvent = wantsEventLocked(event);
- if (!serviceWantsEvent && !mUsesAccessibilityCache &&
- ((AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK & event.getEventType()) == 0)) {
+ final boolean requiredForCacheConsistency = mUsesAccessibilityCache
+ && ((AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK & eventType) != 0);
+ if (!serviceWantsEvent && !requiredForCacheConsistency) {
return;
}
- final int eventType = event.getEventType();
// Make a copy since during dispatch it is possible the event to
// be modified to remove its source if the receiving service does
// not have permission to access the window content.
@@ -1226,6 +1233,10 @@
return windowId;
}
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
private final class InvocationHandler extends Handler {
public static final int MSG_ON_GESTURE = 1;
public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 2;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index ac0cdd7..d83f6ae 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -20,6 +20,8 @@
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
+
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -28,6 +30,7 @@
import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.app.AlertDialog;
+import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManagerInternal;
import android.content.BroadcastReceiver;
@@ -83,7 +86,6 @@
import android.view.View;
import android.view.WindowInfo;
import android.view.WindowManager;
-import android.view.accessibility.AccessibilityCache;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityManager;
@@ -113,6 +115,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -130,7 +133,7 @@
* on the device. Events are dispatched to {@link AccessibilityService}s.
*/
public class AccessibilityManagerService extends IAccessibilityManager.Stub
- implements AccessibilityClientConnection.SystemSupport {
+ implements AbstractAccessibilityServiceConnection.SystemSupport {
private static final boolean DEBUG = false;
@@ -193,6 +196,8 @@
private final SecurityPolicy mSecurityPolicy;
+ private final AppOpsManager mAppOpsManager;
+
private final MainHandler mMainHandler;
private final GlobalActionPerformer mGlobalActionPerformer;
@@ -261,6 +266,7 @@
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mSecurityPolicy = new SecurityPolicy();
+ mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mMainHandler = new MainHandler(mContext.getMainLooper());
mGlobalActionPerformer = new GlobalActionPerformer(mContext, mWindowManagerService);
@@ -451,7 +457,7 @@
}
@Override
- public long addClient(IAccessibilityManagerClient client, int userId) {
+ public long addClient(IAccessibilityManagerClient callback, int userId) {
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -463,15 +469,17 @@
// the system UI or the system we add it to the global state that
// is shared across users.
UserState userState = getUserStateLocked(resolvedUserId);
+ Client client = new Client(callback, Binder.getCallingUid(), userState);
if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) {
- mGlobalClients.register(client);
+ mGlobalClients.register(callback, client);
if (DEBUG) {
Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid());
}
return IntPair.of(
- userState.getClientState(), userState.mLastSentRelevantEventTypes);
+ userState.getClientState(),
+ client.mLastSentRelevantEventTypes);
} else {
- userState.mUserClients.register(client);
+ userState.mUserClients.register(callback, client);
// If this client is not for the current user we do not
// return a state since it is not for the foreground user.
// We will send the state to the client on a user switch.
@@ -481,7 +489,7 @@
}
return IntPair.of(
(resolvedUserId == mCurrentUserId) ? userState.getClientState() : 0,
- userState.mLastSentRelevantEventTypes);
+ client.mLastSentRelevantEventTypes);
}
}
}
@@ -768,7 +776,6 @@
public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) {
synchronized (mLock) {
mUiAutomationManager.unregisterUiTestAutomationServiceLocked(serviceClient);
- onUserStateChangedLocked(getCurrentUserStateLocked());
}
}
@@ -948,7 +955,7 @@
* Has no effect if no item has accessibility focus, if the item with accessibility
* focus does not expose the specified action, or if the action fails.
*
- * @param actionId The id of the action to perform.
+ * @param action The action to perform.
*
* @return {@code true} if the action was performed. {@code false} if it was not.
*/
@@ -1223,14 +1230,11 @@
for (int i = 0, count = installedServices.size(); i < count; i++) {
ResolveInfo resolveInfo = installedServices.get(i);
ServiceInfo serviceInfo = resolveInfo.serviceInfo;
- if (!android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE.equals(
- serviceInfo.permission)) {
- Slog.w(LOG_TAG, "Skipping accessibilty service " + new ComponentName(
- serviceInfo.packageName, serviceInfo.name).flattenToShortString()
- + ": it does not require the permission "
- + android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE);
+
+ if (!canRegisterService(serviceInfo)) {
continue;
}
+
AccessibilityServiceInfo accessibilityServiceInfo;
try {
accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
@@ -1251,6 +1255,28 @@
return false;
}
+ private boolean canRegisterService(ServiceInfo serviceInfo) {
+ if (!android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE.equals(
+ serviceInfo.permission)) {
+ Slog.w(LOG_TAG, "Skipping accessibility service " + new ComponentName(
+ serviceInfo.packageName, serviceInfo.name).flattenToShortString()
+ + ": it does not require the permission "
+ + android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE);
+ return false;
+ }
+
+ int servicePackageUid = serviceInfo.applicationInfo.uid;
+ if (mAppOpsManager.noteOpNoThrow(AppOpsManager.OP_BIND_ACCESSIBILITY_SERVICE,
+ servicePackageUid, serviceInfo.packageName) != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(LOG_TAG, "Skipping accessibility service " + new ComponentName(
+ serviceInfo.packageName, serviceInfo.name).flattenToShortString()
+ + ": disallowed by AppOps");
+ return false;
+ }
+
+ return true;
+ }
+
private boolean readEnabledAccessibilityServicesLocked(UserState userState) {
mTempComponentNameSet.clear();
readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
@@ -1307,33 +1333,67 @@
}
private void updateRelevantEventsLocked(UserState userState) {
- int relevantEventTypes = AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK;
- for (AccessibilityServiceConnection service : userState.mBoundServices) {
- relevantEventTypes |= service.mEventTypes;
- }
- relevantEventTypes |= mUiAutomationManager.getRequestedEventMaskLocked();
- int finalRelevantEventTypes = relevantEventTypes;
+ mMainHandler.post(() -> {
+ broadcastToClients(userState, ignoreRemoteException(client -> {
+ int relevantEventTypes = computeRelevantEventTypes(userState, client);
- if (userState.mLastSentRelevantEventTypes != finalRelevantEventTypes) {
- userState.mLastSentRelevantEventTypes = finalRelevantEventTypes;
- mMainHandler.obtainMessage(MainHandler.MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS,
- userState.mUserId, finalRelevantEventTypes);
- mMainHandler.post(() -> {
- broadcastToClients(userState, (client) -> {
- try {
- client.setRelevantEventTypes(finalRelevantEventTypes);
- } catch (RemoteException re) {
- /* ignore */
- }
- });
- });
+ if (client.mLastSentRelevantEventTypes != relevantEventTypes) {
+ client.mLastSentRelevantEventTypes = relevantEventTypes;
+ client.mCallback.setRelevantEventTypes(relevantEventTypes);
+ }
+ }));
+ });
+ }
+
+ private int computeRelevantEventTypes(UserState userState, Client client) {
+ int relevantEventTypes = 0;
+
+ int numBoundServices = userState.mBoundServices.size();
+ for (int i = 0; i < numBoundServices; i++) {
+ AccessibilityServiceConnection service =
+ userState.mBoundServices.get(i);
+ relevantEventTypes |= isClientInPackageWhitelist(service.getServiceInfo(), client)
+ ? service.getRelevantEventTypes()
+ : 0;
}
+ relevantEventTypes |= isClientInPackageWhitelist(
+ mUiAutomationManager.getServiceInfo(), client)
+ ? mUiAutomationManager.getRelevantEventTypes()
+ : 0;
+ return relevantEventTypes;
+ }
+
+ private static boolean isClientInPackageWhitelist(
+ @Nullable AccessibilityServiceInfo serviceInfo, Client client) {
+ if (serviceInfo == null) return false;
+
+ String[] clientPackages = client.mPackageNames;
+ boolean result = ArrayUtils.isEmpty(serviceInfo.packageNames);
+ if (!result && clientPackages != null) {
+ for (String packageName : clientPackages) {
+ if (ArrayUtils.contains(serviceInfo.packageNames, packageName)) {
+ result = true;
+ break;
+ }
+ }
+ }
+ if (!result) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "Dropping events: "
+ + Arrays.toString(clientPackages) + " -> "
+ + serviceInfo.getComponentName().flattenToShortString()
+ + " due to not being in package whitelist "
+ + Arrays.toString(serviceInfo.packageNames));
+ }
+ }
+
+ return result;
}
private void broadcastToClients(
- UserState userState, Consumer<IAccessibilityManagerClient> clientAction) {
- mGlobalClients.broadcast(clientAction);
- userState.mUserClients.broadcast(clientAction);
+ UserState userState, Consumer<Client> clientAction) {
+ mGlobalClients.broadcastForEachCookie(clientAction);
+ userState.mUserClients.broadcastForEachCookie(clientAction);
}
private void unbindAllServicesLocked(UserState userState) {
@@ -2134,6 +2194,7 @@
* permission to write secure settings, since someone with that permission can enable
* accessibility services themselves.
*/
+ @Override
public void performAccessibilityShortcut() {
if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)
&& (mContext.checkCallingPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
@@ -2423,13 +2484,8 @@
synchronized (mLock) {
userState = getUserStateLocked(userId);
}
- broadcastToClients(userState, (client) -> {
- try {
- client.setRelevantEventTypes(relevantEventTypes);
- } catch (RemoteException re) {
- /* ignore */
- }
- });
+ broadcastToClients(userState, ignoreRemoteException(
+ client -> client.mCallback.setRelevantEventTypes(relevantEventTypes)));
} break;
case MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER: {
@@ -2478,30 +2534,14 @@
private void sendStateToClients(int clientState,
RemoteCallbackList<IAccessibilityManagerClient> clients) {
- clients.broadcast((client) -> {
- try {
- client.setState(clientState);
- } catch (RemoteException re) {
- /* ignore */
- }
- });
+ clients.broadcast(ignoreRemoteException(
+ client -> client.setState(clientState)));
}
private void notifyClientsOfServicesStateChange(
RemoteCallbackList<IAccessibilityManagerClient> clients) {
- try {
- final int userClientCount = clients.beginBroadcast();
- for (int i = 0; i < userClientCount; i++) {
- IAccessibilityManagerClient client = clients.getBroadcastItem(i);
- try {
- client.notifyServicesStateChanged();
- } catch (RemoteException re) {
- /* ignore */
- }
- }
- } finally {
- clients.finishBroadcast();
- }
+ clients.broadcast(ignoreRemoteException(
+ client -> client.notifyServicesStateChanged()));
}
}
@@ -2962,11 +3002,14 @@
}
private boolean isValidPackageForUid(String packageName, int uid) {
+ final long token = Binder.clearCallingIdentity();
try {
return uid == mPackageManager.getPackageUidAsUser(
packageName, UserHandle.getUserId(uid));
} catch (PackageManager.NameNotFoundException e) {
return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
@@ -3310,20 +3353,20 @@
}
public boolean canGetAccessibilityNodeInfoLocked(
- AccessibilityClientConnection service, int windowId) {
+ AbstractAccessibilityServiceConnection service, int windowId) {
return canRetrieveWindowContentLocked(service) && isRetrievalAllowingWindow(windowId);
}
- public boolean canRetrieveWindowsLocked(AccessibilityClientConnection service) {
+ public boolean canRetrieveWindowsLocked(AbstractAccessibilityServiceConnection service) {
return canRetrieveWindowContentLocked(service) && service.mRetrieveInteractiveWindows;
}
- public boolean canRetrieveWindowContentLocked(AccessibilityClientConnection service) {
+ public boolean canRetrieveWindowContentLocked(AbstractAccessibilityServiceConnection service) {
return (service.mAccessibilityServiceInfo.getCapabilities()
& AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
}
- public boolean canControlMagnification(AccessibilityClientConnection service) {
+ public boolean canControlMagnification(AbstractAccessibilityServiceConnection service) {
return (service.mAccessibilityServiceInfo.getCapabilities()
& AccessibilityServiceInfo.CAPABILITY_CAN_CONTROL_MAGNIFICATION) != 0;
}
@@ -3420,7 +3463,7 @@
final int windowCount = mWindows.size();
for (int i = 0; i < windowCount; i++) {
AccessibilityWindowInfo window = mWindows.get(i);
- if (window.inPictureInPicture()) {
+ if (window.isInPictureInPictureMode()) {
return window;
}
}
@@ -3451,13 +3494,26 @@
}
}
+ /** Represents an {@link AccessibilityManager} */
+ class Client {
+ final IAccessibilityManagerClient mCallback;
+ final String[] mPackageNames;
+ int mLastSentRelevantEventTypes;
+
+ private Client(IAccessibilityManagerClient callback, int clientUid, UserState userState) {
+ mCallback = callback;
+ mPackageNames = mPackageManager.getPackagesForUid(clientUid);
+ mLastSentRelevantEventTypes = computeRelevantEventTypes(userState, this);
+ }
+ }
+
public class UserState {
public final int mUserId;
// Non-transient state.
public final RemoteCallbackList<IAccessibilityManagerClient> mUserClients =
- new RemoteCallbackList<>();
+ new RemoteCallbackList<>();
public final SparseArray<RemoteAccessibilityConnection> mInteractionConnections =
new SparseArray<>();
@@ -3469,8 +3525,6 @@
public final CopyOnWriteArrayList<AccessibilityServiceConnection> mBoundServices =
new CopyOnWriteArrayList<>();
- public int mLastSentRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK;
-
public final Map<ComponentName, AccessibilityServiceConnection> mComponentNameToServiceMap =
new HashMap<>();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 9cafa1e..5f6efb6 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -19,9 +19,7 @@
import static android.provider.Settings.Secure.SHOW_MODE_AUTO;
import android.accessibilityservice.AccessibilityServiceInfo;
-import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.IAccessibilityServiceClient;
-import android.accessibilityservice.IAccessibilityServiceConnection;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -39,7 +37,6 @@
import com.android.server.wm.WindowManagerInternal;
import java.lang.ref.WeakReference;
-import java.util.List;
import java.util.Set;
/**
@@ -50,7 +47,7 @@
* passed to the service it represents as soon it is bound. It also serves as the
* connection for the service.
*/
-class AccessibilityServiceConnection extends AccessibilityClientConnection {
+class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection {
private static final String LOG_TAG = "AccessibilityServiceConnection";
/*
Holding a weak reference so there isn't a loop of references. UserState keeps lists of bound
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index f057112..ed3b3e7 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -18,6 +18,7 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
+import android.annotation.Nullable;
import android.app.UiAutomation;
import android.content.ComponentName;
import android.content.Context;
@@ -28,6 +29,7 @@
import android.util.Slog;
import android.view.accessibility.AccessibilityEvent;
+import com.android.internal.util.DumpUtils;
import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
@@ -45,6 +47,8 @@
private AccessibilityServiceInfo mUiAutomationServiceInfo;
+ private AbstractAccessibilityServiceConnection.SystemSupport mSystemSupport;
+
private int mUiAutomationFlags;
private IBinder mUiAutomationServiceOwner;
@@ -75,7 +79,7 @@
Context context, AccessibilityServiceInfo accessibilityServiceInfo,
int id, Handler mainHandler, Object lock,
AccessibilityManagerService.SecurityPolicy securityPolicy,
- AccessibilityClientConnection.SystemSupport systemSupport,
+ AbstractAccessibilityServiceConnection.SystemSupport systemSupport,
WindowManagerInternal windowManagerInternal,
GlobalActionPerformer globalActionPerfomer, int flags) {
accessibilityServiceInfo.setComponentName(COMPONENT_NAME);
@@ -92,6 +96,7 @@
return;
}
+ mSystemSupport = systemSupport;
mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
mainHandler, lock, securityPolicy, systemSupport, windowManagerInternal,
globalActionPerfomer);
@@ -153,6 +158,17 @@
return mUiAutomationService.mEventTypes;
}
+ int getRelevantEventTypes() {
+ if (mUiAutomationService == null) return 0;
+ return mUiAutomationService.getRelevantEventTypes();
+ }
+
+ @Nullable
+ AccessibilityServiceInfo getServiceInfo() {
+ if (mUiAutomationService == null) return null;
+ return mUiAutomationService.getServiceInfo();
+ }
+
void dumpUiAutomationService(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (mUiAutomationService != null) {
mUiAutomationService.dump(fd, pw, args);
@@ -169,9 +185,10 @@
mUiAutomationServiceOwner.unlinkToDeath(mUiAutomationServiceOwnerDeathRecipient, 0);
mUiAutomationServiceOwner = null;
}
+ mSystemSupport.onClientChange(false);
}
- private class UiAutomationService extends AccessibilityClientConnection {
+ private class UiAutomationService extends AbstractAccessibilityServiceConnection {
private final Handler mMainHandler;
UiAutomationService(Context context, AccessibilityServiceInfo accessibilityServiceInfo,
@@ -224,6 +241,17 @@
return true;
}
+ @Override
+ public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
+ synchronized (mLock) {
+ pw.append("Ui Automation[eventTypes="
+ + AccessibilityEvent.eventTypeToString(mEventTypes));
+ pw.append(", notificationTimeout=" + mNotificationTimeout);
+ pw.append("]");
+ }
+ }
+
// Since this isn't really an accessibility service, several methods are just stubbed here.
@Override
public boolean setSoftKeyboardShowMode(int mode) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 7bc63f0..3361824 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -26,6 +26,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.IActivityManager;
import android.content.ComponentName;
@@ -42,6 +43,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -50,6 +52,7 @@
import android.provider.Settings;
import android.service.autofill.AutofillService;
import android.service.autofill.AutofillServiceInfo;
+import android.service.autofill.FieldClassification;
import android.service.autofill.FieldClassification.Match;
import android.service.autofill.FillEventHistory;
import android.service.autofill.FillEventHistory.Event;
@@ -74,11 +77,12 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.HandlerCaller;
+import com.android.server.LocalServices;
import com.android.server.autofill.ui.AutoFillUI;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.List;
import java.util.Random;
/**
@@ -482,16 +486,17 @@
* Asserts the component is owned by the caller.
*/
private void assertCallerLocked(@NonNull ComponentName componentName) {
+ final String packageName = componentName.getPackageName();
final PackageManager pm = mContext.getPackageManager();
final int callingUid = Binder.getCallingUid();
final int packageUid;
try {
- packageUid = pm.getPackageUidAsUser(componentName.getPackageName(),
- UserHandle.getCallingUserId());
+ packageUid = pm.getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
} catch (NameNotFoundException e) {
throw new SecurityException("Could not verify UID for " + componentName);
}
- if (callingUid != packageUid) {
+ if (callingUid != packageUid && !LocalServices.getService(ActivityManagerInternal.class)
+ .hasRunningActivity(callingUid, packageName)) {
final String[] packages = pm.getPackagesForUid(callingUid);
final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid;
Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid
@@ -717,37 +722,45 @@
@Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
@Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
@Nullable ArrayList<AutofillId> detectedFieldIdsList,
- @Nullable ArrayList<Match> detectedMatchesList,
+ @Nullable ArrayList<FieldClassification> detectedFieldClassificationsList,
@NonNull String appPackageName) {
-
synchronized (mLock) {
if (isValidEventLocked("logDatasetNotSelected()", sessionId)) {
AutofillId[] detectedFieldsIds = null;
- Match[] detectedMatches = null;
+ FieldClassification[] detectedFieldClassifications = null;
if (detectedFieldIdsList != null) {
detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()];
detectedFieldIdsList.toArray(detectedFieldsIds);
- detectedMatches = new Match[detectedMatchesList.size()];
- detectedMatchesList.toArray(detectedMatches);
+ detectedFieldClassifications =
+ new FieldClassification[detectedFieldClassificationsList.size()];
+ detectedFieldClassificationsList.toArray(detectedFieldClassifications);
- final int size = detectedMatchesList.size();
+ final int numberFields = detectedFieldsIds.length;
+ int totalSize = 0;
float totalScore = 0;
- for (int i = 0; i < size; i++) {
- totalScore += detectedMatches[i].getScore();
+ for (int i = 0; i < numberFields; i++) {
+ final FieldClassification fc = detectedFieldClassifications[i];
+ final List<Match> matches = fc.getMatches();
+ final int size = matches.size();
+ totalSize += size;
+ for (int j = 0; j < size; j++) {
+ totalScore += matches.get(j).getScore();
+ }
}
- final int averageScore = (int) ((totalScore * 100) / size);
- mMetricsLogger.write(
- Helper.newLogMaker(MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES,
- appPackageName, getServicePackageName())
- .setCounterValue(size)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE, averageScore));
+ final int averageScore = (int) ((totalScore * 100) / totalSize);
+ mMetricsLogger.write(Helper
+ .newLogMaker(MetricsEvent.AUTOFILL_FIELD_CLASSIFICATION_MATCHES,
+ appPackageName, getServicePackageName())
+ .setCounterValue(numberFields)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_MATCH_SCORE,
+ averageScore));
}
mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null,
clientState, selectedDatasets, ignoredDatasets,
changedFieldIds, changedDatasetIds,
manuallyFilledFieldIds, manuallyFilledDatasetIds,
- detectedFieldsIds, detectedMatches));
+ detectedFieldsIds, detectedFieldClassifications));
}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 56f5f64..7b85a6c 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -66,6 +66,7 @@
import android.service.autofill.UserData;
import android.service.autofill.ValueFinder;
import android.service.autofill.EditDistanceScorer;
+import android.service.autofill.FieldClassification;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LocalLog;
@@ -512,7 +513,6 @@
}
final AutofillId[] fieldClassificationIds = response.getFieldClassificationIds();
- // TODO(b/67867469): remove once feature is finished (or use method from AFM to check)
if (fieldClassificationIds != null && !mService.isFieldClassificationEnabled()) {
Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
processNullResponseLocked(requestFlags);
@@ -962,15 +962,15 @@
final UserData userData = mService.getUserData();
final ArrayList<AutofillId> detectedFieldIds;
- final ArrayList<Match> detectedMatches;
+ final ArrayList<FieldClassification> detectedFieldClassifications;
if (userData != null) {
final int maxFieldsSize = UserData.getMaxFieldClassificationIdsSize();
detectedFieldIds = new ArrayList<>(maxFieldsSize);
- detectedMatches = new ArrayList<>(maxFieldsSize);
+ detectedFieldClassifications = new ArrayList<>(maxFieldsSize);
} else {
detectedFieldIds = null;
- detectedMatches = null;
+ detectedFieldClassifications = null;
}
for (int i = 0; i < mViewStates.size(); i++) {
@@ -1079,8 +1079,8 @@
// Sets field classification score for field
if (userData!= null) {
- setScore(detectedFieldIds, detectedMatches, userData, viewState.id,
- currentValue);
+ setScore(detectedFieldIds, detectedFieldClassifications, userData,
+ viewState.id, currentValue);
}
} // else
} // else
@@ -1094,7 +1094,7 @@
+ ", changedDatasetIds=" + changedDatasetIds
+ ", manuallyFilledIds=" + manuallyFilledIds
+ ", detectedFieldIds=" + detectedFieldIds
- + ", detectedMatches=" + detectedMatches
+ + ", detectedFieldClassifications=" + detectedFieldClassifications
);
}
@@ -1117,16 +1117,17 @@
mService.logContextCommitted(id, mClientState, mSelectedDatasetIds, ignoredDatasets,
changedFieldIds, changedDatasetIds,
manuallyFilledFieldIds, manuallyFilledDatasetIds,
- detectedFieldIds, detectedMatches, mComponentName.getPackageName());
+ detectedFieldIds, detectedFieldClassifications, mComponentName.getPackageName());
}
/**
- * Adds the top score match to {@code detectedFieldsIds} and {@code detectedMatches} for
+ * Adds the matches to {@code detectedFieldsIds} and {@code detectedFieldClassifications} for
* {@code fieldId} based on its {@code currentValue} and {@code userData}.
*/
private static void setScore(@NonNull ArrayList<AutofillId> detectedFieldIds,
- @NonNull ArrayList<Match> detectedMatches, @NonNull UserData userData,
- @NonNull AutofillId fieldId, @NonNull AutofillValue currentValue) {
+ @NonNull ArrayList<FieldClassification> detectedFieldClassifications,
+ @NonNull UserData userData, @NonNull AutofillId fieldId,
+ @NonNull AutofillValue currentValue) {
final String[] userValues = userData.getValues();
final String[] remoteIds = userData.getRemoteIds();
@@ -1139,23 +1140,26 @@
+ valuesLength + ", ids.length = " + idsLength);
return;
}
- String remoteId = null;
- float topScore = 0;
+
+ ArrayList<Match> matches = null;
for (int i = 0; i < userValues.length; i++) {
+ String remoteId = remoteIds[i];
final String value = userValues[i];
final float score = userData.getScorer().getScore(currentValue, value);
- if (score > topScore) {
- topScore = score;
- remoteId = remoteIds[i];
+ if (score > 0) {
+ if (sVerbose) {
+ Slog.v(TAG, "adding score " + score + " at index " + i + " and id " + fieldId);
+ }
+ if (matches == null) {
+ matches = new ArrayList<>(userValues.length);
+ }
+ matches.add(new Match(remoteId, score));
}
+ else if (sVerbose) Slog.v(TAG, "skipping score 0 at index " + i + " and id " + fieldId);
}
-
- if (remoteId != null && topScore > 0) {
- if (sVerbose) Slog.v(TAG, "setScores(): top score for #" + fieldId + " is " + topScore);
+ if (matches != null) {
detectedFieldIds.add(fieldId);
- detectedMatches.add(new Match(remoteId, topScore));
- } else if (sVerbose) {
- Slog.v(TAG, "setScores(): no top score for #" + fieldId + ": " + topScore);
+ detectedFieldClassifications.add(new FieldClassification(matches));
}
}
@@ -1662,7 +1666,7 @@
isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL);
mViewStates.put(id, viewState);
- // TODO(b/67867469): for optimization purposes, should also ignore if change is
+ // TODO(b/70407264): for optimization purposes, should also ignore if change is
// detectable, and batch-send them when the session is finished (but that will
// require tracking detectable fields on AutofillManager)
if (isIgnored) {
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index bd0d853..94b06b6 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -25,7 +25,6 @@
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_CLEAR;
-import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_INIT;
import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP;
import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE;
import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR;
@@ -120,6 +119,7 @@
import com.android.server.backup.restore.ActiveRestoreSession;
import com.android.server.backup.restore.PerformUnifiedRestoreTask;
import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportNotRegisteredException;
import com.android.server.backup.utils.AppBackupUtils;
import com.android.server.backup.utils.BackupManagerMonitorUtils;
import com.android.server.backup.utils.BackupObserverUtils;
@@ -1083,56 +1083,35 @@
return mBackupPasswordManager.backupPasswordMatches(currentPw);
}
- // Maintain persistent state around whether need to do an initialize operation.
- // Must be called with the queue lock held.
- public void recordInitPendingLocked(boolean isPending, String transportName) {
+ /**
+ * Maintain persistent state around whether need to do an initialize operation.
+ * Must be called with the queue lock held.
+ */
+ @GuardedBy("mQueueLock")
+ public void recordInitPendingLocked(
+ boolean isPending, String transportName, String transportDirName) {
if (MORE_DEBUG) {
Slog.i(TAG, "recordInitPendingLocked: " + isPending
+ " on transport " + transportName);
}
- mBackupHandler.removeMessages(MSG_RETRY_INIT);
- try {
- IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
- if (transport != null) {
- String transportDirName = transport.transportDirName();
- File stateDir = new File(mBaseStateDir, transportDirName);
- File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
+ File stateDir = new File(mBaseStateDir, transportDirName);
+ File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
- if (isPending) {
- // We need an init before we can proceed with sending backup data.
- // Record that with an entry in our set of pending inits, as well as
- // journaling it via creation of a sentinel file.
- mPendingInits.add(transportName);
- try {
- (new FileOutputStream(initPendingFile)).close();
- } catch (IOException ioe) {
- // Something is badly wrong with our permissions; just try to move on
- }
- } else {
- // No more initialization needed; wipe the journal and reset our state.
- initPendingFile.delete();
- mPendingInits.remove(transportName);
- }
- return; // done; don't fall through to the error case
- }
- } catch (Exception e) {
- // transport threw when asked its name; fall through to the lookup-failed case
- Slog.e(TAG, "Transport " + transportName + " failed to report name: "
- + e.getMessage());
- }
-
- // The named transport doesn't exist or threw. This operation is
- // important, so we record the need for a an init and post a message
- // to retry the init later.
if (isPending) {
+ // We need an init before we can proceed with sending backup data.
+ // Record that with an entry in our set of pending inits, as well as
+ // journaling it via creation of a sentinel file.
mPendingInits.add(transportName);
- mBackupHandler.sendMessageDelayed(
- mBackupHandler.obtainMessage(MSG_RETRY_INIT,
- (isPending ? 1 : 0),
- 0,
- transportName),
- TRANSPORT_RETRY_INTERVAL);
+ try {
+ (new FileOutputStream(initPendingFile)).close();
+ } catch (IOException ioe) {
+ // Something is badly wrong with our permissions; just try to move on
+ }
+ } else {
+ // No more initialization needed; wipe the journal and reset our state.
+ initPendingFile.delete();
+ mPendingInits.remove(transportName);
}
}
@@ -1520,7 +1499,11 @@
}
// clear an application's data, blocking until the operation completes or times out
- public void clearApplicationDataSynchronous(String packageName) {
+ // if keepSystemState is true, we intentionally do not also clear system state that
+ // would ordinarily also be cleared, because we aren't actually wiping the app back
+ // to empty; we're bringing it into the actual expected state related to the already-
+ // restored notification state etc.
+ public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) {
// Don't wipe packages marked allowClearUserData=false
try {
PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
@@ -1541,7 +1524,7 @@
synchronized (mClearDataLock) {
mClearingData = true;
try {
- mActivityManager.clearApplicationUserData(packageName, observer, 0);
+ mActivityManager.clearApplicationUserData(packageName, keepSystemState, observer, 0);
} catch (RemoteException e) {
// can't happen because the activity manager is in this process
}
@@ -1610,27 +1593,9 @@
return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
}
- // We're using pieces of the new binding on-demand infra-structure and the old always-bound
- // infra-structure below this comment. The TransportManager.getCurrentTransportClient() line
- // is using the new one and TransportManager.getCurrentTransportBinder() is using the old.
- // This is weird but there is a reason.
- // This is the natural place to put TransportManager.getCurrentTransportClient() because of
- // the null handling below that should be the same for TransportClient.
- // TransportClient.connect() would return a IBackupTransport for us (instead of using the
- // old infra), but it may block and we don't want this in this thread.
- // The only usage of transport in this method is for transport.transportDirName(). When the
- // push-from-transport part of binding on-demand is in place we will replace the calls for
- // IBackupTransport.transportDirName() with calls for
- // TransportManager.transportDirName(transportName) or similar. So we'll leave the old piece
- // here until we implement that.
- // TODO(brufino): Remove always-bound code mTransportManager.getCurrentTransportBinder()
TransportClient transportClient =
mTransportManager.getCurrentTransportClient("BMS.requestBackup()");
- IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
- if (transportClient == null || transport == null) {
- if (transportClient != null) {
- mTransportManager.disposeOfTransportClient(transportClient, "BMS.requestBackup()");
- }
+ if (transportClient == null) {
BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
@@ -1675,15 +1640,7 @@
+ " k/v backups");
}
- String dirName;
- try {
- dirName = transport.transportDirName();
- } catch (Exception e) {
- Slog.e(TAG, "Transport unavailable while attempting backup: " + e.getMessage());
- BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
- return BackupManager.ERROR_TRANSPORT_ABORTED;
- }
-
+ String dirName = transportClient.getTransportDirName();
boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;
Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
@@ -1994,16 +1951,17 @@
writeFullBackupScheduleAsync();
}
- private boolean fullBackupAllowable(IBackupTransport transport) {
- if (transport == null) {
- Slog.w(TAG, "Transport not present; full data backup not performed");
+ private boolean fullBackupAllowable(String transportName) {
+ if (!mTransportManager.isTransportRegistered(transportName)) {
+ Slog.w(TAG, "Transport not registered; full data backup not performed");
return false;
}
// Don't proceed unless we have already established package metadata
// for the current dataset via a key/value backup pass.
try {
- File stateDir = new File(mBaseStateDir, transport.transportDirName());
+ String transportDirName = mTransportManager.getTransportDirName(transportName);
+ File stateDir = new File(mBaseStateDir, transportDirName);
File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
if (pmState.length() <= 0) {
if (DEBUG) {
@@ -2093,7 +2051,8 @@
headBusy = false;
- if (!fullBackupAllowable(mTransportManager.getCurrentTransportBinder())) {
+ String transportName = mTransportManager.getCurrentTransportName();
+ if (!fullBackupAllowable(transportName)) {
if (MORE_DEBUG) {
Slog.i(TAG, "Preconditions not met; not running full backup");
}
@@ -2541,7 +2500,8 @@
throw new IllegalStateException("Restore supported only for the device owner");
}
- if (!fullBackupAllowable(mTransportManager.getCurrentTransportBinder())) {
+ String transportName = mTransportManager.getCurrentTransportName();
+ if (!fullBackupAllowable(transportName)) {
Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
} else {
if (DEBUG) {
@@ -2822,10 +2782,30 @@
if (wasEnabled && mProvisioned) {
// NOTE: we currently flush every registered transport, not just
// the currently-active one.
- String[] allTransports = mTransportManager.getBoundTransportNames();
+ List<String> transportNames = new ArrayList<>();
+ List<String> transportDirNames = new ArrayList<>();
+ mTransportManager.forEachRegisteredTransport(
+ name -> {
+ final String dirName;
+ try {
+ dirName =
+ mTransportManager
+ .getTransportDirName(name);
+ } catch (TransportNotRegisteredException e) {
+ // Should never happen
+ Slog.e(TAG, "Unexpected unregistered transport", e);
+ return;
+ }
+ transportNames.add(name);
+ transportDirNames.add(dirName);
+ });
+
// build the set of transports for which we are posting an init
- for (String transport : allTransports) {
- recordInitPendingLocked(true, transport);
+ for (int i = 0; i < transportNames.size(); i++) {
+ recordInitPendingLocked(
+ true,
+ transportNames.get(i),
+ transportDirNames.get(i));
}
mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
mRunInitIntent);
@@ -2989,7 +2969,7 @@
final long oldId = Binder.clearCallingIdentity();
try {
- mTransportManager.describeTransport(
+ mTransportManager.updateTransportAttributes(
transportComponent,
name,
configurationIntent,
@@ -3089,23 +3069,16 @@
public Intent getConfigurationIntent(String transportName) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getConfigurationIntent");
-
- final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
- if (transport != null) {
- try {
- final Intent intent = transport.configurationIntent();
- if (MORE_DEBUG) {
- Slog.d(TAG, "getConfigurationIntent() returning config intent "
- + intent);
- }
- return intent;
- } catch (Exception e) {
- /* fall through to return null */
- Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
+ try {
+ Intent intent = mTransportManager.getTransportConfigurationIntent(transportName);
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "getConfigurationIntent() returning intent " + intent);
}
+ return intent;
+ } catch (TransportNotRegisteredException e) {
+ Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
+ return null;
}
-
- return null;
}
// Supply the configuration summary string for the given transport. If the name is
@@ -3139,22 +3112,16 @@
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getDataManagementIntent");
- final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
- if (transport != null) {
- try {
- final Intent intent = transport.dataManagementIntent();
- if (MORE_DEBUG) {
- Slog.d(TAG, "getDataManagementIntent() returning intent "
- + intent);
- }
- return intent;
- } catch (Exception e) {
- /* fall through to return null */
- Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
+ try {
+ Intent intent = mTransportManager.getTransportDataManagementIntent(transportName);
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "getDataManagementIntent() returning intent " + intent);
}
+ return intent;
+ } catch (TransportNotRegisteredException e) {
+ Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
+ return null;
}
-
- return null;
}
// Supply the menu label for affordances that fire the manage-data intent
@@ -3164,19 +3131,16 @@
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getDataManagementLabel");
- final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
- if (transport != null) {
- try {
- final String text = transport.dataManagementLabel();
- if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text);
- return text;
- } catch (Exception e) {
- /* fall through to return null */
- Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
+ try {
+ String label = mTransportManager.getTransportDataManagementLabel(transportName);
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "getDataManagementLabel() returning " + label);
}
+ return label;
+ } catch (TransportNotRegisteredException e) {
+ Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
+ return null;
}
-
- return null;
}
// Callback: a requested backup agent has been instantiated. This should only
@@ -3238,10 +3202,10 @@
skip = true;
}
- // Do we have a transport to fetch data for us?
- IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
- if (transport == null) {
- if (DEBUG) Slog.w(TAG, "No transport");
+ TransportClient transportClient =
+ mTransportManager.getCurrentTransportClient("BMS.restoreAtInstall()");
+ if (transportClient == null) {
+ if (DEBUG) Slog.w(TAG, "No transport client");
skip = true;
}
@@ -3258,16 +3222,26 @@
// The eventual message back into the Package Manager to run the post-install
// steps for 'token' will be issued from the restore handling code.
- // This can throw and so *must* happen before the wakelock is acquired
- String dirName = transport.transportDirName();
-
mWakelock.acquire();
+
+ OnTaskFinishedListener listener = caller -> {
+ mTransportManager.disposeOfTransportClient(transportClient, caller);
+ mWakelock.release();
+ };
+
if (MORE_DEBUG) {
Slog.d(TAG, "Restore at install of " + packageName);
}
Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
- msg.obj = new RestoreParams(transport, dirName, null, null,
- restoreSet, packageName, token);
+ msg.obj =
+ RestoreParams.createForRestoreAtInstall(
+ transportClient,
+ /* observer */ null,
+ /* monitor */ null,
+ restoreSet,
+ packageName,
+ token,
+ listener);
mBackupHandler.sendMessage(msg);
} catch (Exception e) {
// Calling into the transport broke; back off and proceed with the installation.
@@ -3277,8 +3251,14 @@
}
if (skip) {
- // Auto-restore disabled or no way to attempt a restore; just tell the Package
- // Manager to proceed with the post-install handling for this package.
+ // Auto-restore disabled or no way to attempt a restore
+
+ if (transportClient != null) {
+ mTransportManager.disposeOfTransportClient(
+ transportClient, "BMS.restoreAtInstall()");
+ }
+
+ // Tell the PackageManager to proceed with the post-install handling for this package.
if (DEBUG) Slog.v(TAG, "Finishing install immediately");
try {
mPackageManagerBinder.finishPackageInstall(token, false);
@@ -3477,14 +3457,16 @@
pw.println("Available transports:");
final String[] transports = listAllTransports();
if (transports != null) {
- for (String t : listAllTransports()) {
+ for (String t : transports) {
pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? " * "
: " ") + t);
try {
IBackupTransport transport = mTransportManager.getTransportBinder(t);
- File dir = new File(mBaseStateDir, transport.transportDirName());
+ File dir = new File(mBaseStateDir,
+ mTransportManager.getTransportDirName(t));
pw.println(" destination: " + transport.currentDestinationString());
- pw.println(" intent: " + transport.configurationIntent());
+ pw.println(" intent: "
+ + mTransportManager.getTransportConfigurationIntent(t));
for (File f : dir.listFiles()) {
pw.println(
" " + f.getName() + " - " + f.length() + " state bytes");
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index f8f1448..fbdb183 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -49,12 +49,14 @@
import com.android.server.backup.transport.TransportClient;
import com.android.server.backup.transport.TransportClientManager;
import com.android.server.backup.transport.TransportConnectionListener;
+import com.android.server.backup.transport.TransportNotRegisteredException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Consumer;
import java.util.function.Predicate;
/**
@@ -236,6 +238,72 @@
return getTransportBinder(mCurrentTransportName);
}
+ /**
+ * Retrieve the configuration intent of {@code transportName}.
+ * @throws TransportNotRegisteredException if the transport is not registered.
+ */
+ @Nullable
+ public Intent getTransportConfigurationIntent(String transportName)
+ throws TransportNotRegisteredException {
+ synchronized (mTransportLock) {
+ return getRegisteredTransportDescriptionOrThrowLocked(transportName)
+ .configurationIntent;
+ }
+ }
+
+ /**
+ * Retrieve the data management intent of {@code transportName}.
+ * @throws TransportNotRegisteredException if the transport is not registered.
+ */
+ @Nullable
+ public Intent getTransportDataManagementIntent(String transportName)
+ throws TransportNotRegisteredException {
+ synchronized (mTransportLock) {
+ return getRegisteredTransportDescriptionOrThrowLocked(transportName)
+ .dataManagementIntent;
+ }
+ }
+
+ /**
+ * Retrieve the data management label of {@code transportName}.
+ * @throws TransportNotRegisteredException if the transport is not registered.
+ */
+ @Nullable
+ public String getTransportDataManagementLabel(String transportName)
+ throws TransportNotRegisteredException {
+ synchronized (mTransportLock) {
+ return getRegisteredTransportDescriptionOrThrowLocked(transportName)
+ .dataManagementLabel;
+ }
+ }
+
+ /**
+ * Retrieve the transport dir name of {@code transportName}.
+ * @throws TransportNotRegisteredException if the transport is not registered.
+ */
+ public String getTransportDirName(String transportName)
+ throws TransportNotRegisteredException {
+ synchronized (mTransportLock) {
+ return getRegisteredTransportDescriptionOrThrowLocked(transportName)
+ .transportDirName;
+ }
+ }
+
+ /**
+ * Execute {@code transportConsumer} for each registered transport passing the transport name.
+ * This is called with an internal lock held, ensuring that the transport will remain registered
+ * while {@code transportConsumer} is being executed. Don't do heavy operations in
+ * {@code transportConsumer}.
+ */
+ public void forEachRegisteredTransport(Consumer<String> transportConsumer) {
+ synchronized (mTransportLock) {
+ for (TransportDescription transportDescription
+ : mRegisteredTransportsDescriptionMap.values()) {
+ transportConsumer.accept(transportDescription.name);
+ }
+ }
+ }
+
public String getTransportName(IBackupTransport binder) {
synchronized (mTransportLock) {
for (TransportConnection conn : mValidTransports.values()) {
@@ -281,6 +349,17 @@
}
@GuardedBy("mTransportLock")
+ private TransportDescription getRegisteredTransportDescriptionOrThrowLocked(
+ String transportName) throws TransportNotRegisteredException {
+ TransportDescription description = getRegisteredTransportDescriptionLocked(transportName);
+ if (description == null) {
+ throw new TransportNotRegisteredException(transportName);
+ }
+ return description;
+ }
+
+
+ @GuardedBy("mTransportLock")
@Nullable
private Map.Entry<ComponentName, TransportDescription> getRegisteredTransportEntryLocked(
String transportName) {
@@ -308,6 +387,12 @@
}
}
+ public boolean isTransportRegistered(String transportName) {
+ synchronized (mTransportLock) {
+ return getRegisteredTransportEntryLocked(transportName) != null;
+ }
+ }
+
/**
* Returns a {@link TransportClient} for the current transport or null if not found.
*
@@ -379,13 +464,13 @@
* Updates given values for the transport already registered and identified with
* {@param transportComponent}. If the transport is not registered it will log and return.
*/
- public void describeTransport(
+ public void updateTransportAttributes(
ComponentName transportComponent,
String name,
@Nullable Intent configurationIntent,
String currentDestinationString,
@Nullable Intent dataManagementIntent,
- String dataManagementLabel) {
+ @Nullable String dataManagementLabel) {
synchronized (mTransportLock) {
TransportDescription description =
mRegisteredTransportsDescriptionMap.get(transportComponent);
@@ -398,6 +483,7 @@
description.currentDestinationString = currentDestinationString;
description.dataManagementIntent = dataManagementIntent;
description.dataManagementLabel = dataManagementLabel;
+ Slog.d(TAG, "Transport " + name + " updated its attributes");
}
}
@@ -760,7 +846,7 @@
@Nullable private Intent configurationIntent;
private String currentDestinationString;
@Nullable private Intent dataManagementIntent;
- private String dataManagementLabel;
+ @Nullable private String dataManagementLabel;
private TransportDescription(
String name,
@@ -768,7 +854,7 @@
@Nullable Intent configurationIntent,
String currentDestinationString,
@Nullable Intent dataManagementIntent,
- String dataManagementLabel) {
+ @Nullable String dataManagementLabel) {
this.name = name;
this.transportDirName = transportDirName;
this.configurationIntent = configurationIntent;
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 477724d..f29a9c2 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -189,7 +189,7 @@
}
task.execute();
} catch (ClassCastException e) {
- Slog.e(TAG, "Invalid backup task in flight, obj=" + msg.obj);
+ Slog.e(TAG, "Invalid backup/restore task in flight, obj=" + msg.obj);
}
break;
}
@@ -229,10 +229,18 @@
RestoreParams params = (RestoreParams) msg.obj;
Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
- PerformUnifiedRestoreTask task = new PerformUnifiedRestoreTask(backupManagerService,
- params.transport,
- params.observer, params.monitor, params.token, params.pkgInfo,
- params.pmToken, params.isSystemRestore, params.filterSet);
+ PerformUnifiedRestoreTask task =
+ new PerformUnifiedRestoreTask(
+ backupManagerService,
+ params.transportClient,
+ params.observer,
+ params.monitor,
+ params.token,
+ params.packageInfo,
+ params.pmToken,
+ params.isSystemRestore,
+ params.filterSet,
+ params.listener);
synchronized (backupManagerService.getPendingRestores()) {
if (backupManagerService.isRestoreInProgress()) {
@@ -285,22 +293,15 @@
break;
}
- case MSG_RETRY_INIT: {
- synchronized (backupManagerService.getQueueLock()) {
- backupManagerService.recordInitPendingLocked(msg.arg1 != 0, (String) msg.obj);
- backupManagerService.getAlarmManager().set(AlarmManager.RTC_WAKEUP,
- System.currentTimeMillis(),
- backupManagerService.getRunInitIntent());
- }
- break;
- }
-
case MSG_RUN_GET_RESTORE_SETS: {
// Like other async operations, this is entered with the wakelock held
RestoreSet[] sets = null;
RestoreGetSetsParams params = (RestoreGetSetsParams) msg.obj;
+ String callerLogString = "BH/MSG_RUN_GET_RESTORE_SETS";
try {
- sets = params.transport.getAvailableRestoreSets();
+ IBackupTransport transport =
+ params.transportClient.connectOrThrow(callerLogString);
+ sets = transport.getAvailableRestoreSets();
// cache the result in the active session
synchronized (params.session) {
params.session.mRestoreSets = sets;
@@ -325,7 +326,7 @@
removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
- backupManagerService.getWakelock().release();
+ params.listener.onFinished(callerLogString);
}
break;
}
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index e65eb28..a002334 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -907,7 +907,7 @@
mStatus = BackupTransport.TRANSPORT_OK;
long size = 0;
try {
- TransportUtils.checkTransport(transport);
+ TransportUtils.checkTransportNotNull(transport);
size = mBackupDataName.length();
if (size > 0) {
if (mStatus == BackupTransport.TRANSPORT_OK) {
@@ -997,7 +997,7 @@
}
if (mAgentBinder != null) {
try {
- TransportUtils.checkTransport(transport);
+ TransportUtils.checkTransportNotNull(transport);
long quota = transport.getBackupQuota(mCurrentPackage.packageName, false);
mAgentBinder.doQuotaExceeded(size, quota);
} catch (Exception e) {
diff --git a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
index 690922f..b21b072 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
@@ -98,7 +98,8 @@
transportDirName));
EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
synchronized (backupManagerService.getQueueLock()) {
- backupManagerService.recordInitPendingLocked(false, transportName);
+ backupManagerService.recordInitPendingLocked(
+ false, transportName, transportDirName);
}
notifyResult(transportName, BackupTransport.TRANSPORT_OK);
} else {
@@ -107,7 +108,8 @@
Slog.e(TAG, "Transport error in initializeDevice()");
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
synchronized (backupManagerService.getQueueLock()) {
- backupManagerService.recordInitPendingLocked(true, transportName);
+ backupManagerService.recordInitPendingLocked(
+ true, transportName, transportDirName);
}
notifyResult(transportName, status);
result = status;
diff --git a/services/backup/java/com/android/server/backup/params/RestoreGetSetsParams.java b/services/backup/java/com/android/server/backup/params/RestoreGetSetsParams.java
index bff476b..914e9ea 100644
--- a/services/backup/java/com/android/server/backup/params/RestoreGetSetsParams.java
+++ b/services/backup/java/com/android/server/backup/params/RestoreGetSetsParams.java
@@ -20,20 +20,24 @@
import android.app.backup.IRestoreObserver;
import com.android.internal.backup.IBackupTransport;
+import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.restore.ActiveRestoreSession;
+import com.android.server.backup.transport.TransportClient;
public class RestoreGetSetsParams {
+ public final TransportClient transportClient;
+ public final ActiveRestoreSession session;
+ public final IRestoreObserver observer;
+ public final IBackupManagerMonitor monitor;
+ public final OnTaskFinishedListener listener;
- public IBackupTransport transport;
- public ActiveRestoreSession session;
- public IRestoreObserver observer;
- public IBackupManagerMonitor monitor;
-
- public RestoreGetSetsParams(IBackupTransport _transport, ActiveRestoreSession _session,
- IRestoreObserver _observer, IBackupManagerMonitor _monitor) {
- transport = _transport;
+ public RestoreGetSetsParams(TransportClient _transportClient, ActiveRestoreSession _session,
+ IRestoreObserver _observer, IBackupManagerMonitor _monitor,
+ OnTaskFinishedListener _listener) {
+ transportClient = _transportClient;
session = _session;
observer = _observer;
monitor = _monitor;
+ listener = _listener;
}
}
diff --git a/services/backup/java/com/android/server/backup/params/RestoreParams.java b/services/backup/java/com/android/server/backup/params/RestoreParams.java
index 93ce00d..e500d6e 100644
--- a/services/backup/java/com/android/server/backup/params/RestoreParams.java
+++ b/services/backup/java/com/android/server/backup/params/RestoreParams.java
@@ -20,84 +20,128 @@
import android.app.backup.IRestoreObserver;
import android.content.pm.PackageInfo;
-import com.android.internal.backup.IBackupTransport;
+import com.android.server.backup.internal.OnTaskFinishedListener;
+import com.android.server.backup.transport.TransportClient;
public class RestoreParams {
-
- public IBackupTransport transport;
- public String dirName;
- public IRestoreObserver observer;
- public IBackupManagerMonitor monitor;
- public long token;
- public PackageInfo pkgInfo;
- public int pmToken; // in post-install restore, the PM's token for this transaction
- public boolean isSystemRestore;
- public String[] filterSet;
+ public final TransportClient transportClient;
+ public final IRestoreObserver observer;
+ public final IBackupManagerMonitor monitor;
+ public final long token;
+ public final PackageInfo packageInfo;
+ public final int pmToken; // in post-install restore, the PM's token for this transaction
+ public final boolean isSystemRestore;
+ public final String[] filterSet;
+ public final OnTaskFinishedListener listener;
/**
- * Restore a single package; no kill after restore
+ * No kill after restore.
*/
- public RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
- IBackupManagerMonitor _monitor, long _token, PackageInfo _pkg) {
- transport = _transport;
- dirName = _dirName;
- observer = _obs;
- monitor = _monitor;
- token = _token;
- pkgInfo = _pkg;
- pmToken = 0;
- isSystemRestore = false;
- filterSet = null;
+ public static RestoreParams createForSinglePackage(
+ TransportClient transportClient,
+ IRestoreObserver observer,
+ IBackupManagerMonitor monitor,
+ long token,
+ PackageInfo packageInfo,
+ OnTaskFinishedListener listener) {
+ return new RestoreParams(
+ transportClient,
+ observer,
+ monitor,
+ token,
+ packageInfo,
+ /* pmToken */ 0,
+ /* isSystemRestore */ false,
+ /* filterSet */ null,
+ listener);
}
/**
- * Restore at install: PM token needed, kill after restore
+ * Kill after restore.
*/
- public RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
- IBackupManagerMonitor _monitor, long _token, String _pkgName, int _pmToken) {
- transport = _transport;
- dirName = _dirName;
- observer = _obs;
- monitor = _monitor;
- token = _token;
- pkgInfo = null;
- pmToken = _pmToken;
- isSystemRestore = false;
- filterSet = new String[]{_pkgName};
+ public static RestoreParams createForRestoreAtInstall(
+ TransportClient transportClient,
+ IRestoreObserver observer,
+ IBackupManagerMonitor monitor,
+ long token,
+ String packageName,
+ int pmToken,
+ OnTaskFinishedListener listener) {
+ String[] filterSet = {packageName};
+ return new RestoreParams(
+ transportClient,
+ observer,
+ monitor,
+ token,
+ /* packageInfo */ null,
+ pmToken,
+ /* isSystemRestore */ false,
+ filterSet,
+ listener);
}
/**
- * Restore everything possible. This is the form that Setup Wizard or similar
- * restore UXes use.
+ * This is the form that Setup Wizard or similar restore UXes use.
*/
- public RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
- IBackupManagerMonitor _monitor, long _token) {
- transport = _transport;
- dirName = _dirName;
- observer = _obs;
- monitor = _monitor;
- token = _token;
- pkgInfo = null;
- pmToken = 0;
- isSystemRestore = true;
- filterSet = null;
+ public static RestoreParams createForRestoreAll(
+ TransportClient transportClient,
+ IRestoreObserver observer,
+ IBackupManagerMonitor monitor,
+ long token,
+ OnTaskFinishedListener listener) {
+ return new RestoreParams(
+ transportClient,
+ observer,
+ monitor,
+ token,
+ /* packageInfo */ null,
+ /* pmToken */ 0,
+ /* isSystemRestore */ true,
+ /* filterSet */ null,
+ listener);
}
/**
- * Restore some set of packages. Leave this one up to the caller to specify
- * whether it's to be considered a system-level restore.
+ * Caller specifies whether is considered a system-level restore.
*/
- public RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
- IBackupManagerMonitor _monitor, long _token,
- String[] _filterSet, boolean _isSystemRestore) {
- transport = _transport;
- dirName = _dirName;
- observer = _obs;
- monitor = _monitor;
- token = _token;
- pkgInfo = null;
- pmToken = 0;
- isSystemRestore = _isSystemRestore;
- filterSet = _filterSet;
+ public static RestoreParams createForRestoreSome(
+ TransportClient transportClient,
+ IRestoreObserver observer,
+ IBackupManagerMonitor monitor,
+ long token,
+ String[] filterSet,
+ boolean isSystemRestore,
+ OnTaskFinishedListener listener) {
+ return new RestoreParams(
+ transportClient,
+ observer,
+ monitor,
+ token,
+ /* packageInfo */ null,
+ /* pmToken */ 0,
+ isSystemRestore,
+ filterSet,
+ listener);
+ }
+
+ private RestoreParams(
+ TransportClient transportClient,
+ IRestoreObserver observer,
+ IBackupManagerMonitor monitor,
+ long token,
+ PackageInfo packageInfo,
+ int pmToken,
+ boolean isSystemRestore,
+ String[] filterSet,
+ OnTaskFinishedListener listener) {
+ this.transportClient = transportClient;
+ this.observer = observer;
+ this.monitor = monitor;
+ this.token = token;
+ this.packageInfo = packageInfo;
+ this.pmToken = pmToken;
+ this.isSystemRestore = isSystemRestore;
+ this.filterSet = filterSet;
+ this.listener = listener;
}
}
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index a08c19e..7ae5b43 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -31,33 +31,39 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Binder;
import android.os.Message;
+import android.os.PowerManager;
import android.util.Slog;
-import com.android.internal.backup.IBackupTransport;
import com.android.server.backup.RefactoredBackupManagerService;
+import com.android.server.backup.TransportManager;
+import com.android.server.backup.internal.BackupHandler;
+import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.params.RestoreGetSetsParams;
import com.android.server.backup.params.RestoreParams;
+import com.android.server.backup.transport.TransportClient;
+
+import java.util.function.BiFunction;
/**
* Restore session.
*/
public class ActiveRestoreSession extends IRestoreSession.Stub {
-
private static final String TAG = "RestoreSession";
- private RefactoredBackupManagerService backupManagerService;
- private String mPackageName;
- private IBackupTransport mRestoreTransport = null;
+ private final TransportManager mTransportManager;
+ private final String mTransportName;
+ private final RefactoredBackupManagerService mBackupManagerService;
+ private final String mPackageName;
public RestoreSet[] mRestoreSets = null;
boolean mEnded = false;
boolean mTimedOut = false;
public ActiveRestoreSession(RefactoredBackupManagerService backupManagerService,
- String packageName, String transport) {
- this.backupManagerService = backupManagerService;
+ String packageName, String transportName) {
+ mBackupManagerService = backupManagerService;
mPackageName = packageName;
- mRestoreTransport = backupManagerService.getTransportManager().getTransportBinder(
- transport);
+ mTransportManager = backupManagerService.getTransportManager();
+ mTransportName = transportName;
}
public void markTimedOut() {
@@ -67,7 +73,7 @@
// --- Binder interface ---
public synchronized int getAvailableRestoreSets(IRestoreObserver observer,
IBackupManagerMonitor monitor) {
- backupManagerService.getContext().enforceCallingOrSelfPermission(
+ mBackupManagerService.getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP,
"getAvailableRestoreSets");
if (observer == null) {
@@ -85,23 +91,32 @@
long oldId = Binder.clearCallingIdentity();
try {
- if (mRestoreTransport == null) {
- Slog.w(TAG, "Null transport getting restore sets");
+ TransportClient transportClient =
+ mTransportManager.getTransportClient(
+ mTransportName, "RestoreSession.getAvailableRestoreSets()");
+ if (transportClient == null) {
+ Slog.w(TAG, "Null transport client getting restore sets");
return -1;
}
// We know we're doing legit work now, so halt the timeout
// until we're done. It gets started again when the result
// comes in.
- backupManagerService.getBackupHandler().removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
+ mBackupManagerService.getBackupHandler().removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
- // spin off the transport request to our service thread
- backupManagerService.getWakelock().acquire();
- Message msg = backupManagerService.getBackupHandler().obtainMessage(
+ PowerManager.WakeLock wakelock = mBackupManagerService.getWakelock();
+ wakelock.acquire();
+
+ // Prevent lambda from leaking 'this'
+ TransportManager transportManager = mTransportManager;
+ OnTaskFinishedListener listener = caller -> {
+ transportManager.disposeOfTransportClient(transportClient, caller);
+ wakelock.release();
+ };
+ Message msg = mBackupManagerService.getBackupHandler().obtainMessage(
MSG_RUN_GET_RESTORE_SETS,
- new RestoreGetSetsParams(mRestoreTransport, this, observer,
- monitor));
- backupManagerService.getBackupHandler().sendMessage(msg);
+ new RestoreGetSetsParams(transportClient, this, observer, monitor, listener));
+ mBackupManagerService.getBackupHandler().sendMessage(msg);
return 0;
} catch (Exception e) {
Slog.e(TAG, "Error in getAvailableRestoreSets", e);
@@ -113,7 +128,7 @@
public synchronized int restoreAll(long token, IRestoreObserver observer,
IBackupManagerMonitor monitor) {
- backupManagerService.getContext().enforceCallingOrSelfPermission(
+ mBackupManagerService.getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP,
"performRestore");
@@ -131,7 +146,7 @@
return -1;
}
- if (mRestoreTransport == null || mRestoreSets == null) {
+ if (mRestoreSets == null) {
Slog.e(TAG, "Ignoring restoreAll() with no restore set");
return -1;
}
@@ -141,37 +156,28 @@
return -1;
}
- String dirName;
- try {
- dirName = mRestoreTransport.transportDirName();
- } catch (Exception e) {
- // Transport went AWOL; fail.
- Slog.e(TAG, "Unable to get transport dir for restore: " + e.getMessage());
+ if (!mTransportManager.isTransportRegistered(mTransportName)) {
+ Slog.e(TAG, "Transport " + mTransportName + " not registered");
return -1;
}
- synchronized (backupManagerService.getQueueLock()) {
+ synchronized (mBackupManagerService.getQueueLock()) {
for (int i = 0; i < mRestoreSets.length; i++) {
if (token == mRestoreSets[i].token) {
- // Real work, so stop the session timeout until we finalize the restore
- backupManagerService.getBackupHandler().removeMessages(
- MSG_RESTORE_SESSION_TIMEOUT);
-
long oldId = Binder.clearCallingIdentity();
try {
- backupManagerService.getWakelock().acquire();
- if (MORE_DEBUG) {
- Slog.d(TAG, "restoreAll() kicking off");
- }
- Message msg = backupManagerService.getBackupHandler().obtainMessage(
- MSG_RUN_RESTORE);
- msg.obj = new RestoreParams(mRestoreTransport, dirName,
- observer, monitor, token);
- backupManagerService.getBackupHandler().sendMessage(msg);
+ return sendRestoreToHandlerLocked(
+ (transportClient, listener) ->
+ RestoreParams.createForRestoreAll(
+ transportClient,
+ observer,
+ monitor,
+ token,
+ listener),
+ "RestoreSession.restoreAll()");
} finally {
Binder.restoreCallingIdentity(oldId);
}
- return 0;
}
}
}
@@ -183,7 +189,7 @@
// Restores of more than a single package are treated as 'system' restores
public synchronized int restoreSome(long token, IRestoreObserver observer,
IBackupManagerMonitor monitor, String[] packages) {
- backupManagerService.getContext().enforceCallingOrSelfPermission(
+ mBackupManagerService.getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.BACKUP,
"performRestore");
@@ -227,7 +233,7 @@
return -1;
}
- if (mRestoreTransport == null || mRestoreSets == null) {
+ if (mRestoreSets == null) {
Slog.e(TAG, "Ignoring restoreAll() with no restore set");
return -1;
}
@@ -237,34 +243,30 @@
return -1;
}
- String dirName;
- try {
- dirName = mRestoreTransport.transportDirName();
- } catch (Exception e) {
- // Transport went AWOL; fail.
- Slog.e(TAG, "Unable to get transport name for restoreSome: " + e.getMessage());
+ if (!mTransportManager.isTransportRegistered(mTransportName)) {
+ Slog.e(TAG, "Transport " + mTransportName + " not registered");
return -1;
}
- synchronized (backupManagerService.getQueueLock()) {
+ synchronized (mBackupManagerService.getQueueLock()) {
for (int i = 0; i < mRestoreSets.length; i++) {
if (token == mRestoreSets[i].token) {
- // Stop the session timeout until we finalize the restore
- backupManagerService.getBackupHandler().removeMessages(
- MSG_RESTORE_SESSION_TIMEOUT);
-
long oldId = Binder.clearCallingIdentity();
- backupManagerService.getWakelock().acquire();
- if (MORE_DEBUG) {
- Slog.d(TAG, "restoreSome() of " + packages.length + " packages");
+ try {
+ return sendRestoreToHandlerLocked(
+ (transportClient, listener) ->
+ RestoreParams.createForRestoreSome(
+ transportClient,
+ observer,
+ monitor,
+ token,
+ packages,
+ /* isSystemRestore */ packages.length > 1,
+ listener),
+ "RestoreSession.restoreSome(" + packages.length + " packages)");
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
}
- Message msg = backupManagerService.getBackupHandler().obtainMessage(
- MSG_RUN_RESTORE);
- msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, monitor,
- token, packages, packages.length > 1);
- backupManagerService.getBackupHandler().sendMessage(msg);
- Binder.restoreCallingIdentity(oldId);
- return 0;
}
}
}
@@ -297,9 +299,9 @@
}
}
- PackageInfo app = null;
+ final PackageInfo app;
try {
- app = backupManagerService.getPackageManager().getPackageInfo(packageName, 0);
+ app = mBackupManagerService.getPackageManager().getPackageInfo(packageName, 0);
} catch (NameNotFoundException nnf) {
Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
return -1;
@@ -307,7 +309,7 @@
// If the caller is not privileged and is not coming from the target
// app's uid, throw a permission exception back to the caller.
- int perm = backupManagerService.getContext().checkPermission(
+ int perm = mBackupManagerService.getContext().checkPermission(
android.Manifest.permission.BACKUP,
Binder.getCallingPid(), Binder.getCallingUid());
if ((perm == PackageManager.PERMISSION_DENIED) &&
@@ -317,12 +319,17 @@
throw new SecurityException("No permission to restore other packages");
}
+ if (!mTransportManager.isTransportRegistered(mTransportName)) {
+ Slog.e(TAG, "Transport " + mTransportName + " not registered");
+ return -1;
+ }
+
// So far so good; we're allowed to try to restore this package.
long oldId = Binder.clearCallingIdentity();
try {
// Check whether there is data for it in the current dataset, falling back
// to the ancestral dataset if not.
- long token = backupManagerService.getAvailableRestoreToken(packageName);
+ long token = mBackupManagerService.getAvailableRestoreToken(packageName);
if (DEBUG) {
Slog.v(TAG, "restorePackage pkg=" + packageName
+ " token=" + Long.toHexString(token));
@@ -338,30 +345,53 @@
return -1;
}
- String dirName;
- try {
- dirName = mRestoreTransport.transportDirName();
- } catch (Exception e) {
- // Transport went AWOL; fail.
- Slog.e(TAG, "Unable to get transport dir for restorePackage: " + e.getMessage());
- return -1;
- }
-
- // Stop the session timeout until we finalize the restore
- backupManagerService.getBackupHandler().removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
-
- // Ready to go: enqueue the restore request and claim success
- backupManagerService.getWakelock().acquire();
- if (MORE_DEBUG) {
- Slog.d(TAG, "restorePackage() : " + packageName);
- }
- Message msg = backupManagerService.getBackupHandler().obtainMessage(MSG_RUN_RESTORE);
- msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, monitor,
- token, app);
- backupManagerService.getBackupHandler().sendMessage(msg);
+ return sendRestoreToHandlerLocked(
+ (transportClient, listener) ->
+ RestoreParams.createForSinglePackage(
+ transportClient,
+ observer,
+ monitor,
+ token,
+ app,
+ listener),
+ "RestoreSession.restorePackage(" + packageName + ")");
} finally {
Binder.restoreCallingIdentity(oldId);
}
+ }
+
+ /**
+ * Returns 0 if operation sent or -1 otherwise.
+ */
+ private int sendRestoreToHandlerLocked(
+ BiFunction<TransportClient, OnTaskFinishedListener, RestoreParams> restoreParamsBuilder,
+ String callerLogString) {
+ TransportClient transportClient =
+ mTransportManager.getTransportClient(mTransportName, callerLogString);
+ if (transportClient == null) {
+ Slog.e(TAG, "Transport " + mTransportName + " got unregistered");
+ return -1;
+ }
+
+ // Stop the session timeout until we finalize the restore
+ BackupHandler backupHandler = mBackupManagerService.getBackupHandler();
+ backupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
+
+ PowerManager.WakeLock wakelock = mBackupManagerService.getWakelock();
+ wakelock.acquire();
+ if (MORE_DEBUG) {
+ Slog.d(TAG, callerLogString);
+ }
+
+ // Prevent lambda from leaking 'this'
+ TransportManager transportManager = mTransportManager;
+ OnTaskFinishedListener listener = caller -> {
+ transportManager.disposeOfTransportClient(transportClient, caller);
+ wakelock.release();
+ };
+ Message msg = backupHandler.obtainMessage(MSG_RUN_RESTORE);
+ msg.obj = restoreParamsBuilder.apply(transportClient, listener);
+ backupHandler.sendMessage(msg);
return 0;
}
@@ -380,7 +410,6 @@
public void run() {
// clean up the session's bookkeeping
synchronized (mSession) {
- mSession.mRestoreTransport = null;
mSession.mEnded = true;
}
@@ -404,7 +433,7 @@
throw new IllegalStateException("Restore session already ended");
}
- backupManagerService.getBackupHandler().post(
- new EndRestoreRunnable(backupManagerService, this));
+ mBackupManagerService.getBackupHandler().post(
+ new EndRestoreRunnable(mBackupManagerService, this));
}
}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 888dcd7e..7efe5ca 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -327,7 +327,7 @@
Slog.d(TAG,
"Clearing app data preparatory to full restore");
}
- mBackupManagerService.clearApplicationDataSynchronous(pkg);
+ mBackupManagerService.clearApplicationDataSynchronous(pkg, true);
} else {
if (MORE_DEBUG) {
Slog.d(TAG, "backup agent ("
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index 22691bb..3dc242f 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -591,7 +591,7 @@
Slog.d(TAG,
"Clearing app data preparatory to full restore");
}
- mBackupManagerService.clearApplicationDataSynchronous(pkg);
+ mBackupManagerService.clearApplicationDataSynchronous(pkg, true);
} else {
if (DEBUG) {
Slog.d(TAG, "backup agent ("
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 5884dc5..86866dc 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -61,6 +61,8 @@
import com.android.server.backup.PackageManagerBackupAgent;
import com.android.server.backup.PackageManagerBackupAgent.Metadata;
import com.android.server.backup.RefactoredBackupManagerService;
+import com.android.server.backup.internal.OnTaskFinishedListener;
+import com.android.server.backup.transport.TransportClient;
import com.android.server.backup.utils.AppBackupUtils;
import com.android.server.backup.utils.BackupManagerMonitorUtils;
@@ -76,8 +78,8 @@
public class PerformUnifiedRestoreTask implements BackupRestoreTask {
private RefactoredBackupManagerService backupManagerService;
- // Transport we're working with to do the restore
- private IBackupTransport mTransport;
+ // Transport client we're working with to do the restore
+ private final TransportClient mTransportClient;
// Where per-transport saved state goes
File mStateDir;
@@ -141,6 +143,9 @@
// Done?
private boolean mFinished;
+ // When finished call listener
+ private final OnTaskFinishedListener mListener;
+
// Key/value: bookkeeping about staged data and files for agent access
private File mBackupDataName;
private File mStageName;
@@ -154,15 +159,16 @@
// Invariant: mWakelock is already held, and this task is responsible for
// releasing it at the end of the restore operation.
public PerformUnifiedRestoreTask(RefactoredBackupManagerService backupManagerService,
- IBackupTransport transport, IRestoreObserver observer,
+ TransportClient transportClient, IRestoreObserver observer,
IBackupManagerMonitor monitor, long restoreSetToken, PackageInfo targetPackage,
- int pmToken, boolean isFullSystemRestore, String[] filterSet) {
+ int pmToken, boolean isFullSystemRestore, String[] filterSet,
+ OnTaskFinishedListener listener) {
this.backupManagerService = backupManagerService;
mEphemeralOpToken = backupManagerService.generateRandomIntegerToken();
mState = UnifiedRestoreState.INITIAL;
mStartRealtime = SystemClock.elapsedRealtime();
- mTransport = transport;
+ mTransportClient = transportClient;
mObserver = observer;
mMonitor = monitor;
mToken = restoreSetToken;
@@ -171,6 +177,7 @@
mIsSystemRestore = isFullSystemRestore;
mFinished = false;
mDidLaunch = false;
+ mListener = listener;
if (targetPackage != null) {
// Single package restore
@@ -342,7 +349,7 @@
}
try {
- String transportDir = mTransport.transportDirName();
+ String transportDir = mTransportClient.getTransportDirName();
mStateDir = new File(backupManagerService.getBaseStateDir(), transportDir);
// Fetch the current metadata from the dataset first
@@ -351,7 +358,11 @@
mAcceptSet.add(0, pmPackage);
PackageInfo[] packages = mAcceptSet.toArray(new PackageInfo[0]);
- mStatus = mTransport.startRestore(mToken, packages);
+
+ IBackupTransport transport =
+ mTransportClient.connectOrThrow("PerformUnifiedRestoreTask.startRestore()");
+
+ mStatus = transport.startRestore(mToken, packages);
if (mStatus != BackupTransport.TRANSPORT_OK) {
Slog.e(TAG, "Transport error " + mStatus + "; no restore possible");
mStatus = BackupTransport.TRANSPORT_ERROR;
@@ -359,7 +370,7 @@
return;
}
- RestoreDescription desc = mTransport.nextRestorePackage();
+ RestoreDescription desc = transport.nextRestorePackage();
if (desc == null) {
Slog.e(TAG, "No restore metadata available; halting");
mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
@@ -444,7 +455,10 @@
private void dispatchNextRestore() {
UnifiedRestoreState nextState = UnifiedRestoreState.FINAL;
try {
- mRestoreDescription = mTransport.nextRestorePackage();
+ IBackupTransport transport =
+ mTransportClient.connectOrThrow(
+ "PerformUnifiedRestoreTask.dispatchNextRestore()");
+ mRestoreDescription = transport.nextRestorePackage();
final String pkgName = (mRestoreDescription != null)
? mRestoreDescription.getPackageName() : null;
if (pkgName == null) {
@@ -657,13 +671,17 @@
File downloadFile = (staging) ? mStageName : mBackupDataName;
try {
+ IBackupTransport transport =
+ mTransportClient.connectOrThrow(
+ "PerformUnifiedRestoreTask.initiateOneRestore()");
+
// Run the transport's restore pass
stage = ParcelFileDescriptor.open(downloadFile,
ParcelFileDescriptor.MODE_READ_WRITE |
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE);
- if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
+ if (transport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
// Transport-level failure, so we wind everything up and
// terminate the restore operation.
Slog.e(TAG, "Error getting restore data for " + packageName);
@@ -750,7 +768,7 @@
// None of this can run on the work looper here, so we spin asynchronous
// work like this:
//
- // StreamFeederThread: read data from mTransport.getNextFullRestoreDataChunk()
+ // StreamFeederThread: read data from transport.getNextFullRestoreDataChunk()
// write it into the pipe to the engine
// EngineThread: FullRestoreEngine thread communicating with the target app
//
@@ -844,10 +862,12 @@
// spin up the engine and start moving data to it
new Thread(mEngineThread, "unified-restore-engine").start();
+ String callerLogString = "PerformUnifiedRestoreTask$StreamFeederThread.run()";
try {
+ IBackupTransport transport = mTransportClient.connectOrThrow(callerLogString);
while (status == BackupTransport.TRANSPORT_OK) {
// have the transport write some of the restoring data to us
- int result = mTransport.getNextFullRestoreDataChunk(tWriteEnd);
+ int result = transport.getNextFullRestoreDataChunk(tWriteEnd);
if (result > 0) {
// The transport wrote this many bytes of restore data to the
// pipe, so pass it along to the engine.
@@ -936,7 +956,9 @@
// Something went wrong somewhere. Whether it was at the transport
// level is immaterial; we need to tell the transport to bail
try {
- mTransport.abortFullRestore();
+ IBackupTransport transport =
+ mTransportClient.connectOrThrow(callerLogString);
+ transport.abortFullRestore();
} catch (Exception e) {
// transport itself is dead; make sure we handle this as a
// fatal error
@@ -947,7 +969,7 @@
// We also need to wipe the current target's data, as it's probably
// in an incoherent state.
backupManagerService.clearApplicationDataSynchronous(
- mCurrentPackage.packageName);
+ mCurrentPackage.packageName, false);
// Schedule the next state based on the nature of our failure
if (status == BackupTransport.TRANSPORT_ERROR) {
@@ -1039,8 +1061,11 @@
Slog.d(TAG, "finishing restore mObserver=" + mObserver);
}
+ String callerLogString = "PerformUnifiedRestoreTask.finalizeRestore()";
try {
- mTransport.finishRestore();
+ IBackupTransport transport =
+ mTransportClient.connectOrThrow(callerLogString);
+ transport.finishRestore();
} catch (Exception e) {
Slog.e(TAG, "Error finishing restore", e);
}
@@ -1087,9 +1112,6 @@
backupManagerService.writeRestoreTokens();
}
- // done; we can finally release the wakelock and be legitimately done.
- Slog.i(TAG, "Restore complete.");
-
synchronized (backupManagerService.getPendingRestores()) {
if (backupManagerService.getPendingRestores().size() > 0) {
if (DEBUG) {
@@ -1108,14 +1130,15 @@
}
}
- backupManagerService.getWakelock().release();
+ Slog.i(TAG, "Restore complete.");
+ mListener.onFinished(callerLogString);
}
void keyValueAgentErrorCleanup() {
// If the agent fails restore, it might have put the app's data
// into an incoherent state. For consistency we wipe its data
// again in this case before continuing with normal teardown
- backupManagerService.clearApplicationDataSynchronous(mCurrentPackage.packageName);
+ backupManagerService.clearApplicationDataSynchronous(mCurrentPackage.packageName, false);
keyValueAgentCleanup();
}
@@ -1301,13 +1324,11 @@
void sendOnRestorePackage(String name) {
if (mObserver != null) {
- if (mObserver != null) {
- try {
- mObserver.onUpdate(mCount, name);
- } catch (RemoteException e) {
- Slog.d(TAG, "Restore observer died in onUpdate");
- mObserver = null;
- }
+ try {
+ mObserver.onUpdate(mCount, name);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Restore observer died in onUpdate");
+ mObserver = null;
}
}
}
diff --git a/services/backup/java/com/android/server/backup/restore/RestoreEngine.java b/services/backup/java/com/android/server/backup/restore/RestoreEngine.java
index b2fdbb8..9d3ae86 100644
--- a/services/backup/java/com/android/server/backup/restore/RestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/RestoreEngine.java
@@ -30,8 +30,8 @@
public static final int TARGET_FAILURE = -2;
public static final int TRANSPORT_FAILURE = -3;
- private AtomicBoolean mRunning = new AtomicBoolean(false);
- private AtomicInteger mResult = new AtomicInteger(SUCCESS);
+ private final AtomicBoolean mRunning = new AtomicBoolean(false);
+ private final AtomicInteger mResult = new AtomicInteger(SUCCESS);
public boolean isRunning() {
return mRunning.get();
diff --git a/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java b/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java
new file mode 100644
index 0000000..26bf92c
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java
@@ -0,0 +1,35 @@
+/*
+ * 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.backup.transport;
+
+import android.util.AndroidException;
+
+import com.android.server.backup.TransportManager;
+
+/**
+ * Exception thrown when the transport is not registered.
+ *
+ * @see TransportManager#getTransportDirName(String)
+ * @see TransportManager#getTransportConfigurationIntent(String)
+ * @see TransportManager#getTransportDataManagementIntent(String)
+ * @see TransportManager#getTransportDataManagementLabel(String)
+ */
+public class TransportNotRegisteredException extends AndroidException {
+ public TransportNotRegisteredException(String transportName) {
+ super("Transport " + transportName + " not registered");
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/transport/TransportUtils.java b/services/backup/java/com/android/server/backup/transport/TransportUtils.java
index 85599b7..92bba9b 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportUtils.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportUtils.java
@@ -31,7 +31,7 @@
* Throws {@link TransportNotAvailableException} if {@param transport} is null. The semantics is
* similar to a {@link DeadObjectException} coming from a dead transport binder.
*/
- public static IBackupTransport checkTransport(@Nullable IBackupTransport transport)
+ public static IBackupTransport checkTransportNotNull(@Nullable IBackupTransport transport)
throws TransportNotAvailableException {
if (transport == null) {
log(Log.ERROR, TAG, "Transport not available");
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 446c2a5..9fb2681 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -1,5 +1,5 @@
java_library_static {
- name: "services.core",
+ name: "services.core.unboosted",
aidl: {
include_dirs: [
@@ -31,6 +31,7 @@
static_libs: [
"time_zone_distro",
"time_zone_distro_installer",
+ "android.hardware.broadcastradio-V2.0-java",
"android.hardware.health-V1.0-java",
"android.hardware.health-V2.0-java",
"android.hardware.weaver-V1.0-java",
@@ -45,7 +46,7 @@
java_genrule {
name: "services.core.priorityboosted",
- srcs: [":services.core"],
+ srcs: [":services.core.unboosted"],
tools: ["lockedregioncodeinjection"],
cmd: "$(location lockedregioncodeinjection) " +
" --targets \"Lcom/android/server/am/ActivityManagerService;,Lcom/android/server/wm/WindowHashMap;\" " +
@@ -55,3 +56,8 @@
" -i $(in)",
out: ["services.core.priorityboosted.jar"],
}
+
+java_library {
+ name: "services.core",
+ static_libs: ["services.core.priorityboosted"],
+}
diff --git a/services/core/java/com/android/server/AlarmManagerInternal.java b/services/core/java/com/android/server/AlarmManagerInternal.java
new file mode 100644
index 0000000..dbff957
--- /dev/null
+++ b/services/core/java/com/android/server/AlarmManagerInternal.java
@@ -0,0 +1,21 @@
+/*
+ * 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;
+
+public interface AlarmManagerInternal {
+ void removeAlarmsForUid(int uid);
+}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index ca15249..06cf982 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -92,6 +92,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.LocalLog;
import com.android.server.ForceAppStandbyTracker.Listener;
+import com.android.server.LocalServices;
/**
* Alarm manager implementaion.
@@ -467,21 +468,14 @@
return newStart;
}
- boolean remove(final PendingIntent operation, final IAlarmListener listener) {
- if (operation == null && listener == null) {
- if (localLOGV) {
- Slog.w(TAG, "requested remove() of null operation",
- new RuntimeException("here"));
- }
- return false;
- }
+ boolean remove(Predicate<Alarm> predicate) {
boolean didRemove = false;
long newStart = 0; // recalculate endpoints as we go
long newEnd = Long.MAX_VALUE;
int newFlags = 0;
for (int i = 0; i < alarms.size(); ) {
Alarm alarm = alarms.get(i);
- if (alarm.matches(operation, listener)) {
+ if (predicate.test(alarm)) {
alarms.remove(i);
didRemove = true;
if (alarm.alarmClock != null) {
@@ -507,111 +501,6 @@
return didRemove;
}
- boolean remove(final String packageName) {
- if (packageName == null) {
- if (localLOGV) {
- Slog.w(TAG, "requested remove() of null packageName",
- new RuntimeException("here"));
- }
- return false;
- }
- boolean didRemove = false;
- long newStart = 0; // recalculate endpoints as we go
- long newEnd = Long.MAX_VALUE;
- int newFlags = 0;
- for (int i = alarms.size()-1; i >= 0; i--) {
- Alarm alarm = alarms.get(i);
- if (alarm.matches(packageName)) {
- alarms.remove(i);
- didRemove = true;
- if (alarm.alarmClock != null) {
- mNextAlarmClockMayChange = true;
- }
- } else {
- if (alarm.whenElapsed > newStart) {
- newStart = alarm.whenElapsed;
- }
- if (alarm.maxWhenElapsed < newEnd) {
- newEnd = alarm.maxWhenElapsed;
- }
- newFlags |= alarm.flags;
- }
- }
- if (didRemove) {
- // commit the new batch bounds
- start = newStart;
- end = newEnd;
- flags = newFlags;
- }
- return didRemove;
- }
-
- boolean removeForStopped(final int uid) {
- boolean didRemove = false;
- long newStart = 0; // recalculate endpoints as we go
- long newEnd = Long.MAX_VALUE;
- int newFlags = 0;
- for (int i = alarms.size()-1; i >= 0; i--) {
- Alarm alarm = alarms.get(i);
- try {
- if (alarm.uid == uid && ActivityManager.getService().isAppStartModeDisabled(
- uid, alarm.packageName)) {
- alarms.remove(i);
- didRemove = true;
- if (alarm.alarmClock != null) {
- mNextAlarmClockMayChange = true;
- }
- } else {
- if (alarm.whenElapsed > newStart) {
- newStart = alarm.whenElapsed;
- }
- if (alarm.maxWhenElapsed < newEnd) {
- newEnd = alarm.maxWhenElapsed;
- }
- newFlags |= alarm.flags;
- }
- } catch (RemoteException e) {
- }
- }
- if (didRemove) {
- // commit the new batch bounds
- start = newStart;
- end = newEnd;
- flags = newFlags;
- }
- return didRemove;
- }
-
- boolean remove(final int userHandle) {
- boolean didRemove = false;
- long newStart = 0; // recalculate endpoints as we go
- long newEnd = Long.MAX_VALUE;
- for (int i = 0; i < alarms.size(); ) {
- Alarm alarm = alarms.get(i);
- if (UserHandle.getUserId(alarm.creatorUid) == userHandle) {
- alarms.remove(i);
- didRemove = true;
- if (alarm.alarmClock != null) {
- mNextAlarmClockMayChange = true;
- }
- } else {
- if (alarm.whenElapsed > newStart) {
- newStart = alarm.whenElapsed;
- }
- if (alarm.maxWhenElapsed < newEnd) {
- newEnd = alarm.maxWhenElapsed;
- }
- i++;
- }
- }
- if (didRemove) {
- // commit the new batch bounds
- start = newStart;
- end = newEnd;
- }
- return didRemove;
- }
-
boolean hasPackage(final String packageName) {
final int N = alarms.size();
for (int i = 0; i < N; i++) {
@@ -759,6 +648,8 @@
mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
mForceAppStandbyTracker.addListener(mForceAppStandbyListener);
+
+ publishLocalService(AlarmManagerInternal.class, new LocalService());
}
static long convertToElapsed(long when, int type) {
@@ -1554,6 +1445,21 @@
}
}
+ /**
+ * System-process internal API
+ */
+ private final class LocalService implements AlarmManagerInternal {
+ @Override
+ public void removeAlarmsForUid(int uid) {
+ synchronized (mLock) {
+ removeLocked(uid);
+ }
+ }
+ }
+
+ /**
+ * Public-facing binder interface
+ */
private final IBinder mService = new IAlarmManager.Stub() {
@Override
public void set(String callingPackage,
@@ -2430,10 +2336,19 @@
}
private void removeLocked(PendingIntent operation, IAlarmListener directReceiver) {
+ if (operation == null && directReceiver == null) {
+ if (localLOGV) {
+ Slog.w(TAG, "requested remove() of null operation",
+ new RuntimeException("here"));
+ }
+ return;
+ }
+
boolean didRemove = false;
+ final Predicate<Alarm> whichAlarms = (Alarm a) -> a.matches(operation, directReceiver);
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
Batch b = mAlarmBatches.get(i);
- didRemove |= b.remove(operation, directReceiver);
+ didRemove |= b.remove(whichAlarms);
if (b.size() == 0) {
mAlarmBatches.remove(i);
}
@@ -2476,11 +2391,58 @@
}
}
- void removeLocked(String packageName) {
+ void removeLocked(final int uid) {
boolean didRemove = false;
+ final Predicate<Alarm> whichAlarms = (Alarm a) -> a.uid == uid;
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
Batch b = mAlarmBatches.get(i);
- didRemove |= b.remove(packageName);
+ didRemove |= b.remove(whichAlarms);
+ if (b.size() == 0) {
+ mAlarmBatches.remove(i);
+ }
+ }
+ for (int i = mPendingWhileIdleAlarms.size() - 1; i >= 0; i--) {
+ final Alarm a = mPendingWhileIdleAlarms.get(i);
+ if (a.uid == uid) {
+ // Don't set didRemove, since this doesn't impact the scheduled alarms.
+ mPendingWhileIdleAlarms.remove(i);
+ }
+ }
+ for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i --) {
+ final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
+ for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
+ if (alarmsForUid.get(j).uid == uid) {
+ alarmsForUid.remove(j);
+ }
+ }
+ if (alarmsForUid.size() == 0) {
+ mPendingBackgroundAlarms.removeAt(i);
+ }
+ }
+ if (didRemove) {
+ if (DEBUG_BATCH) {
+ Slog.v(TAG, "remove(uid) changed bounds; rebatching");
+ }
+ rebatchAllAlarmsLocked(true);
+ rescheduleKernelAlarmsLocked();
+ updateNextAlarmClockLocked();
+ }
+ }
+
+ void removeLocked(final String packageName) {
+ if (packageName == null) {
+ if (localLOGV) {
+ Slog.w(TAG, "requested remove() of null packageName",
+ new RuntimeException("here"));
+ }
+ return;
+ }
+
+ boolean didRemove = false;
+ final Predicate<Alarm> whichAlarms = (Alarm a) -> a.matches(packageName);
+ for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
+ Batch b = mAlarmBatches.get(i);
+ didRemove |= b.remove(whichAlarms);
if (b.size() == 0) {
mAlarmBatches.remove(i);
}
@@ -2513,11 +2475,20 @@
}
}
- void removeForStoppedLocked(int uid) {
+ void removeForStoppedLocked(final int uid) {
boolean didRemove = false;
+ final Predicate<Alarm> whichAlarms = (Alarm a) -> {
+ try {
+ if (a.uid == uid && ActivityManager.getService().isAppStartModeDisabled(
+ uid, a.packageName)) {
+ return true;
+ }
+ } catch (RemoteException e) { /* fall through */}
+ return false;
+ };
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
Batch b = mAlarmBatches.get(i);
- didRemove |= b.removeForStopped(uid);
+ didRemove |= b.remove(whichAlarms);
if (b.size() == 0) {
mAlarmBatches.remove(i);
}
@@ -2546,9 +2517,11 @@
void removeUserLocked(int userHandle) {
boolean didRemove = false;
+ final Predicate<Alarm> whichAlarms =
+ (Alarm a) -> UserHandle.getUserId(a.creatorUid) == userHandle;
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
Batch b = mAlarmBatches.get(i);
- didRemove |= b.remove(userHandle);
+ didRemove |= b.remove(whichAlarms);
if (b.size() == 0) {
mAlarmBatches.remove(i);
}
@@ -2967,7 +2940,7 @@
proto.write(AlarmProto.TAG, statsTag);
proto.write(AlarmProto.TYPE, type);
- proto.write(AlarmProto.WHEN_ELAPSED_MS, whenElapsed - nowElapsed);
+ proto.write(AlarmProto.TIME_UNTIL_WHEN_ELAPSED_MS, whenElapsed - nowElapsed);
proto.write(AlarmProto.WINDOW_LENGTH_MS, windowLength);
proto.write(AlarmProto.REPEAT_INTERVAL_MS, repeatInterval);
proto.write(AlarmProto.COUNT, count);
@@ -3396,6 +3369,7 @@
@Override
public void onReceive(Context context, Intent intent) {
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
synchronized (mLock) {
String action = intent.getAction();
String pkgList[] = null;
@@ -3416,7 +3390,6 @@
removeUserLocked(userHandle);
}
} else if (Intent.ACTION_UID_REMOVED.equals(action)) {
- int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
if (uid >= 0) {
mLastAllowWhileIdleDispatch.delete(uid);
}
@@ -3436,7 +3409,13 @@
}
if (pkgList != null && (pkgList.length > 0)) {
for (String pkg : pkgList) {
- removeLocked(pkg);
+ if (uid >= 0) {
+ // package-removed case
+ removeLocked(uid);
+ } else {
+ // external-applications-unavailable etc case
+ removeLocked(pkg);
+ }
mPriorities.remove(pkg);
for (int i=mBroadcastStats.size()-1; i>=0; i--) {
ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(i);
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 924e736..04d292f 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -421,7 +421,9 @@
boolean logOutlier = false;
long dischargeDuration = 0;
- mBatteryLevelCritical = (mHealthInfo.batteryLevel <= mCriticalBatteryLevel);
+ mBatteryLevelCritical =
+ mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+ && mHealthInfo.batteryLevel <= mCriticalBatteryLevel;
if (mHealthInfo.chargerAcOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
} else if (mHealthInfo.chargerUsbOnline) {
@@ -509,6 +511,8 @@
if (!mBatteryLevelLow) {
// Should we now switch in to low battery mode?
if (mPlugType == BATTERY_PLUGGED_NONE
+ && mHealthInfo.batteryStatus !=
+ BatteryManager.BATTERY_STATUS_UNKNOWN
&& mHealthInfo.batteryLevel <= mLowBatteryWarningLevel) {
mBatteryLevelLow = true;
}
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 4f1e335..6f697c4 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -2828,7 +2828,7 @@
becomeInactiveIfAppropriateLocked();
int curLightState = mLightState;
while (curLightState != LIGHT_STATE_IDLE) {
- stepIdleStateLocked("s:shell");
+ stepLightIdleStateLocked("s:shell");
if (curLightState == mLightState) {
pw.print("Unable to go light idle; stopped at ");
pw.println(lightStateToString(mLightState));
diff --git a/services/core/java/com/android/server/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java
index 24d8d1e..9877717 100644
--- a/services/core/java/com/android/server/EntropyMixer.java
+++ b/services/core/java/com/android/server/EntropyMixer.java
@@ -70,7 +70,10 @@
/**
* Handler that periodically updates the entropy on disk.
*/
- private final Handler mHandler = new Handler() {
+ private final Handler mHandler = new Handler(IoThread.getHandler().getLooper()) {
+ // IMPLEMENTATION NOTE: This handler runs on the I/O thread to avoid I/O on the main thread.
+ // The reason we're using our own Handler instead of IoThread.getHandler() is to create our
+ // own ID space for the "what" parameter of messages seen by the handler.
@Override
public void handleMessage(Message msg) {
if (msg.what != ENTROPY_WHAT) {
@@ -115,7 +118,12 @@
IntentFilter broadcastFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
broadcastFilter.addAction(Intent.ACTION_POWER_CONNECTED);
broadcastFilter.addAction(Intent.ACTION_REBOOT);
- context.registerReceiver(mBroadcastReceiver, broadcastFilter);
+ context.registerReceiver(
+ mBroadcastReceiver,
+ broadcastFilter,
+ null, // do not require broadcaster to hold any permissions
+ mHandler // process received broadcasts on the I/O thread instead of the main thread
+ );
}
private void scheduleEntropyWriter() {
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
index d3f77b6..4639d75 100644
--- a/services/core/java/com/android/server/GraphicsStatsService.java
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -175,7 +175,8 @@
0,
UserHandle.getUserId(uid));
synchronized (mLock) {
- pfd = requestBufferForProcessLocked(token, uid, pid, packageName, info.versionCode);
+ pfd = requestBufferForProcessLocked(token, uid, pid, packageName,
+ info.getLongVersionCode());
}
} catch (PackageManager.NameNotFoundException ex) {
throw new RemoteException("Unable to find package: '" + packageName + "'");
@@ -197,7 +198,7 @@
}
private ParcelFileDescriptor requestBufferForProcessLocked(IGraphicsStatsCallback token,
- int uid, int pid, String packageName, int versionCode) throws RemoteException {
+ int uid, int pid, String packageName, long versionCode) throws RemoteException {
ActiveBuffer buffer = fetchActiveBuffersLocked(token, uid, pid, packageName, versionCode);
scheduleRotateLocked();
return getPfd(buffer.mProcessBuffer);
@@ -292,7 +293,7 @@
}
private ActiveBuffer fetchActiveBuffersLocked(IGraphicsStatsCallback token, int uid, int pid,
- String packageName, int versionCode) throws RemoteException {
+ String packageName, long versionCode) throws RemoteException {
int size = mActive.size();
long today = normalizeDate(System.currentTimeMillis()).getTimeInMillis();
for (int i = 0; i < size; i++) {
@@ -381,19 +382,19 @@
private static native int nGetAshmemSize();
private static native long nCreateDump(int outFd, boolean isProto);
private static native void nAddToDump(long dump, String path, String packageName,
- int versionCode, long startTime, long endTime, byte[] data);
+ long versionCode, long startTime, long endTime, byte[] data);
private static native void nAddToDump(long dump, String path);
private static native void nFinishDump(long dump);
- private static native void nSaveBuffer(String path, String packageName, int versionCode,
+ private static native void nSaveBuffer(String path, String packageName, long versionCode,
long startTime, long endTime, byte[] data);
private final class BufferInfo {
final String packageName;
- final int versionCode;
+ final long versionCode;
long startTime;
long endTime;
- BufferInfo(String packageName, int versionCode, long startTime) {
+ BufferInfo(String packageName, long versionCode, long startTime) {
this.packageName = packageName;
this.versionCode = versionCode;
this.startTime = startTime;
@@ -408,7 +409,8 @@
final IBinder mToken;
MemoryFile mProcessBuffer;
- ActiveBuffer(IGraphicsStatsCallback token, int uid, int pid, String packageName, int versionCode)
+ ActiveBuffer(IGraphicsStatsCallback token, int uid, int pid, String packageName,
+ long versionCode)
throws RemoteException, IOException {
mInfo = new BufferInfo(packageName, versionCode, System.currentTimeMillis());
mUid = uid;
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 37f1dc4..55046959 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -35,6 +35,7 @@
import com.android.internal.os.TransferPipe;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethod;
import com.android.internal.view.IInputMethodClient;
@@ -49,12 +50,14 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import android.Manifest;
import android.annotation.BinderThread;
import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -104,6 +107,8 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -178,6 +183,13 @@
static final boolean DEBUG_RESTORE = DEBUG || false;
static final String TAG = "InputMethodManagerService";
+ @Retention(SOURCE)
+ @IntDef({ShellCommandResult.SUCCESS, ShellCommandResult.FAILURE})
+ private @interface ShellCommandResult {
+ int SUCCESS = 0;
+ int FAILURE = -1;
+ }
+
static final int MSG_SHOW_IM_SUBTYPE_PICKER = 1;
static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 2;
static final int MSG_SHOW_IM_CONFIG = 3;
@@ -258,8 +270,6 @@
private final AppOpsManager mAppOpsManager;
private final UserManager mUserManager;
- final InputBindResult mNoBinding = new InputBindResult(null, null, null, -1, -1);
-
// All known input methods. mMethodMap also serves as the global
// lock for this class.
final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
@@ -1766,6 +1776,7 @@
return flags;
}
+ @NonNull
InputBindResult attachNewInputLocked(
/* @InputMethodClient.StartInputReason */ final int startInputReason, boolean initial) {
if (!mBoundToMethod) {
@@ -1789,11 +1800,12 @@
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(getAppShowFlags(), null);
}
- return new InputBindResult(session.session,
- (session.channel != null ? session.channel.dup() : null),
+ return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
+ session.session, (session.channel != null ? session.channel.dup() : null),
mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
}
+ @NonNull
InputBindResult startInputLocked(
/* @InputMethodClient.StartInputReason */ final int startInputReason,
IInputMethodClient client, IInputContext inputContext,
@@ -1801,7 +1813,7 @@
@Nullable EditorInfo attribute, int controlFlags) {
// If no method is currently selected, do nothing.
if (mCurMethodId == null) {
- return mNoBinding;
+ return InputBindResult.NO_IME;
}
ClientState cs = mClients.get(client.asBinder());
@@ -1813,7 +1825,7 @@
if (attribute == null) {
Slog.w(TAG, "Ignoring startInput with null EditorInfo."
+ " uid=" + cs.uid + " pid=" + cs.pid);
- return null;
+ return InputBindResult.NULL_EDITOR_INFO;
}
try {
@@ -1827,7 +1839,7 @@
Slog.w(TAG, "Starting input on non-focused client " + cs.client
+ " (uid=" + cs.uid + " pid=" + cs.pid + ")");
}
- return null;
+ return InputBindResult.NOT_IME_TARGET_WINDOW;
}
} catch (RemoteException e) {
}
@@ -1836,20 +1848,21 @@
controlFlags, startInputReason);
}
+ @NonNull
InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
/* @InputConnectionInspector.missingMethods */ final int missingMethods,
@NonNull EditorInfo attribute, int controlFlags,
/* @InputMethodClient.StartInputReason */ final int startInputReason) {
// If no method is currently selected, do nothing.
if (mCurMethodId == null) {
- return mNoBinding;
+ return InputBindResult.NO_IME;
}
if (!InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, cs.uid,
attribute.packageName)) {
Slog.e(TAG, "Rejecting this client as it reported an invalid package name."
+ " uid=" + cs.uid + " package=" + attribute.packageName);
- return mNoBinding;
+ return InputBindResult.INVALID_PACKAGE_NAME;
}
if (mCurClient != cs) {
@@ -1889,7 +1902,9 @@
// Return to client, and we will get back with it when
// we have had a session made for it.
requestClientSessionLocked(cs);
- return new InputBindResult(null, null, mCurId, mCurSeq,
+ return new InputBindResult(
+ InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
+ null, null, mCurId, mCurSeq,
mCurUserActionNotificationSequenceNumber);
} else if (SystemClock.uptimeMillis()
< (mLastBindTime+TIME_TO_RECONNECT)) {
@@ -1900,7 +1915,9 @@
// we can report back. If it has been too long, we want
// to fall through so we can try a disconnect/reconnect
// to see if we can get back in touch with the service.
- return new InputBindResult(null, null, mCurId, mCurSeq,
+ return new InputBindResult(
+ InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
+ null, null, mCurId, mCurSeq,
mCurUserActionNotificationSequenceNumber);
} else {
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
@@ -1914,13 +1931,15 @@
InputBindResult startInputInnerLocked() {
if (mCurMethodId == null) {
- return mNoBinding;
+ return InputBindResult.NO_IME;
}
if (!mSystemReady) {
// If the system is not yet ready, we shouldn't be running third
// party code.
- return new InputBindResult(null, null, mCurMethodId, mCurSeq,
+ return new InputBindResult(
+ InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
+ null, null, mCurMethodId, mCurSeq,
mCurUserActionNotificationSequenceNumber);
}
@@ -1947,23 +1966,24 @@
mIWindowManager.addWindowToken(mCurToken, TYPE_INPUT_METHOD, DEFAULT_DISPLAY);
} catch (RemoteException e) {
}
- return new InputBindResult(null, null, mCurId, mCurSeq,
+ return new InputBindResult(
+ InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
+ null, null, mCurId, mCurSeq,
mCurUserActionNotificationSequenceNumber);
- } else {
- mCurIntent = null;
- Slog.w(TAG, "Failure connecting to input method service: "
- + mCurIntent);
}
- return null;
+ mCurIntent = null;
+ Slog.w(TAG, "Failure connecting to input method service: " + mCurIntent);
+ return InputBindResult.IME_NOT_CONNECTED;
}
+ @NonNull
private InputBindResult startInput(
/* @InputMethodClient.StartInputReason */ final int startInputReason,
IInputMethodClient client, IInputContext inputContext,
/* @InputConnectionInspector.missingMethods */ final int missingMethods,
@Nullable EditorInfo attribute, int controlFlags) {
if (!calledFromValidUser()) {
- return null;
+ return InputBindResult.INVALID_USER;
}
synchronized (mMethodMap) {
if (DEBUG) {
@@ -2707,27 +2727,42 @@
return res;
}
+ @NonNull
@Override
public InputBindResult startInputOrWindowGainedFocus(
/* @InputMethodClient.StartInputReason */ final int startInputReason,
IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode,
int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
- /* @InputConnectionInspector.missingMethods */ final int missingMethods) {
+ /* @InputConnectionInspector.missingMethods */ final int missingMethods,
+ int unverifiedTargetSdkVersion) {
+ final InputBindResult result;
if (windowToken != null) {
- return windowGainedFocus(startInputReason, client, windowToken, controlFlags,
- softInputMode, windowFlags, attribute, inputContext, missingMethods);
+ result = windowGainedFocus(startInputReason, client, windowToken, controlFlags,
+ softInputMode, windowFlags, attribute, inputContext, missingMethods,
+ unverifiedTargetSdkVersion);
} else {
- return startInput(startInputReason, client, inputContext, missingMethods, attribute,
+ result = startInput(startInputReason, client, inputContext, missingMethods, attribute,
controlFlags);
}
+ if (result == null) {
+ // This must never happen, but just in case.
+ Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
+ + InputMethodClient.getStartInputReason(startInputReason)
+ + " windowFlags=#" + Integer.toHexString(windowFlags)
+ + " editorInfo=" + attribute);
+ return InputBindResult.NULL;
+ }
+ return result;
}
+ @NonNull
private InputBindResult windowGainedFocus(
/* @InputMethodClient.StartInputReason */ final int startInputReason,
IInputMethodClient client, IBinder windowToken, int controlFlags,
/* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
int windowFlags, EditorInfo attribute, IInputContext inputContext,
- /* @InputConnectionInspector.missingMethods */ final int missingMethods) {
+ /* @InputConnectionInspector.missingMethods */ final int missingMethods,
+ int unverifiedTargetSdkVersion) {
// Needs to check the validity before clearing calling identity
final boolean calledFromValidUser = calledFromValidUser();
InputBindResult res = null;
@@ -2743,7 +2778,8 @@
+ " attribute=" + attribute
+ " controlFlags=#" + Integer.toHexString(controlFlags)
+ " softInputMode=" + InputMethodClient.softInputModeToString(softInputMode)
- + " windowFlags=#" + Integer.toHexString(windowFlags));
+ + " windowFlags=#" + Integer.toHexString(windowFlags)
+ + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion);
ClientState cs = mClients.get(client.asBinder());
if (cs == null) {
@@ -2762,7 +2798,7 @@
Slog.w(TAG, "Focus gain on non-focused client " + cs.client
+ " (uid=" + cs.uid + " pid=" + cs.pid + ")");
}
- return null;
+ return InputBindResult.NOT_IME_TARGET_WINDOW;
}
} catch (RemoteException e) {
}
@@ -2772,7 +2808,7 @@
Slog.w(TAG, "If you want to interect with IME, you need "
+ "android.permission.INTERACT_ACROSS_USERS_FULL");
hideCurrentInputLocked(0, null);
- return null;
+ return InputBindResult.INVALID_USER;
}
if (mCurFocusedWindow == windowToken) {
@@ -2784,7 +2820,9 @@
return startInputUncheckedLocked(cs, inputContext, missingMethods,
attribute, controlFlags, startInputReason);
}
- return null;
+ return new InputBindResult(
+ InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
+ null, null, null, -1, -1);
}
mCurFocusedWindow = windowToken;
mCurFocusedWindowSoftInputMode = softInputMode;
@@ -2857,22 +2895,37 @@
if ((softInputMode &
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
if (DEBUG) Slog.v(TAG, "Window asks to show input going forward");
- if (attribute != null) {
- res = startInputUncheckedLocked(cs, inputContext,
- missingMethods, attribute, controlFlags, startInputReason);
- didStart = true;
+ if (InputMethodUtils.isSoftInputModeStateVisibleAllowed(
+ unverifiedTargetSdkVersion, controlFlags)) {
+ if (attribute != null) {
+ res = startInputUncheckedLocked(cs, inputContext,
+ missingMethods, attribute, controlFlags,
+ startInputReason);
+ didStart = true;
+ }
+ showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
+ } else {
+ Slog.e(TAG, "SOFT_INPUT_STATE_VISIBLE is ignored because"
+ + " there is no focused view that also returns true from"
+ + " View#onCheckIsTextEditor()");
}
- showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
}
break;
case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
if (DEBUG) Slog.v(TAG, "Window asks to always show input");
- if (attribute != null) {
- res = startInputUncheckedLocked(cs, inputContext, missingMethods,
- attribute, controlFlags, startInputReason);
- didStart = true;
+ if (InputMethodUtils.isSoftInputModeStateVisibleAllowed(
+ unverifiedTargetSdkVersion, controlFlags)) {
+ if (attribute != null) {
+ res = startInputUncheckedLocked(cs, inputContext, missingMethods,
+ attribute, controlFlags, startInputReason);
+ didStart = true;
+ }
+ showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
+ } else {
+ Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because"
+ + " there is no focused view that also returns true from"
+ + " View#onCheckIsTextEditor()");
}
- showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
break;
}
@@ -3885,30 +3938,6 @@
// ----------------------------------------------------------------------
- @Override
- public boolean setInputMethodEnabled(String id, boolean enabled) {
- // TODO: Make this work even for non-current users?
- if (!calledFromValidUser()) {
- return false;
- }
- synchronized (mMethodMap) {
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(
- "Requires permission "
- + android.Manifest.permission.WRITE_SECURE_SETTINGS);
- }
-
- long ident = Binder.clearCallingIdentity();
- try {
- return setInputMethodEnabledLocked(id, enabled);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
boolean setInputMethodEnabledLocked(String id, boolean enabled) {
// Make sure this is a valid input method.
InputMethodInfo imm = mMethodMap.get(id);
@@ -4633,4 +4662,294 @@
p.println("No input method service.");
}
}
+
+ @BinderThread
+ @Override
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err,
+ @NonNull String[] args, @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) throws RemoteException {
+ new ShellCommandImpl(this).exec(
+ this, in, out, err, args, callback, resultReceiver);
+ }
+
+ private static final class ShellCommandImpl extends ShellCommand {
+ @NonNull
+ final InputMethodManagerService mService;
+
+ ShellCommandImpl(InputMethodManagerService service) {
+ mService = service;
+ }
+
+ @BinderThread
+ @ShellCommandResult
+ @Override
+ public int onCommand(@Nullable String cmd) {
+ // For existing "adb shell ime <command>".
+ if ("ime".equals(cmd)) {
+ final String imeCommand = getNextArg();
+ if (imeCommand == null || "help".equals(imeCommand) || "-h".equals(imeCommand)) {
+ onImeCommandHelp();
+ return ShellCommandResult.SUCCESS;
+ }
+ switch (imeCommand) {
+ case "list":
+ return mService.handleShellCommandListInputMethods(this);
+ case "enable":
+ return mService.handleShellCommandEnableDisableInputMethod(this, true);
+ case "disable":
+ return mService.handleShellCommandEnableDisableInputMethod(this, false);
+ case "set":
+ return mService.handleShellCommandSetInputMethod(this);
+ case "reset":
+ return mService.handleShellCommandResetInputMethod(this);
+ default:
+ getOutPrintWriter().println("Unknown command: " + imeCommand);
+ return ShellCommandResult.FAILURE;
+ }
+ }
+
+ return handleDefaultCommands(cmd);
+ }
+
+ @BinderThread
+ @Override
+ public void onHelp() {
+ try (PrintWriter pw = getOutPrintWriter()) {
+ pw.println("InputMethodManagerService commands:");
+ pw.println(" help");
+ pw.println(" Prints this help text.");
+ pw.println(" dump [options]");
+ pw.println(" Synonym of dumpsys.");
+ pw.println(" ime <command> [options]");
+ pw.println(" Manipulate IMEs. Run \"ime help\" for details.");
+ }
+ }
+
+ private void onImeCommandHelp() {
+ try (IndentingPrintWriter pw =
+ new IndentingPrintWriter(getOutPrintWriter(), " ", 100)) {
+ pw.println("ime <command>:");
+ pw.increaseIndent();
+
+ pw.println("list [-a] [-s]");
+ pw.increaseIndent();
+ pw.println("prints all enabled input methods.");
+ pw.increaseIndent();
+ pw.println("-a: see all input methods");
+ pw.println("-s: only a single summary line of each");
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+
+ pw.println("enable <ID>");
+ pw.increaseIndent();
+ pw.println("allows the given input method ID to be used.");
+ pw.decreaseIndent();
+
+ pw.println("disable <ID>");
+ pw.increaseIndent();
+ pw.println("disallows the given input method ID to be used.");
+ pw.decreaseIndent();
+
+ pw.println("set <ID>");
+ pw.increaseIndent();
+ pw.println("switches to the given input method ID.");
+ pw.decreaseIndent();
+
+ pw.println("reset");
+ pw.increaseIndent();
+ pw.println("reset currently selected/enabled IMEs to the default ones as if "
+ + "the device is initially booted with the current locale.");
+ pw.decreaseIndent();
+
+ pw.decreaseIndent();
+ }
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ // Shell command handlers:
+
+ /**
+ * Handles {@code adb shell ime list}.
+ * @param shellCommand {@link ShellCommand} object that is handling this command.
+ * @return Exit code of the command.
+ */
+ @BinderThread
+ @ShellCommandResult
+ private int handleShellCommandListInputMethods(@NonNull ShellCommand shellCommand) {
+ boolean all = false;
+ boolean brief = false;
+ while (true) {
+ final String nextOption = shellCommand.getNextOption();
+ if (nextOption == null) {
+ break;
+ }
+ switch (nextOption) {
+ case "-a":
+ all = true;
+ break;
+ case "-s":
+ brief = true;
+ break;
+ }
+ }
+ final List<InputMethodInfo> methods = all ?
+ getInputMethodList() : getEnabledInputMethodList();
+ final PrintWriter pr = shellCommand.getOutPrintWriter();
+ final Printer printer = x -> pr.println(x);
+ final int N = methods.size();
+ for (int i = 0; i < N; ++i) {
+ if (brief) {
+ pr.println(methods.get(i).getId());
+ } else {
+ pr.print(methods.get(i).getId()); pr.println(":");
+ methods.get(i).dump(printer, " ");
+ }
+ }
+ return ShellCommandResult.SUCCESS;
+ }
+
+ /**
+ * Handles {@code adb shell ime enable} and {@code adb shell ime disable}.
+ * @param shellCommand {@link ShellCommand} object that is handling this command.
+ * @param enabled {@code true} if the command was {@code adb shell ime enable}.
+ * @return Exit code of the command.
+ */
+ @BinderThread
+ @ShellCommandResult
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ private int handleShellCommandEnableDisableInputMethod(
+ @NonNull ShellCommand shellCommand, boolean enabled) {
+ if (!calledFromValidUser()) {
+ shellCommand.getErrPrintWriter().print(
+ "Must be called from the foreground user or with INTERACT_ACROSS_USERS_FULL");
+ return ShellCommandResult.FAILURE;
+ }
+ final String id = shellCommand.getNextArgRequired();
+
+ final boolean previouslyEnabled;
+ synchronized (mMethodMap) {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ != PackageManager.PERMISSION_GRANTED) {
+ shellCommand.getErrPrintWriter().print(
+ "Caller must have WRITE_SECURE_SETTINGS permission");
+ throw new SecurityException(
+ "Requires permission "
+ + android.Manifest.permission.WRITE_SECURE_SETTINGS);
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ previouslyEnabled = setInputMethodEnabledLocked(id, enabled);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ final PrintWriter pr = shellCommand.getOutPrintWriter();
+ pr.print("Input method ");
+ pr.print(id);
+ pr.print(": ");
+ pr.print((enabled == previouslyEnabled) ? "already " : "now ");
+ pr.println(enabled ? "enabled" : "disabled");
+ return ShellCommandResult.SUCCESS;
+ }
+
+ /**
+ * Handles {@code adb shell ime set}.
+ * @param shellCommand {@link ShellCommand} object that is handling this command.
+ * @return Exit code of the command.
+ */
+ @BinderThread
+ @ShellCommandResult
+ private int handleShellCommandSetInputMethod(@NonNull ShellCommand shellCommand) {
+ final String id = shellCommand.getNextArgRequired();
+ setInputMethod(null, id);
+ final PrintWriter pr = shellCommand.getOutPrintWriter();
+ pr.print("Input method ");
+ pr.print(id);
+ pr.println(" selected");
+ return ShellCommandResult.SUCCESS;
+ }
+
+ /**
+ * Handles {@code adb shell ime reset-ime}.
+ * @param shellCommand {@link ShellCommand} object that is handling this command.
+ * @return Exit code of the command.
+ */
+ @BinderThread
+ @ShellCommandResult
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ private int handleShellCommandResetInputMethod(@NonNull ShellCommand shellCommand) {
+ if (!calledFromValidUser()) {
+ shellCommand.getErrPrintWriter().print(
+ "Must be called from the foreground user or with INTERACT_ACROSS_USERS_FULL");
+ return ShellCommandResult.FAILURE;
+ }
+ synchronized (mMethodMap) {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ != PackageManager.PERMISSION_GRANTED) {
+ shellCommand.getErrPrintWriter().print(
+ "Caller must have WRITE_SECURE_SETTINGS permission");
+ throw new SecurityException(
+ "Requires permission "
+ + android.Manifest.permission.WRITE_SECURE_SETTINGS);
+ }
+ final String nextIme;
+ final List<InputMethodInfo> nextEnabledImes;
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mMethodMap) {
+ hideCurrentInputLocked(0, null);
+ unbindCurrentMethodLocked(false);
+ // Reset the current IME
+ resetSelectedInputMethodAndSubtypeLocked(null);
+ // Also reset the settings of the current IME
+ mSettings.putSelectedInputMethod(null);
+ // Disable all enabled IMEs.
+ {
+ final ArrayList<InputMethodInfo> enabledImes =
+ mSettings.getEnabledInputMethodListLocked();
+ final int N = enabledImes.size();
+ for (int i = 0; i < N; ++i) {
+ setInputMethodEnabledLocked(enabledImes.get(i).getId(), false);
+ }
+ }
+ // Re-enable with default enabled IMEs.
+ {
+ final ArrayList<InputMethodInfo> defaultEnabledIme =
+ InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList);
+ final int N = defaultEnabledIme.size();
+ for (int i = 0; i < N; ++i) {
+ setInputMethodEnabledLocked(defaultEnabledIme.get(i).getId(), true);
+ }
+ }
+ updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
+ InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
+ mSettings.getEnabledInputMethodListLocked(),
+ mSettings.getCurrentUserId(),
+ mContext.getBasePackageName());
+ nextIme = mSettings.getSelectedInputMethod();
+ nextEnabledImes = getEnabledInputMethodList();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ final PrintWriter pr = shellCommand.getOutPrintWriter();
+ pr.println("Reset current and enabled IMEs");
+ pr.println("Newly selected IME:");
+ pr.print(" "); pr.println(nextIme);
+ pr.println("Newly enabled IMEs:");
+ {
+ final int N = nextEnabledImes.size();
+ for (int i = 0; i < N; ++i) {
+ pr.print(" ");
+ pr.println(nextEnabledImes.get(i).getId());
+ }
+ }
+ return ShellCommandResult.SUCCESS;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 1154fbe..d3ab125 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -34,6 +34,7 @@
import android.net.IpSecTransformResponse;
import android.net.IpSecUdpEncapResponse;
import android.net.NetworkUtils;
+import android.net.TrafficStats;
import android.net.util.NetdService;
import android.os.Binder;
import android.os.IBinder;
@@ -57,11 +58,23 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import libcore.io.IoUtils;
-/** @hide */
+/**
+ * A service to manage multiple clients that want to access the IpSec API. The service is
+ * responsible for maintaining a list of clients and managing the resources (and related quotas)
+ * that each of them own.
+ *
+ * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at
+ * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one
+ * thread is ever running at a time.
+ *
+ * @hide
+ */
public class IpSecService extends IIpSecService.Stub {
private static final String TAG = "IpSecService";
private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
@@ -91,17 +104,6 @@
/** Should be a never-repeating global ID for resources */
private static AtomicInteger mNextResourceId = new AtomicInteger(0x00FADED0);
- @GuardedBy("this")
- private final ManagedResourceArray<SpiRecord> mSpiRecords = new ManagedResourceArray<>();
-
- @GuardedBy("this")
- private final ManagedResourceArray<TransformRecord> mTransformRecords =
- new ManagedResourceArray<>();
-
- @GuardedBy("this")
- private final ManagedResourceArray<UdpSocketRecord> mUdpSocketRecords =
- new ManagedResourceArray<>();
-
interface IpSecServiceConfiguration {
INetd getNetdInstance() throws RemoteException;
@@ -119,9 +121,178 @@
}
private final IpSecServiceConfiguration mSrvConfig;
+ final UidFdTagger mUidFdTagger;
+
+ /**
+ * Interface for user-reference and kernel-resource cleanup.
+ *
+ * <p>This interface must be implemented for a resource to be reference counted.
+ */
+ @VisibleForTesting
+ public interface IResource {
+ /**
+ * Invalidates a IResource object, ensuring it is invalid for the purposes of allocating new
+ * objects dependent on it.
+ *
+ * <p>Implementations of this method are expected to remove references to the IResource
+ * object from the IpSecService's tracking arrays. The removal from the arrays ensures that
+ * the resource is considered invalid for user access or allocation or use in other
+ * resources.
+ *
+ * <p>References to the IResource object may be held by other RefcountedResource objects,
+ * and as such, the kernel resources and quota may not be cleaned up.
+ */
+ void invalidate() throws RemoteException;
+
+ /**
+ * Releases underlying resources and related quotas.
+ *
+ * <p>Implementations of this method are expected to remove all system resources that are
+ * tracked by the IResource object. Due to other RefcountedResource objects potentially
+ * having references to the IResource object, freeUnderlyingResources may not always be
+ * called from releaseIfUnreferencedRecursively().
+ */
+ void freeUnderlyingResources() throws RemoteException;
+ }
+
+ /**
+ * RefcountedResource manages references and dependencies in an exclusively acyclic graph.
+ *
+ * <p>RefcountedResource implements both explicit and implicit resource management. Creating a
+ * RefcountedResource object creates an explicit reference that must be freed by calling
+ * userRelease(). Additionally, adding this object as a child of another RefcountedResource
+ * object will add an implicit reference.
+ *
+ * <p>Resources are cleaned up when all references, both implicit and explicit, are released
+ * (ie, when userRelease() is called and when all parents have called releaseReference() on this
+ * object.)
+ */
+ @VisibleForTesting
+ public class RefcountedResource<T extends IResource> implements IBinder.DeathRecipient {
+ private final T mResource;
+ private final List<RefcountedResource> mChildren;
+ int mRefCount = 1; // starts at 1 for user's reference.
+ IBinder mBinder;
+
+ RefcountedResource(T resource, IBinder binder, RefcountedResource... children) {
+ synchronized (IpSecService.this) {
+ this.mResource = resource;
+ this.mChildren = new ArrayList<>(children.length);
+ this.mBinder = binder;
+
+ for (RefcountedResource child : children) {
+ mChildren.add(child);
+ child.mRefCount++;
+ }
+
+ try {
+ mBinder.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ binderDied();
+ }
+ }
+ }
+
+ /**
+ * If the Binder object dies, this function is called to free the system resources that are
+ * being tracked by this record and to subsequently release this record for garbage
+ * collection
+ */
+ @Override
+ public void binderDied() {
+ synchronized (IpSecService.this) {
+ try {
+ userRelease();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to release resource: " + e);
+ }
+ }
+ }
+
+ public T getResource() {
+ return mResource;
+ }
+
+ /**
+ * Unlinks from binder and performs IpSecService resource cleanup (removes from resource
+ * arrays)
+ *
+ * <p>If this method has been previously called, the RefcountedResource's binder field will
+ * be null, and the method will return without performing the cleanup a second time.
+ *
+ * <p>Note that calling this function does not imply that kernel resources will be freed at
+ * this time, or that the related quota will be returned. Such actions will only be
+ * performed upon the reference count reaching zero.
+ */
+ @GuardedBy("IpSecService.this")
+ public void userRelease() throws RemoteException {
+ // Prevent users from putting reference counts into a bad state by calling
+ // userRelease() multiple times.
+ if (mBinder == null) {
+ return;
+ }
+
+ mBinder.unlinkToDeath(this, 0);
+ mBinder = null;
+
+ mResource.invalidate();
+
+ releaseReference();
+ }
+
+ /**
+ * Removes a reference to this resource. If the resultant reference count is zero, the
+ * underlying resources are freed, and references to all child resources are also dropped
+ * recursively (resulting in them freeing their resources and children, etcetera)
+ *
+ * <p>This method also sets the reference count to an invalid value (-1) to signify that it
+ * has been fully released. Any subsequent calls to this method will result in an
+ * IllegalStateException being thrown due to resource already having been previously
+ * released
+ */
+ @VisibleForTesting
+ @GuardedBy("IpSecService.this")
+ public void releaseReference() throws RemoteException {
+ mRefCount--;
+
+ if (mRefCount > 0) {
+ return;
+ } else if (mRefCount < 0) {
+ throw new IllegalStateException(
+ "Invalid operation - resource has already been released.");
+ }
+
+ // Cleanup own resources
+ mResource.freeUnderlyingResources();
+
+ // Cleanup child resources as needed
+ for (RefcountedResource<? extends IResource> child : mChildren) {
+ child.releaseReference();
+ }
+
+ // Enforce that resource cleanup can only be called once
+ // By decrementing the refcount (from 0 to -1), the next call will throw an
+ // IllegalStateException - it has already been released fully.
+ mRefCount--;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("{mResource=")
+ .append(mResource)
+ .append(", mRefCount=")
+ .append(mRefCount)
+ .append(", mChildren=")
+ .append(mChildren)
+ .append("}")
+ .toString();
+ }
+ }
/* Very simple counting class that looks much like a counting semaphore */
- public static class ResourceTracker {
+ @VisibleForTesting
+ static class ResourceTracker {
private final int mMax;
int mCurrent;
@@ -130,18 +301,18 @@
mCurrent = 0;
}
- synchronized boolean isAvailable() {
+ boolean isAvailable() {
return (mCurrent < mMax);
}
- synchronized void take() {
+ void take() {
if (!isAvailable()) {
Log.wtf(TAG, "Too many resources allocated!");
}
mCurrent++;
}
- synchronized void give() {
+ void give() {
if (mCurrent <= 0) {
Log.wtf(TAG, "We've released this resource too many times");
}
@@ -160,40 +331,70 @@
}
}
- private static final class UserQuotaTracker {
- /* Maximum number of UDP Encap Sockets that a single UID may possess */
+ @VisibleForTesting
+ static final class UserRecord {
+ /* Type names */
+ public static final String TYPENAME_SPI = "SecurityParameterIndex";
+ public static final String TYPENAME_TRANSFORM = "IpSecTransform";
+ public static final String TYPENAME_ENCAP_SOCKET = "UdpEncapSocket";
+
+ /* Maximum number of each type of resource that a single UID may possess */
public static final int MAX_NUM_ENCAP_SOCKETS = 2;
-
- /* Maximum number of IPsec Transforms that a single UID may possess */
public static final int MAX_NUM_TRANSFORMS = 4;
-
- /* Maximum number of IPsec Transforms that a single UID may possess */
public static final int MAX_NUM_SPIS = 8;
- /* Record for one users's IpSecService-managed objects */
- public static class UserRecord {
- public final ResourceTracker socket = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
- public final ResourceTracker transform = new ResourceTracker(MAX_NUM_TRANSFORMS);
- public final ResourceTracker spi = new ResourceTracker(MAX_NUM_SPIS);
+ final RefcountedResourceArray<SpiRecord> mSpiRecords =
+ new RefcountedResourceArray<>(TYPENAME_SPI);
+ final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
- @Override
- public String toString() {
- return new StringBuilder()
- .append("{socket=")
- .append(socket)
- .append(", transform=")
- .append(transform)
- .append(", spi=")
- .append(spi)
- .append("}")
- .toString();
- }
+ final RefcountedResourceArray<TransformRecord> mTransformRecords =
+ new RefcountedResourceArray<>(TYPENAME_TRANSFORM);
+ final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
+
+ final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
+ new RefcountedResourceArray<>(TYPENAME_ENCAP_SOCKET);
+ final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
+
+ void removeSpiRecord(int resourceId) {
+ mSpiRecords.remove(resourceId);
}
+ void removeTransformRecord(int resourceId) {
+ mTransformRecords.remove(resourceId);
+ }
+
+ void removeEncapSocketRecord(int resourceId) {
+ mEncapSocketRecords.remove(resourceId);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("{mSpiQuotaTracker=")
+ .append(mSpiQuotaTracker)
+ .append(", mTransformQuotaTracker=")
+ .append(mTransformQuotaTracker)
+ .append(", mSocketQuotaTracker=")
+ .append(mSocketQuotaTracker)
+ .append(", mSpiRecords=")
+ .append(mSpiRecords)
+ .append(", mTransformRecords=")
+ .append(mTransformRecords)
+ .append(", mEncapSocketRecords=")
+ .append(mEncapSocketRecords)
+ .append("}")
+ .toString();
+ }
+ }
+
+ @VisibleForTesting
+ static final class UserResourceTracker {
private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
- /* a never-fail getter so that we can populate the list of UIDs as-needed */
- public synchronized UserRecord getUserRecord(int uid) {
+ /** Never-fail getter that populates the list of UIDs as-needed */
+ public UserRecord getUserRecord(int uid) {
+ checkCallerUid(uid);
+
UserRecord r = mUserRecords.get(uid);
if (r == null) {
r = new UserRecord();
@@ -202,122 +403,56 @@
return r;
}
+ /** Safety method; guards against access of other user's UserRecords */
+ private void checkCallerUid(int uid) {
+ if (uid != Binder.getCallingUid()
+ && android.os.Process.SYSTEM_UID != Binder.getCallingUid()) {
+ throw new SecurityException("Attempted access of unowned resources");
+ }
+ }
+
@Override
public String toString() {
return mUserRecords.toString();
}
}
- private final UserQuotaTracker mUserQuotaTracker = new UserQuotaTracker();
+ @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
/**
- * The ManagedResource class provides a facility to cleanly and reliably release system
- * resources. It relies on two things: an IBinder that allows ManagedResource to automatically
- * clean up in the event that the Binder dies and a user-provided resourceId that should
- * uniquely identify the managed resource. To use this class, the user should implement the
- * releaseResources() method that is responsible for releasing system resources when invoked.
+ * The KernelResourceRecord class provides a facility to cleanly and reliably track system
+ * resources. It relies on a provided resourceId that should uniquely identify the kernel
+ * resource. To use this class, the user should implement the invalidate() and
+ * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
+ * tracking arrays and kernel resources, respectively
*/
- private abstract class ManagedResource implements IBinder.DeathRecipient {
+ private abstract class KernelResourceRecord implements IResource {
final int pid;
final int uid;
- private IBinder mBinder;
- protected int mResourceId;
+ protected final int mResourceId;
- private AtomicInteger mReferenceCount = new AtomicInteger(0);
-
- ManagedResource(int resourceId, IBinder binder) {
+ KernelResourceRecord(int resourceId) {
super();
if (resourceId == INVALID_RESOURCE_ID) {
throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
}
- mBinder = binder;
mResourceId = resourceId;
pid = Binder.getCallingPid();
uid = Binder.getCallingUid();
getResourceTracker().take();
- try {
- mBinder.linkToDeath(this, 0);
- } catch (RemoteException e) {
- binderDied();
- }
}
- public void addReference() {
- mReferenceCount.incrementAndGet();
+ @Override
+ public abstract void invalidate() throws RemoteException;
+
+ /** Convenience method; retrieves the user resource record for the stored UID. */
+ protected UserRecord getUserRecord() {
+ return mUserResourceTracker.getUserRecord(uid);
}
- public void removeReference() {
- if (mReferenceCount.decrementAndGet() < 0) {
- Log.wtf(TAG, "Programming error: negative reference count");
- }
- }
-
- public boolean isReferenced() {
- return (mReferenceCount.get() > 0);
- }
-
- /**
- * Ensures that the caller is either the owner of this resource or has the system UID and
- * throws a SecurityException otherwise.
- */
- public void checkOwnerOrSystem() {
- if (uid != Binder.getCallingUid()
- && android.os.Process.SYSTEM_UID != Binder.getCallingUid()) {
- throw new SecurityException("Only the owner may access managed resources!");
- }
- }
-
- /**
- * When this record is no longer needed for managing system resources this function should
- * clean up all system resources and nullify the record. This function shall perform all
- * necessary cleanup of the resources managed by this record.
- *
- * <p>NOTE: this function verifies ownership before allowing resources to be freed.
- */
- public final void release() throws RemoteException {
- synchronized (IpSecService.this) {
- if (isReferenced()) {
- throw new IllegalStateException(
- "Cannot release a resource that has active references!");
- }
-
- if (mResourceId == INVALID_RESOURCE_ID) {
- return;
- }
-
- releaseResources();
- getResourceTracker().give();
- if (mBinder != null) {
- mBinder.unlinkToDeath(this, 0);
- }
- mBinder = null;
-
- mResourceId = INVALID_RESOURCE_ID;
- }
- }
-
- /**
- * If the Binder object dies, this function is called to free the system resources that are
- * being managed by this record and to subsequently release this record for garbage
- * collection
- */
- public final void binderDied() {
- try {
- release();
- } catch (Exception e) {
- Log.e(TAG, "Failed to release resource: " + e);
- }
- }
-
- /**
- * Implement this method to release all system resources that are being protected by this
- * record. Once the resources are released, the record should be invalidated and no longer
- * used by calling release(). This should NEVER be called directly.
- *
- * <p>Calls to this are always guarded by IpSecService#this
- */
- protected abstract void releaseResources() throws RemoteException;
+ @Override
+ public abstract void freeUnderlyingResources() throws RemoteException;
/** Get the resource tracker for this resource */
protected abstract ResourceTracker getResourceTracker();
@@ -331,30 +466,52 @@
.append(pid)
.append(", uid=")
.append(uid)
- .append(", mReferenceCount=")
- .append(mReferenceCount.get())
.append("}")
.toString();
}
};
+ // TODO: Move this to right after RefcountedResource. With this here, Gerrit was showing many
+ // more things as changed.
/**
- * Minimal wrapper around SparseArray that performs ownership validation on element accesses.
+ * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
+ *
+ * <p>RefcountedResourceArray prevents null insertions, and throws an IllegalArgumentException
+ * if a key is not found during a retrieval process.
*/
- private class ManagedResourceArray<T extends ManagedResource> {
- SparseArray<T> mArray = new SparseArray<>();
+ static class RefcountedResourceArray<T extends IResource> {
+ SparseArray<RefcountedResource<T>> mArray = new SparseArray<>();
+ private final String mTypeName;
- T getAndCheckOwner(int key) {
- T val = mArray.get(key);
- // The value should never be null unless the resource doesn't exist
- // (since we do not allow null resources to be added).
- if (val != null) {
- val.checkOwnerOrSystem();
- }
- return val;
+ public RefcountedResourceArray(String typeName) {
+ this.mTypeName = typeName;
}
- void put(int key, T obj) {
+ /**
+ * Accessor method to get inner resource object.
+ *
+ * @throws IllegalArgumentException if no resource with provided key is found.
+ */
+ T getResourceOrThrow(int key) {
+ return getRefcountedResourceOrThrow(key).getResource();
+ }
+
+ /**
+ * Accessor method to get reference counting wrapper.
+ *
+ * @throws IllegalArgumentException if no resource with provided key is found.
+ */
+ RefcountedResource<T> getRefcountedResourceOrThrow(int key) {
+ RefcountedResource<T> resource = mArray.get(key);
+ if (resource == null) {
+ throw new IllegalArgumentException(
+ String.format("No such %s found for given id: %d", mTypeName, key));
+ }
+
+ return resource;
+ }
+
+ void put(int key, RefcountedResource<T> obj) {
checkNotNull(obj, "Null resources cannot be added");
mArray.put(key, obj);
}
@@ -369,30 +526,17 @@
}
}
- private final class TransformRecord extends ManagedResource {
+ private final class TransformRecord extends KernelResourceRecord {
private final IpSecConfig mConfig;
private final SpiRecord[] mSpis;
- private final UdpSocketRecord mSocket;
+ private final EncapSocketRecord mSocket;
TransformRecord(
- int resourceId,
- IBinder binder,
- IpSecConfig config,
- SpiRecord[] spis,
- UdpSocketRecord socket) {
- super(resourceId, binder);
+ int resourceId, IpSecConfig config, SpiRecord[] spis, EncapSocketRecord socket) {
+ super(resourceId);
mConfig = config;
mSpis = spis;
mSocket = socket;
-
- for (int direction : DIRECTIONS) {
- mSpis[direction].addReference();
- mSpis[direction].setOwnedByTransform();
- }
-
- if (mSocket != null) {
- mSocket.addReference();
- }
}
public IpSecConfig getConfig() {
@@ -405,7 +549,7 @@
/** always guarded by IpSecService#this */
@Override
- protected void releaseResources() {
+ public void freeUnderlyingResources() {
for (int direction : DIRECTIONS) {
int spi = mSpis[direction].getSpi();
try {
@@ -424,17 +568,17 @@
}
}
- for (int direction : DIRECTIONS) {
- mSpis[direction].removeReference();
- }
-
- if (mSocket != null) {
- mSocket.removeReference();
- }
+ getResourceTracker().give();
}
+ @Override
+ public void invalidate() throws RemoteException {
+ getUserRecord().removeTransformRecord(mResourceId);
+ }
+
+ @Override
protected ResourceTracker getResourceTracker() {
- return mUserQuotaTracker.getUserRecord(this.uid).transform;
+ return getUserRecord().mTransformQuotaTracker;
}
@Override
@@ -456,7 +600,7 @@
}
}
- private final class SpiRecord extends ManagedResource {
+ private final class SpiRecord extends KernelResourceRecord {
private final int mDirection;
private final String mLocalAddress;
private final String mRemoteAddress;
@@ -466,12 +610,11 @@
SpiRecord(
int resourceId,
- IBinder binder,
int direction,
String localAddress,
String remoteAddress,
int spi) {
- super(resourceId, binder);
+ super(resourceId);
mDirection = direction;
mLocalAddress = localAddress;
mRemoteAddress = remoteAddress;
@@ -480,7 +623,7 @@
/** always guarded by IpSecService#this */
@Override
- protected void releaseResources() {
+ public void freeUnderlyingResources() {
if (mOwnedByTransform) {
Log.d(TAG, "Cannot release Spi " + mSpi + ": Currently locked by a Transform");
// Because SPIs are "handed off" to transform, objects, they should never be
@@ -503,11 +646,8 @@
}
mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
- }
- @Override
- protected ResourceTracker getResourceTracker() {
- return mUserQuotaTracker.getUserRecord(this.uid).spi;
+ getResourceTracker().give();
}
public int getSpi() {
@@ -524,6 +664,16 @@
}
@Override
+ public void invalidate() throws RemoteException {
+ getUserRecord().removeSpiRecord(mResourceId);
+ }
+
+ @Override
+ protected ResourceTracker getResourceTracker() {
+ return getUserRecord().mSpiQuotaTracker;
+ }
+
+ @Override
public String toString() {
StringBuilder strBuilder = new StringBuilder();
strBuilder
@@ -544,27 +694,24 @@
}
}
- private final class UdpSocketRecord extends ManagedResource {
+ private final class EncapSocketRecord extends KernelResourceRecord {
private FileDescriptor mSocket;
private final int mPort;
- UdpSocketRecord(int resourceId, IBinder binder, FileDescriptor socket, int port) {
- super(resourceId, binder);
+ EncapSocketRecord(int resourceId, FileDescriptor socket, int port) {
+ super(resourceId);
mSocket = socket;
mPort = port;
}
/** always guarded by IpSecService#this */
@Override
- protected void releaseResources() {
+ public void freeUnderlyingResources() {
Log.d(TAG, "Closing port " + mPort);
IoUtils.closeQuietly(mSocket);
mSocket = null;
- }
- @Override
- protected ResourceTracker getResourceTracker() {
- return mUserQuotaTracker.getUserRecord(this.uid).socket;
+ getResourceTracker().give();
}
public int getPort() {
@@ -576,6 +723,16 @@
}
@Override
+ protected ResourceTracker getResourceTracker() {
+ return getUserRecord().mSocketQuotaTracker;
+ }
+
+ @Override
+ public void invalidate() {
+ getUserRecord().removeEncapSocketRecord(mResourceId);
+ }
+
+ @Override
public String toString() {
return new StringBuilder()
.append("{super=")
@@ -607,8 +764,23 @@
/** @hide */
@VisibleForTesting
public IpSecService(Context context, IpSecServiceConfiguration config) {
+ this(context, config, (fd, uid) -> {
+ try{
+ TrafficStats.setThreadStatsUid(uid);
+ TrafficStats.tagFileDescriptor(fd);
+ } finally {
+ TrafficStats.clearThreadStatsUid();
+ }
+ });
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public IpSecService(
+ Context context, IpSecServiceConfiguration config, UidFdTagger uidFdTagger) {
mContext = context;
mSrvConfig = config;
+ mUidFdTagger = uidFdTagger;
}
public void systemReady() {
@@ -672,23 +844,24 @@
throw new IllegalArgumentException("Invalid Direction: " + direction);
}
- @Override
/** Get a new SPI and maintain the reservation in the system server */
- public synchronized IpSecSpiResponse reserveSecurityParameterIndex(
+ @Override
+ public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
int direction, String remoteAddress, int requestedSpi, IBinder binder)
throws RemoteException {
checkDirection(direction);
checkInetAddress(remoteAddress);
/* requestedSpi can be anything in the int range, so no check is needed. */
- checkNotNull(binder, "Null Binder passed to reserveSecurityParameterIndex");
+ checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
int resourceId = mNextResourceId.getAndIncrement();
int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
String localAddress = "";
try {
- if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).spi.isAvailable()) {
+ if (!userRecord.mSpiQuotaTracker.isAvailable()) {
return new IpSecSpiResponse(
IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
}
@@ -702,9 +875,11 @@
remoteAddress,
requestedSpi);
Log.d(TAG, "Allocated SPI " + spi);
- mSpiRecords.put(
+ userRecord.mSpiRecords.put(
resourceId,
- new SpiRecord(resourceId, binder, direction, localAddress, remoteAddress, spi));
+ new RefcountedResource<SpiRecord>(
+ new SpiRecord(resourceId, direction, localAddress, remoteAddress, spi),
+ binder));
} catch (ServiceSpecificException e) {
// TODO: Add appropriate checks when other ServiceSpecificException types are supported
return new IpSecSpiResponse(
@@ -718,26 +893,17 @@
/* This method should only be called from Binder threads. Do not call this from
* within the system server as it will crash the system on failure.
*/
- private synchronized <T extends ManagedResource> void releaseManagedResource(
- ManagedResourceArray<T> resArray, int resourceId, String typeName)
+ private void releaseResource(RefcountedResourceArray resArray, int resourceId)
throws RemoteException {
- // We want to non-destructively get so that we can check credentials before removing
- // this from the records.
- T record = resArray.getAndCheckOwner(resourceId);
- if (record == null) {
- throw new IllegalArgumentException(
- typeName + " " + resourceId + " is not available to be deleted");
- }
-
- record.release();
- resArray.remove(resourceId);
+ resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
}
/** Release a previously allocated SPI that has been registered with the system server */
@Override
- public void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
- releaseManagedResource(mSpiRecords, resourceId, "SecurityParameterIndex");
+ public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException {
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+ releaseResource(userRecord.mSpiRecords, resourceId);
}
/**
@@ -776,6 +942,26 @@
}
/**
+ * Functional interface to do traffic tagging of given sockets to UIDs.
+ *
+ * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap
+ * sockets are billed to the UID that the UDP encap socket was created on behalf of.
+ *
+ * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static
+ * methods that cannot be easily mocked/tested.
+ */
+ @VisibleForTesting
+ public interface UidFdTagger {
+ /**
+ * Sets socket tag to assign all traffic to the provided UID.
+ *
+ * <p>Since the socket is created on behalf of an unprivileged application, all traffic
+ * should be accounted to the UID of the unprivileged application.
+ */
+ public void tag(FileDescriptor fd, int uid) throws IOException;
+ }
+
+ /**
* Open a socket via the system server and bind it to the specified port (random if port=0).
* This will return a PFD to the user that represent a bound UDP socket. The system server will
* cache the socket and a record of its owner so that it can and must be freed when no longer
@@ -790,21 +976,18 @@
}
checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
+ int callingUid = Binder.getCallingUid();
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
int resourceId = mNextResourceId.getAndIncrement();
FileDescriptor sockFd = null;
try {
- if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).socket.isAvailable()) {
+ if (!userRecord.mSocketQuotaTracker.isAvailable()) {
return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
}
sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ mUidFdTagger.tag(sockFd, callingUid);
- if (port != 0) {
- Log.v(TAG, "Binding to port " + port);
- Os.bind(sockFd, INADDR_ANY, port);
- } else {
- port = bindToRandomPort(sockFd);
- }
// This code is common to both the unspecified and specified port cases
Os.setsockoptInt(
sockFd,
@@ -812,8 +995,18 @@
OsConstants.UDP_ENCAP,
OsConstants.UDP_ENCAP_ESPINUDP);
- mUdpSocketRecords.put(
- resourceId, new UdpSocketRecord(resourceId, binder, sockFd, port));
+ mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(sockFd, callingUid);
+ if (port != 0) {
+ Log.v(TAG, "Binding to port " + port);
+ Os.bind(sockFd, INADDR_ANY, port);
+ } else {
+ port = bindToRandomPort(sockFd);
+ }
+
+ userRecord.mEncapSocketRecords.put(
+ resourceId,
+ new RefcountedResource<EncapSocketRecord>(
+ new EncapSocketRecord(resourceId, sockFd, port), binder));
return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd);
} catch (IOException | ErrnoException e) {
IoUtils.closeQuietly(sockFd);
@@ -825,9 +1018,9 @@
/** close a socket that has been been allocated by and registered with the system server */
@Override
- public void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
-
- releaseManagedResource(mUdpSocketRecords, resourceId, "UdpEncapsulationSocket");
+ public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException {
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+ releaseResource(userRecord.mEncapSocketRecords, resourceId);
}
/**
@@ -835,6 +1028,8 @@
* IllegalArgumentException if they are not.
*/
private void checkIpSecConfig(IpSecConfig config) {
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+
if (config.getLocalAddress() == null) {
throw new IllegalArgumentException("Invalid null Local InetAddress");
}
@@ -863,12 +1058,9 @@
break;
case IpSecTransform.ENCAP_ESPINUDP:
case IpSecTransform.ENCAP_ESPINUDP_NON_IKE:
- if (mUdpSocketRecords.getAndCheckOwner(
- config.getEncapSocketResourceId()) == null) {
- throw new IllegalStateException(
- "No Encapsulation socket for Resource Id: "
- + config.getEncapSocketResourceId());
- }
+ // Retrieve encap socket record; will throw IllegalArgumentException if not found
+ userRecord.mEncapSocketRecords.getResourceOrThrow(
+ config.getEncapSocketResourceId());
int port = config.getEncapRemotePort();
if (port <= 0 || port > 0xFFFF) {
@@ -892,9 +1084,8 @@
+ " exclusive with other Authentication or Encryption algorithms");
}
- if (mSpiRecords.getAndCheckOwner(config.getSpiResourceId(direction)) == null) {
- throw new IllegalStateException("No SPI for specified Resource Id");
- }
+ // Retrieve SPI record; will throw IllegalArgumentException if not found
+ userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId(direction));
}
}
@@ -911,16 +1102,27 @@
checkIpSecConfig(c);
checkNotNull(binder, "Null Binder passed to createTransportModeTransform");
int resourceId = mNextResourceId.getAndIncrement();
- if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).transform.isAvailable()) {
+
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+
+ // Avoid resizing by creating a dependency array of min-size 3 (1 UDP encap + 2 SPIs)
+ List<RefcountedResource> dependencies = new ArrayList<>(3);
+
+ if (!userRecord.mTransformQuotaTracker.isAvailable()) {
return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
}
SpiRecord[] spis = new SpiRecord[DIRECTIONS.length];
int encapType, encapLocalPort = 0, encapRemotePort = 0;
- UdpSocketRecord socketRecord = null;
+ EncapSocketRecord socketRecord = null;
encapType = c.getEncapType();
if (encapType != IpSecTransform.ENCAP_NONE) {
- socketRecord = mUdpSocketRecords.getAndCheckOwner(c.getEncapSocketResourceId());
+ RefcountedResource<EncapSocketRecord> refcountedSocketRecord =
+ userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
+ c.getEncapSocketResourceId());
+ dependencies.add(refcountedSocketRecord);
+
+ socketRecord = refcountedSocketRecord.getResource();
encapLocalPort = socketRecord.getPort();
encapRemotePort = c.getEncapRemotePort();
}
@@ -930,7 +1132,12 @@
IpSecAlgorithm crypt = c.getEncryption(direction);
IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption(direction);
- spis[direction] = mSpiRecords.getAndCheckOwner(c.getSpiResourceId(direction));
+ RefcountedResource<SpiRecord> refcountedSpiRecord =
+ userRecord.mSpiRecords.getRefcountedResourceOrThrow(
+ c.getSpiResourceId(direction));
+ dependencies.add(refcountedSpiRecord);
+
+ spis[direction] = refcountedSpiRecord.getResource();
int spi = spis[direction].getSpi();
try {
mSrvConfig
@@ -961,8 +1168,12 @@
}
}
// Both SAs were created successfully, time to construct a record and lock it away
- mTransformRecords.put(
- resourceId, new TransformRecord(resourceId, binder, c, spis, socketRecord));
+ userRecord.mTransformRecords.put(
+ resourceId,
+ new RefcountedResource<TransformRecord>(
+ new TransformRecord(resourceId, c, spis, socketRecord),
+ binder,
+ dependencies.toArray(new RefcountedResource[dependencies.size()])));
return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId);
}
@@ -973,8 +1184,9 @@
* other reasons.
*/
@Override
- public void deleteTransportModeTransform(int resourceId) throws RemoteException {
- releaseManagedResource(mTransformRecords, resourceId, "IpSecTransform");
+ public synchronized void deleteTransportModeTransform(int resourceId) throws RemoteException {
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
+ releaseResource(userRecord.mTransformRecords, resourceId);
}
/**
@@ -984,14 +1196,10 @@
@Override
public synchronized void applyTransportModeTransform(
ParcelFileDescriptor socket, int resourceId) throws RemoteException {
- // Synchronize liberally here because we are using ManagedResources in this block
- TransformRecord info;
- // FIXME: this code should be factored out into a security check + getter
- info = mTransformRecords.getAndCheckOwner(resourceId);
+ UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
- if (info == null) {
- throw new IllegalArgumentException("Transform " + resourceId + " is not active");
- }
+ // Get transform record; if no transform is found, will throw IllegalArgumentException
+ TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
// TODO: make this a function.
if (info.pid != getCallingPid() || info.uid != getCallingUid()) {
@@ -1023,7 +1231,7 @@
* used: reserved for future improved input validation.
*/
@Override
- public void removeTransportModeTransform(ParcelFileDescriptor socket, int resourceId)
+ public synchronized void removeTransportModeTransform(ParcelFileDescriptor socket, int resourceId)
throws RemoteException {
try {
mSrvConfig
@@ -1042,13 +1250,7 @@
pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
pw.println();
- pw.println("mUserQuotaTracker:");
- pw.println(mUserQuotaTracker);
- pw.println("mTransformRecords:");
- pw.println(mTransformRecords);
- pw.println("mUdpSocketRecords:");
- pw.println(mUdpSocketRecords);
- pw.println("mSpiRecords:");
- pw.println(mSpiRecords);
+ pw.println("mUserResourceTracker:");
+ pw.println(mUserResourceTracker);
}
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index bdfccd6e..0a86281 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.util.ArrayMap;
import android.util.ArraySet;
+
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
@@ -147,7 +148,7 @@
private static final long HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000;
private static final int FOREGROUND_IMPORTANCE_CUTOFF
- = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+ = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
// default background throttling interval if not overriden in settings
private static final long DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;
@@ -239,7 +240,7 @@
// current active user on the device - other users are denied location data
private int mCurrentUserId = UserHandle.USER_SYSTEM;
- private int[] mCurrentUserProfiles = new int[] { UserHandle.USER_SYSTEM };
+ private int[] mCurrentUserProfiles = new int[]{UserHandle.USER_SYSTEM};
private GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
@@ -253,7 +254,7 @@
public LocationManagerService(Context context) {
super();
mContext = context;
- mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
+ mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
// Let the package manager query which are the default location
// providers as they get certain permissions granted by default.
@@ -370,18 +371,18 @@
}
}, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(
- Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST),
- true,
- new ContentObserver(mLocationHandler) {
- @Override
- public void onChange(boolean selfChange) {
- synchronized (mLock) {
- updateBackgroundThrottlingWhitelistLocked();
- updateProvidersLocked();
+ Settings.Global.getUriFor(
+ Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST),
+ true,
+ new ContentObserver(mLocationHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mLock) {
+ updateBackgroundThrottlingWhitelistLocked();
+ updateProvidersLocked();
+ }
}
- }
- }, UserHandle.USER_ALL);
+ }, UserHandle.USER_ALL);
mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true);
// listen for user change
@@ -402,7 +403,7 @@
updateUserProfiles(mCurrentUserId);
} else if (Intent.ACTION_SHUTDOWN.equals(action)) {
// shutdown only if UserId indicates whole system, not just one user
- if(D) Log.d(TAG, "Shutdown received with UserId: " + getSendingUserId());
+ if (D) Log.d(TAG, "Shutdown received with UserId: " + getSendingUserId());
if (getSendingUserId() == UserHandle.USER_ALL) {
shutdownComponents();
}
@@ -416,13 +417,15 @@
HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
synchronized (mLock) {
for (Entry<String, ArrayList<UpdateRecord>> entry
- : mRecordsByProvider.entrySet()) {
+ : mRecordsByProvider.entrySet()) {
String provider = entry.getKey();
for (UpdateRecord record : entry.getValue()) {
if (record.mReceiver.mIdentity.mUid == uid
- && record.mIsForegroundUid != foreground) {
- if (D) Log.d(TAG, "request from uid " + uid + " is now "
- + (foreground ? "foreground" : "background)"));
+ && record.mIsForegroundUid != foreground) {
+ if (D) {
+ Log.d(TAG, "request from uid " + uid + " is now "
+ + (foreground ? "foreground" : "background)"));
+ }
record.mIsForegroundUid = foreground;
if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
@@ -436,10 +439,12 @@
}
for (Entry<IGnssMeasurementsListener, Identity> entry
- : mGnssMeasurementsListeners.entrySet()) {
+ : mGnssMeasurementsListeners.entrySet()) {
if (entry.getValue().mUid == uid) {
- if (D) Log.d(TAG, "gnss measurements listener from uid " + uid
- + " is now " + (foreground ? "foreground" : "background)"));
+ if (D) {
+ Log.d(TAG, "gnss measurements listener from uid " + uid
+ + " is now " + (foreground ? "foreground" : "background)"));
+ }
if (foreground || isThrottlingExemptLocked(entry.getValue())) {
mGnssMeasurementsProvider.addListener(entry.getKey());
} else {
@@ -449,11 +454,13 @@
}
for (Entry<IGnssNavigationMessageListener, Identity> entry
- : mGnssNavigationMessageListeners.entrySet()) {
+ : mGnssNavigationMessageListeners.entrySet()) {
if (entry.getValue().mUid == uid) {
- if (D) Log.d(TAG, "gnss navigation message listener from uid "
- + uid + " is now "
- + (foreground ? "foreground" : "background)"));
+ if (D) {
+ Log.d(TAG, "gnss navigation message listener from uid "
+ + uid + " is now "
+ + (foreground ? "foreground" : "background)"));
+ }
if (foreground || isThrottlingExemptLocked(entry.getValue())) {
mGnssNavigationMessageProvider.addListener(entry.getKey());
} else {
@@ -477,7 +484,7 @@
* support for components that do not wish to handle such event.
*/
private void shutdownComponents() {
- if(D) Log.d(TAG, "Shutting down components...");
+ if (D) Log.d(TAG, "Shutting down components...");
LocationProviderInterface gpsProvider = mProvidersByName.get(LocationManager.GPS_PROVIDER);
if (gpsProvider != null && gpsProvider.isEnabled()) {
@@ -563,8 +570,10 @@
// as a proxy for coreApp="true"
if (pm.checkSignatures(systemPackageName, packageName)
!= PackageManager.SIGNATURE_MATCH) {
- if (D) Log.d(TAG, "Fallback candidate not signed the same as system: "
- + packageName);
+ if (D) {
+ Log.d(TAG, "Fallback candidate not signed the same as system: "
+ + packageName);
+ }
continue;
}
@@ -622,8 +631,10 @@
ArrayList<String> providerPackageNames = new ArrayList<>();
String[] pkgs = resources.getStringArray(
com.android.internal.R.array.config_locationProviderPackageNames);
- if (D) Log.d(TAG, "certificates for location providers pulled from: " +
- Arrays.toString(pkgs));
+ if (D) {
+ Log.d(TAG, "certificates for location providers pulled from: " +
+ Arrays.toString(pkgs));
+ }
if (pkgs != null) providerPackageNames.addAll(Arrays.asList(pkgs));
ensureFallbackFusedProviderPresentLocked(providerPackageNames);
@@ -642,7 +653,7 @@
mProxyProviders.add(networkProvider);
addProviderLocked(networkProvider);
} else {
- Slog.w(TAG, "no network location provider found");
+ Slog.w(TAG, "no network location provider found");
}
// bind to fused provider
@@ -671,7 +682,7 @@
com.android.internal.R.array.config_locationProviderPackageNames,
mLocationHandler);
if (mGeocodeProvider == null) {
- Slog.e(TAG, "no geocoder provider found");
+ Slog.e(TAG, "no geocoder provider found");
}
// bind to fused hardware provider if supported
@@ -697,14 +708,14 @@
// bind to geofence provider
GeofenceProxy provider = GeofenceProxy.createAndBind(
- mContext,com.android.internal.R.bool.config_enableGeofenceOverlay,
+ mContext, com.android.internal.R.bool.config_enableGeofenceOverlay,
com.android.internal.R.string.config_geofenceProviderPackageName,
com.android.internal.R.array.config_locationProviderPackageNames,
mLocationHandler,
mGpsGeofenceProxy,
flpHardwareProvider != null ? flpHardwareProvider.getGeofenceHardware() : null);
if (provider == null) {
- Slog.d(TAG, "Unable to bind FLP Geofence proxy.");
+ Slog.d(TAG, "Unable to bind FLP Geofence proxy.");
}
// bind to hardware activity recognition
@@ -751,6 +762,7 @@
/**
* Called when the device's active user changes.
+ *
* @param userId the new active user's UserId
*/
private void switchUser(int userId) {
@@ -797,7 +809,7 @@
final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver.
final Object mKey;
- final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<>();
+ final HashMap<String, UpdateRecord> mUpdateRecords = new HashMap<>();
// True if app ops has started monitoring this receiver for locations.
boolean mOpMonitoring;
@@ -914,9 +926,9 @@
/**
* Update AppOps monitoring for a single location request and op type.
*
- * @param allowMonitoring True if monitoring is allowed for this request/op.
+ * @param allowMonitoring True if monitoring is allowed for this request/op.
* @param currentlyMonitoring True if AppOps is currently monitoring this request/op.
- * @param op AppOps code for the op to update.
+ * @param op AppOps code for the op to update.
* @return True if monitoring is on for this request/op after updating.
*/
private boolean updateMonitoring(boolean allowMonitoring, boolean currentlyMonitoring,
@@ -1004,7 +1016,8 @@
}
} else {
Intent locationChanged = new Intent();
- locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, new Location(location));
+ locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED,
+ new Location(location));
try {
synchronized (this) {
// synchronize to ensure incrementPendingBroadcastsLocked()
@@ -1286,7 +1299,7 @@
}
if (mGnssBatchingProvider != null) {
- mGnssBatchingProvider.flush();
+ mGnssBatchingProvider.flush();
}
}
@@ -1301,7 +1314,7 @@
if (mGnssBatchingProvider != null) {
mGnssBatchingInProgress = false;
return mGnssBatchingProvider.stop();
- } else {
+ } else {
return false;
}
}
@@ -1363,7 +1376,7 @@
* processes belonging to background users.
*
* @param provider the name of the location provider
- * @param uid the requestor's UID
+ * @param uid the requestor's UID
*/
private boolean isAllowedByUserSettingsLocked(String provider, int uid) {
if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
@@ -1467,7 +1480,7 @@
* location provider.
*
* @param allowedResolutionLevel resolution level allowed to caller
- * @param providerName the name of the location provider
+ * @param providerName the name of the location provider
*/
private void checkResolutionLevelIsSufficientForProviderUse(int allowedResolutionLevel,
String providerName) {
@@ -1718,7 +1731,9 @@
resolver,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
+ // initialize the low power mode to true and set to false if any of the records requires
+ providerRequest.lowPowerMode = true;
if (records != null) {
for (UpdateRecord record : records) {
if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
@@ -1742,6 +1757,9 @@
record.mRequest = locationRequest;
providerRequest.locationRequests.add(locationRequest);
+ if (!locationRequest.isLowPowerMode()) {
+ providerRequest.lowPowerMode = false;
+ }
if (interval < providerRequest.interval) {
providerRequest.reportLocation = true;
providerRequest.interval = interval;
@@ -1794,23 +1812,23 @@
public String[] getBackgroundThrottlingWhitelist() {
synchronized (mLock) {
return mBackgroundThrottlePackageWhitelist.toArray(
- new String[mBackgroundThrottlePackageWhitelist.size()]);
+ new String[mBackgroundThrottlePackageWhitelist.size()]);
}
}
private void updateBackgroundThrottlingWhitelistLocked() {
String setting = Settings.Global.getString(
- mContext.getContentResolver(),
- Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
+ mContext.getContentResolver(),
+ Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
if (setting == null) {
setting = "";
}
mBackgroundThrottlePackageWhitelist.clear();
mBackgroundThrottlePackageWhitelist.addAll(
- SystemConfig.getInstance().getAllowUnthrottledLocation());
+ SystemConfig.getInstance().getAllowUnthrottledLocation());
mBackgroundThrottlePackageWhitelist.addAll(
- Arrays.asList(setting.split(",")));
+ Arrays.asList(setting.split(",")));
}
private boolean isThrottlingExemptLocked(Identity identity) {
@@ -1894,7 +1912,8 @@
@Override
public String toString() {
return "UpdateRecord[" + mProvider + " " + mReceiver.mIdentity.mPackageName
- + "(" + mReceiver.mIdentity.mUid + (mIsForegroundUid ? " foreground" : " background")
+ + "(" + mReceiver.mIdentity.mUid + (mIsForegroundUid ? " foreground"
+ : " background")
+ ")" + " " + mRealRequest + "]";
}
}
@@ -1936,8 +1955,13 @@
* @return a version of request that meets the given resolution and consistency requirements
* @hide
*/
- private LocationRequest createSanitizedRequest(LocationRequest request, int resolutionLevel) {
+ private LocationRequest createSanitizedRequest(LocationRequest request, int resolutionLevel,
+ boolean callerHasLocationHardwarePermission) {
LocationRequest sanitizedRequest = new LocationRequest(request);
+ if (!callerHasLocationHardwarePermission) {
+ // allow setting low power mode only for callers with location hardware permission
+ sanitizedRequest.setLowPowerMode(false);
+ }
if (resolutionLevel < RESOLUTION_LEVEL_FINE) {
switch (sanitizedRequest.getQuality()) {
case LocationRequest.ACCURACY_FINE:
@@ -2013,7 +2037,11 @@
if (hideFromAppOps) {
checkUpdateAppOpsAllowed();
}
- LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel);
+ boolean callerHasLocationHardwarePermission =
+ mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ == PackageManager.PERMISSION_GRANTED;
+ LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel,
+ callerHasLocationHardwarePermission);
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -2050,11 +2078,13 @@
}
UpdateRecord record = new UpdateRecord(name, request, receiver);
- if (D) Log.d(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver))
- + " " + name + " " + request + " from " + packageName + "(" + uid + " "
- + (record.mIsForegroundUid ? "foreground" : "background")
- + (isThrottlingExemptLocked(receiver.mIdentity)
+ if (D) {
+ Log.d(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver))
+ + " " + name + " " + request + " from " + packageName + "(" + uid + " "
+ + (record.mIsForegroundUid ? "foreground" : "background")
+ + (isThrottlingExemptLocked(receiver.mIdentity)
? " [whitelisted]" : "") + ")");
+ }
UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
if (oldRecord != null) {
@@ -2159,14 +2189,18 @@
final long identity = Binder.clearCallingIdentity();
try {
if (mBlacklist.isBlacklisted(packageName)) {
- if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
- packageName);
+ if (D) {
+ Log.d(TAG, "not returning last loc for blacklisted app: " +
+ packageName);
+ }
return null;
}
if (!reportLocationAccessNoThrow(pid, uid, packageName, allowedResolutionLevel)) {
- if (D) Log.d(TAG, "not returning last loc for no op app: " +
- packageName);
+ if (D) {
+ Log.d(TAG, "not returning last loc for no op app: " +
+ packageName);
+ }
return null;
}
@@ -2192,7 +2226,8 @@
return null;
}
if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
- Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
+ Location noGPSLocation = location.getExtraLocation(
+ Location.EXTRA_NO_GPS_LOCATION);
if (noGPSLocation != null) {
return new Location(mLocationFudger.getOrCreate(noGPSLocation));
}
@@ -2216,7 +2251,12 @@
checkPackageName(packageName);
checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
request.getProvider());
- LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel);
+ // Require that caller can manage given document
+ boolean callerHasLocationHardwarePermission =
+ mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ == PackageManager.PERMISSION_GRANTED;
+ LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel,
+ callerHasLocationHardwarePermission);
if (D) Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent);
@@ -2282,8 +2322,7 @@
@Override
public boolean addGnssMeasurementsListener(
- IGnssMeasurementsListener listener,
- String packageName) {
+ IGnssMeasurementsListener listener, String packageName) {
if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
return false;
}
@@ -2296,7 +2335,7 @@
try {
if (isThrottlingExemptLocked(callerIdentity)
|| isImportanceForeground(
- mActivityManager.getPackageImportance(packageName))) {
+ mActivityManager.getPackageImportance(packageName))) {
return mGnssMeasurementsProvider.addListener(listener);
}
} finally {
@@ -2327,13 +2366,13 @@
synchronized (mLock) {
Identity callerIdentity
- = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+ = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
mGnssNavigationMessageListeners.put(listener, callerIdentity);
long identity = Binder.clearCallingIdentity();
try {
if (isThrottlingExemptLocked(callerIdentity)
|| isImportanceForeground(
- mActivityManager.getPackageImportance(packageName))) {
+ mActivityManager.getPackageImportance(packageName))) {
return mGnssNavigationMessageProvider.addListener(listener);
}
} finally {
@@ -2394,7 +2433,7 @@
/**
* @return null if the provider does not exist
* @throws SecurityException if the provider is not allowed to be
- * accessed by the caller
+ * accessed by the caller
*/
@Override
public ProviderProperties getProviderProperties(String provider) {
@@ -2417,7 +2456,7 @@
/**
* @return null if the provider does not exist
* @throws SecurityException if the provider is not allowed to be
- * accessed by the caller
+ * accessed by the caller
*/
@Override
public String getNetworkProviderPackage() {
@@ -2641,8 +2680,10 @@
}
if (mBlacklist.isBlacklisted(receiver.mIdentity.mPackageName)) {
- if (D) Log.d(TAG, "skipping loc update for blacklisted app: " +
- receiver.mIdentity.mPackageName);
+ if (D) {
+ Log.d(TAG, "skipping loc update for blacklisted app: " +
+ receiver.mIdentity.mPackageName);
+ }
continue;
}
@@ -2651,8 +2692,10 @@
receiver.mIdentity.mUid,
receiver.mIdentity.mPackageName,
receiver.mAllowedResolutionLevel)) {
- if (D) Log.d(TAG, "skipping loc update for no op app: " +
- receiver.mIdentity.mPackageName);
+ if (D) {
+ Log.d(TAG, "skipping loc update for no op app: " +
+ receiver.mIdentity.mPackageName);
+ }
continue;
}
@@ -3114,12 +3157,12 @@
}
pw.append(" fudger: ");
- mLocationFudger.dump(fd, pw, args);
+ mLocationFudger.dump(fd, pw, args);
if (args.length > 0 && "short".equals(args[0])) {
return;
}
- for (LocationProviderInterface provider: mProviders) {
+ for (LocationProviderInterface provider : mProviders) {
pw.print(provider.getName() + " Internal State");
if (provider instanceof LocationProviderProxy) {
LocationProviderProxy proxy = (LocationProviderProxy) provider;
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 296fb32..a2c0506 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,13 +1,19 @@
# 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
# Vibrator
per-file VibratorService.java=michaelwr@google.com
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7f0b508..6a0d3ff 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -31,8 +31,11 @@
import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerInternal.ScreenObserver;
import android.app.AppOpsManager;
import android.app.IActivityManager;
+import android.app.KeyguardManager;
import android.app.usage.StorageStatsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -160,7 +163,8 @@
* watch for and manage dynamically added storage, such as SD cards and USB mass
* storage. Also decides how storage should be presented to users on the device.
*/
-class StorageManagerService extends IStorageManager.Stub implements Watchdog.Monitor {
+class StorageManagerService extends IStorageManager.Stub
+ implements Watchdog.Monitor, ScreenObserver {
// Static direct instance pointer for the tightly-coupled idle service to use
static StorageManagerService sSelf = null;
@@ -402,6 +406,7 @@
private volatile boolean mSystemReady = false;
private volatile boolean mBootCompleted = false;
private volatile boolean mDaemonConnected = false;
+ private volatile boolean mSecureKeyguardShowing = true;
private PackageManagerService mPms;
@@ -827,6 +832,7 @@
mVold.onUserStarted(userId);
mStoraged.onUserStarted(userId);
}
+ mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -878,6 +884,24 @@
}
}
+ @Override
+ public void onAwakeStateChanged(boolean isAwake) {
+ // Ignored
+ }
+
+ @Override
+ public void onKeyguardStateChanged(boolean isShowing) {
+ // Push down current secure keyguard status so that we ignore malicious
+ // USB devices while locked.
+ mSecureKeyguardShowing = isShowing
+ && mContext.getSystemService(KeyguardManager.class).isDeviceSecure();
+ try {
+ mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ }
+ }
+
void runIdleMaintenance(Runnable callback) {
mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
}
@@ -1414,6 +1438,9 @@
}
private void systemReady() {
+ LocalServices.getService(ActivityManagerInternal.class)
+ .registerScreenObserver(this);
+
mSystemReady = true;
mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
}
@@ -2691,15 +2718,14 @@
final boolean primary = true;
final boolean removable = primaryPhysical;
final boolean emulated = !primaryPhysical;
- final long mtpReserveSize = 0L;
final boolean allowMassStorage = false;
final long maxFileSize = 0L;
final UserHandle owner = new UserHandle(userId);
final String uuid = null;
final String state = Environment.MEDIA_REMOVED;
- res.add(0, new StorageVolume(id, StorageVolume.STORAGE_ID_INVALID, path,
- description, primary, removable, emulated, mtpReserveSize,
+ res.add(0, new StorageVolume(id, path,
+ description, primary, removable, emulated,
allowMassStorage, maxFileSize, owner, uuid, state));
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 0d4f5cb..31aea63 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1406,7 +1406,7 @@
mLocalUnlockedUsers.put(userId, true);
}
if (userId < 1) return;
- syncSharedAccounts(userId);
+ mHandler.post(() -> syncSharedAccounts(userId));
}
private void syncSharedAccounts(int userId) {
@@ -2118,13 +2118,14 @@
userId));
}
/*
- * Only the system or authenticator should be allowed to remove accounts for that
- * authenticator. This will let users remove accounts (via Settings in the system) but not
- * arbitrary applications (like competing authenticators).
+ * Only the system, authenticator or profile owner should be allowed to remove accounts for
+ * that authenticator. This will let users remove accounts (via Settings in the system) but
+ * not arbitrary applications (like competing authenticators).
*/
UserHandle user = UserHandle.of(userId);
if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
- && !isSystemUid(callingUid)) {
+ && !isSystemUid(callingUid)
+ && !isProfileOwner(callingUid)) {
String msg = String.format(
"uid %s cannot remove accounts of type: %s",
callingUid,
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 3cd2f6a..088ddea 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2640,7 +2640,7 @@
try {
bumpServiceExecutingLocked(s, false, "unbind");
if (b.client != s.app && (c.flags&Context.BIND_WAIVE_PRIORITY) == 0
- && s.app.setProcState <= ActivityManager.PROCESS_STATE_RECEIVER) {
+ && s.app.setProcState <= ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
// If this service's process is not already in the cached list,
// then update it in the LRU list here because this may be causing
// it to go down there and we want it to start out near the top.
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 9bfdd0c..af5cf1e 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -409,15 +409,16 @@
otherStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
}
} finally {
- if (mHomeStack != null && !isTopStack(mHomeStack)) {
+ final ActivityStack topFullscreenStack =
+ getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ if (topFullscreenStack != null && mHomeStack != null && !isTopStack(mHomeStack)) {
// Whenever split-screen is dismissed we want the home stack directly behind the
- // currently top stack so it shows up when the top stack is finished.
- final ActivityStack topStack = getTopStack();
+ // current top fullscreen stack so it shows up when the top stack is finished.
// TODO: Would be better to use ActivityDisplay.positionChildAt() for this, however
// ActivityDisplay doesn't have a direct controller to WM side yet. We can switch
// once we have that.
mHomeStack.moveToFront("onSplitScreenModeDismissed");
- topStack.moveToFront("onSplitScreenModeDismissed");
+ topFullscreenStack.moveToFront("onSplitScreenModeDismissed");
}
mSupervisor.mWindowManager.continueSurfaceLayout();
}
@@ -435,7 +436,7 @@
}
otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
false /* animate */, false /* showRecents */,
- false /* sendNonResizeableNotification */);
+ true /* enteringSplitScreenMode */);
}
} finally {
mSupervisor.mWindowManager.continueSurfaceLayout();
@@ -555,6 +556,16 @@
return stack == getTopStack();
}
+ boolean isTopFullscreenStack(ActivityStack stack) {
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack current = mStacks.get(i);
+ if (current.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ return current == stack;
+ }
+ }
+ return false;
+ }
+
int getIndexOf(ActivityStack stack) {
return mStacks.indexOf(stack);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index b52db87..b3a596c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -66,6 +66,7 @@
static final String KEY_BG_START_TIMEOUT = "service_bg_start_timeout";
static final String KEY_BOUND_SERVICE_CRASH_RESTART_DURATION = "service_crash_restart_duration";
static final String KEY_BOUND_SERVICE_CRASH_MAX_RETRY = "service_crash_max_retry";
+ static final String KEY_PROCESS_START_ASYNC = "process_start_async";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
@@ -93,6 +94,7 @@
private static final long DEFAULT_BG_START_TIMEOUT = 15*1000;
private static final long DEFAULT_BOUND_SERVICE_CRASH_RESTART_DURATION = 30*60_000;
private static final int DEFAULT_BOUND_SERVICE_CRASH_MAX_RETRY = 16;
+ private static final boolean DEFAULT_PROCESS_START_ASYNC = true;
// Maximum number of cached processes we will allow.
@@ -202,6 +204,9 @@
// Maximum number of retries for bound foreground services that crash soon after start
public long BOUND_SERVICE_MAX_CRASH_RETRY = DEFAULT_BOUND_SERVICE_CRASH_MAX_RETRY;
+ // Indicates if the processes need to be started asynchronously.
+ public boolean FLAG_PROCESS_START_ASYNC = DEFAULT_PROCESS_START_ASYNC;
+
private final ActivityManagerService mService;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -325,6 +330,8 @@
DEFAULT_BOUND_SERVICE_CRASH_RESTART_DURATION);
BOUND_SERVICE_MAX_CRASH_RETRY = mParser.getInt(KEY_BOUND_SERVICE_CRASH_MAX_RETRY,
DEFAULT_BOUND_SERVICE_CRASH_MAX_RETRY);
+ FLAG_PROCESS_START_ASYNC = mParser.getBoolean(KEY_PROCESS_START_ASYNC,
+ DEFAULT_PROCESS_START_ASYNC);
updateMaxCachedProcesses();
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5d6cf74..7dfde56 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -34,6 +34,7 @@
import static android.app.ActivityManagerInternal.ASSIST_KEY_DATA;
import static android.app.ActivityManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
import static android.app.ActivityManagerInternal.ASSIST_KEY_STRUCTURE;
+import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
import static android.app.AppOpsManager.OP_NONE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -210,6 +211,7 @@
import android.app.ActivityManager.StackInfo;
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerInternal.ScreenObserver;
import android.app.ActivityManagerInternal.SleepToken;
import android.app.ActivityOptions;
import android.app.ActivityThread;
@@ -250,7 +252,6 @@
import android.app.backup.IBackupManager;
import android.app.servertransaction.ConfigurationChangeItem;
import android.app.usage.UsageEvents;
-import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
import android.content.ActivityNotFoundException;
@@ -353,6 +354,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.LongSparseArray;
import android.util.StatsLog;
import android.util.TimingsTraceLog;
import android.util.DebugUtils;
@@ -389,6 +391,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.BinderInternal;
+import com.android.internal.os.ByteTransferPipe;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TransferPipe;
@@ -401,6 +404,7 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;
+import com.android.server.AlarmManagerInternal;
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
import com.android.server.DeviceIdleController;
@@ -471,6 +475,7 @@
import java.util.concurrent.atomic.AtomicLong;
import dalvik.system.VMRuntime;
+
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
@@ -628,7 +633,13 @@
/** All system services */
SystemServiceManager mSystemServiceManager;
- AssistUtils mAssistUtils;
+
+ // Wrapper around VoiceInteractionServiceManager
+ private AssistUtils mAssistUtils;
+
+ // Keeps track of the active voice interaction service component, notified from
+ // VoiceInteractionManagerService
+ ComponentName mActiveVoiceInteractionServiceComponent;
private Installer mInstaller;
@@ -1588,6 +1599,20 @@
@VisibleForTesting
long mProcStateSeqCounter = 0;
+ /**
+ * A global counter for generating sequence numbers to uniquely identify pending process starts.
+ */
+ @GuardedBy("this")
+ private long mProcStartSeqCounter = 0;
+
+ /**
+ * Contains {@link ProcessRecord} objects for pending process starts.
+ *
+ * Mapping: {@link #mProcStartSeqCounter} -> {@link ProcessRecord}
+ */
+ @GuardedBy("this")
+ private final LongSparseArray<ProcessRecord> mPendingStarts = new LongSparseArray<>();
+
private final Injector mInjector;
static final class ProcessChangeItem {
@@ -1620,6 +1645,8 @@
}
}
+ final List<ScreenObserver> mScreenObservers = new ArrayList<>();
+
final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
@@ -1742,13 +1769,13 @@
static final int LOG_STACK_STATE = 60;
static final int VR_MODE_CHANGE_MSG = 61;
static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63;
- static final int NOTIFY_VR_SLEEPING_MSG = 65;
+ static final int DISPATCH_SCREEN_AWAKE_MSG = 64;
+ static final int DISPATCH_SCREEN_KEYGUARD_MSG = 65;
static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66;
static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67;
static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
- static final int NOTIFY_VR_KEYGUARD_MSG = 74;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1775,6 +1802,8 @@
final ServiceThread mHandlerThread;
final MainHandler mHandler;
final Handler mUiHandler;
+ final ServiceThread mProcStartHandlerThread;
+ final Handler mProcStartHandler;
final ActivityManagerConstants mConstants;
@@ -2384,11 +2413,17 @@
}
}
} break;
- case NOTIFY_VR_SLEEPING_MSG: {
- notifyVrManagerOfSleepState(msg.arg1 != 0);
+ case DISPATCH_SCREEN_AWAKE_MSG: {
+ final boolean isAwake = msg.arg1 != 0;
+ for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+ mScreenObservers.get(i).onAwakeStateChanged(isAwake);
+ }
} break;
- case NOTIFY_VR_KEYGUARD_MSG: {
- notifyVrManagerOfKeyguardState(msg.arg1 != 0);
+ case DISPATCH_SCREEN_KEYGUARD_MSG: {
+ final boolean isShowing = msg.arg1 != 0;
+ for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+ mScreenObservers.get(i).onKeyguardStateChanged(isShowing);
+ }
} break;
case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
synchronized (ActivityManagerService.this) {
@@ -2701,6 +2736,8 @@
mVrController = null;
mLockTaskController = null;
mLifecycleManager = null;
+ mProcStartHandlerThread = null;
+ mProcStartHandler = null;
}
// Note: This method is invoked on the main thread but may need to attach various
@@ -2725,6 +2762,11 @@
mHandler = new MainHandler(mHandlerThread.getLooper());
mUiHandler = mInjector.getUiHandler(this);
+ mProcStartHandlerThread = new ServiceThread(TAG + ":procStart",
+ THREAD_PRIORITY_FOREGROUND, false /* allowIo */);
+ mProcStartHandlerThread.start();
+ mProcStartHandler = new Handler(mProcStartHandlerThread.getLooper());
+
mConstants = new ActivityManagerConstants(this, mHandler);
/* static; one-time init here */
@@ -2940,9 +2982,11 @@
try {
return super.onTransact(code, data, reply, flags);
} catch (RuntimeException e) {
- // The activity manager only throws security exceptions, so let's
+ // The activity manager only throws certain exceptions intentionally, so let's
// log all others.
- if (!(e instanceof SecurityException)) {
+ if (!(e instanceof SecurityException
+ || e instanceof IllegalArgumentException
+ || e instanceof IllegalStateException)) {
Slog.wtf(TAG, "Activity Manager Crash."
+ " UID:" + Binder.getCallingUid()
+ " PID:" + Binder.getCallingPid()
@@ -3285,32 +3329,6 @@
mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
}
- private void sendNotifyVrManagerOfSleepState(boolean isSleeping) {
- mHandler.sendMessage(
- mHandler.obtainMessage(NOTIFY_VR_SLEEPING_MSG, isSleeping ? 1 : 0, 0));
- }
-
- private void notifyVrManagerOfSleepState(boolean isSleeping) {
- final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
- if (vrService == null) {
- return;
- }
- vrService.onSleepStateChanged(isSleeping);
- }
-
- private void sendNotifyVrManagerOfKeyguardState(boolean isShowing) {
- mHandler.sendMessage(
- mHandler.obtainMessage(NOTIFY_VR_KEYGUARD_MSG, isShowing ? 1 : 0, 0));
- }
-
- private void notifyVrManagerOfKeyguardState(boolean isShowing) {
- final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
- if (vrService == null) {
- return;
- }
- vrService.onKeyguardStateChanged(isShowing);
- }
-
final void showAskCompatModeDialogLocked(ActivityRecord r) {
Message msg = Message.obtain();
msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
@@ -3384,8 +3402,12 @@
if (lrui >= 0) {
if (!app.killed) {
Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
- killProcessQuiet(app.pid);
- killProcessGroup(app.uid, app.pid);
+ if (app.pid > 0) {
+ killProcessQuiet(app.pid);
+ killProcessGroup(app.uid, app.pid);
+ } else {
+ app.pendingStart = false;
+ }
}
if (lrui <= mLruProcessActivityStart) {
mLruProcessActivityStart--;
@@ -3646,7 +3668,7 @@
|| transit == TRANSIT_TASK_TO_FRONT;
}
- int startIsolatedProcess(String entryPoint, String[] entryPointArgs,
+ boolean startIsolatedProcess(String entryPoint, String[] entryPointArgs,
String processName, String abiOverride, int uid, Runnable crashHandler) {
synchronized(this) {
ApplicationInfo info = new ApplicationInfo();
@@ -3668,7 +3690,7 @@
null /* hostingName */, true /* allowWhileBooting */, true /* isolated */,
uid, true /* keepIfLarge */, abiOverride, entryPoint, entryPointArgs,
crashHandler);
- return proc != null ? proc.pid : 0;
+ return proc != null;
}
}
@@ -3789,9 +3811,9 @@
}
checkTime(startTime, "startProcess: stepping in to startProcess");
- startProcessLocked(app, hostingType, hostingNameStr, abiOverride);
+ final boolean success = startProcessLocked(app, hostingType, hostingNameStr, abiOverride);
checkTime(startTime, "startProcess: done starting proc!");
- return (app.pid != 0) ? app : null;
+ return success ? app : null;
}
boolean isAllowedWhileBooting(ApplicationInfo ai) {
@@ -3803,8 +3825,14 @@
startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */);
}
- private final void startProcessLocked(ProcessRecord app, String hostingType,
+ /**
+ * @return {@code true} if process start is successful, false otherwise.
+ */
+ private final boolean startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride) {
+ if (app.pendingStart) {
+ return true;
+ }
long startTime = SystemClock.elapsedRealtime();
if (app.pid > 0 && app.pid != MY_PID) {
checkTime(startTime, "startProcess: removing from pids map");
@@ -3960,90 +3988,10 @@
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
final String entryPoint = "android.app.ActivityThread";
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
- app.processName);
- checkTime(startTime, "startProcess: asking zygote to start proc");
- ProcessStartResult startResult;
- if (hostingType.equals("webview_service")) {
- startResult = startWebView(entryPoint,
- app.processName, uid, uid, gids, runtimeFlags, mountExternal,
- app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
- app.info.dataDir, null, null);
- } else {
- startResult = Process.start(entryPoint,
- app.processName, uid, uid, gids, runtimeFlags, mountExternal,
- app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
- app.info.dataDir, invokeWith, null);
- }
- checkTime(startTime, "startProcess: returned from zygote!");
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
- checkTime(startTime, "startProcess: done updating battery stats");
-
- EventLog.writeEvent(EventLogTags.AM_PROC_START,
- UserHandle.getUserId(uid), startResult.pid, uid,
- app.processName, hostingType,
- hostingNameStr != null ? hostingNameStr : "");
-
- try {
- AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
- seInfo, app.info.sourceDir, startResult.pid);
- } catch (RemoteException ex) {
- // Ignore
- }
-
- if (app.persistent) {
- Watchdog.getInstance().processStarted(app.processName, startResult.pid);
- }
-
- checkTime(startTime, "startProcess: building log message");
- StringBuilder buf = mStringBuilder;
- buf.setLength(0);
- buf.append("Start proc ");
- buf.append(startResult.pid);
- buf.append(':');
- buf.append(app.processName);
- buf.append('/');
- UserHandle.formatUid(buf, uid);
- if (app.isolatedEntryPoint != null) {
- buf.append(" [");
- buf.append(app.isolatedEntryPoint);
- buf.append("]");
- }
- buf.append(" for ");
- buf.append(hostingType);
- if (hostingNameStr != null) {
- buf.append(" ");
- buf.append(hostingNameStr);
- }
- Slog.i(TAG, buf.toString());
- app.setPid(startResult.pid);
- app.usingWrapper = startResult.usingWrapper;
- app.removed = false;
- app.killed = false;
- app.killedByAm = false;
- checkTime(startTime, "startProcess: starting to update pids map");
- ProcessRecord oldApp;
- synchronized (mPidsSelfLocked) {
- oldApp = mPidsSelfLocked.get(startResult.pid);
- }
- // If there is already an app occupying that pid that hasn't been cleaned up
- if (oldApp != null && !app.isolated) {
- // Clean up anything relating to this pid first
- Slog.w(TAG, "Reusing pid " + startResult.pid
- + " while app is still mapped to it");
- cleanUpApplicationRecordLocked(oldApp, false, false, -1,
- true /*replacingPid*/);
- }
- synchronized (mPidsSelfLocked) {
- this.mPidsSelfLocked.put(startResult.pid, app);
- Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
- msg.obj = app;
- mHandler.sendMessageDelayed(msg, startResult.usingWrapper
- ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
- }
- checkTime(startTime, "startProcess: done updating pids map");
+ return startProcessLocked(hostingType, hostingNameStr, entryPoint, app, uid, gids,
+ runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
+ startTime);
} catch (RuntimeException e) {
Slog.e(TAG, "Failure starting process " + app.processName, e);
@@ -4055,9 +4003,222 @@
// it doesn't hurt to use it again.)
forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid), false,
false, true, false, false, UserHandle.getUserId(app.userId), "start failure");
+ return false;
}
}
+ @GuardedBy("this")
+ private boolean startProcessLocked(String hostingType, String hostingNameStr, String entryPoint,
+ ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
+ String seInfo, String requiredAbi, String instructionSet, String invokeWith,
+ long startTime) {
+ app.pendingStart = true;
+ app.killedByAm = false;
+ app.removed = false;
+ app.killed = false;
+ final long startSeq = app.startSeq = ++mProcStartSeqCounter;
+ app.setStartParams(uid, hostingType, hostingNameStr, seInfo, startTime);
+ if (mConstants.FLAG_PROCESS_START_ASYNC) {
+ if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
+ "Posting procStart msg for " + app.toShortString());
+ mProcStartHandler.post(() -> {
+ try {
+ synchronized (ActivityManagerService.this) {
+ final String reason = isProcStartValidLocked(app, startSeq);
+ if (reason != null) {
+ Slog.w(TAG_PROCESSES, app + " not valid anymore,"
+ + " don't start process, " + reason);
+ app.pendingStart = false;
+ return;
+ }
+ app.usingWrapper = invokeWith != null
+ || SystemProperties.get("wrap." + app.processName) != null;
+ mPendingStarts.put(startSeq, app);
+ }
+ final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint,
+ app, app.startUid, gids, runtimeFlags, mountExternal, app.seInfo,
+ requiredAbi, instructionSet, invokeWith, app.startTime);
+ synchronized (ActivityManagerService.this) {
+ handleProcessStartedLocked(app, startResult, startSeq);
+ }
+ } catch (RuntimeException e) {
+ synchronized (ActivityManagerService.this) {
+ Slog.e(TAG, "Failure starting process " + app.processName, e);
+ mPendingStarts.remove(startSeq);
+ app.pendingStart = false;
+ forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
+ false, false, true, false, false,
+ UserHandle.getUserId(app.userId), "start failure");
+ }
+ }
+ });
+ return true;
+ } else {
+ try {
+ final ProcessStartResult startResult = startProcess(hostingType, entryPoint, app,
+ uid, gids, runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet,
+ invokeWith, startTime);
+ handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
+ startSeq, false);
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "Failure starting process " + app.processName, e);
+ app.pendingStart = false;
+ forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
+ false, false, true, false, false,
+ UserHandle.getUserId(app.userId), "start failure");
+ }
+ return app.pid > 0;
+ }
+ }
+
+ private ProcessStartResult startProcess(String hostingType, String entryPoint,
+ ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
+ String seInfo, String requiredAbi, String instructionSet, String invokeWith,
+ long startTime) {
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
+ app.processName);
+ checkTime(startTime, "startProcess: asking zygote to start proc");
+ final ProcessStartResult startResult;
+ if (hostingType.equals("webview_service")) {
+ startResult = startWebView(entryPoint,
+ app.processName, uid, uid, gids, runtimeFlags, mountExternal,
+ app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
+ app.info.dataDir, null,
+ new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+ } else {
+ startResult = Process.start(entryPoint,
+ app.processName, uid, uid, gids, runtimeFlags, mountExternal,
+ app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
+ app.info.dataDir, invokeWith,
+ new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+ }
+ checkTime(startTime, "startProcess: returned from zygote!");
+ return startResult;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+ }
+
+ @GuardedBy("this")
+ private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
+ StringBuilder sb = null;
+ if (app.killedByAm) {
+ if (sb == null) sb = new StringBuilder();
+ sb.append("killedByAm=true;");
+ }
+ if (mProcessNames.get(app.processName, app.uid) != app) {
+ if (sb == null) sb = new StringBuilder();
+ sb.append("No entry in mProcessNames;");
+ }
+ if (!app.pendingStart) {
+ if (sb == null) sb = new StringBuilder();
+ sb.append("pendingStart=false;");
+ }
+ if (app.startSeq > expectedStartSeq) {
+ if (sb == null) sb = new StringBuilder();
+ sb.append("seq=" + app.startSeq + ",expected=" + expectedStartSeq + ";");
+ }
+ return sb == null ? null : sb.toString();
+ }
+
+ @GuardedBy("this")
+ private boolean handleProcessStartedLocked(ProcessRecord pending,
+ ProcessStartResult startResult, long expectedStartSeq) {
+ // Indicates that this process start has been taken care of.
+ if (mPendingStarts.get(expectedStartSeq) == null) {
+ if (pending.pid == startResult.pid) {
+ pending.usingWrapper = startResult.usingWrapper;
+ // TODO: Update already existing clients of usingWrapper
+ }
+ return false;
+ }
+ return handleProcessStartedLocked(pending, startResult.pid, startResult.usingWrapper,
+ expectedStartSeq, false);
+ }
+
+ @GuardedBy("this")
+ private boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,
+ long expectedStartSeq, boolean procAttached) {
+ mPendingStarts.remove(expectedStartSeq);
+ final String reason = isProcStartValidLocked(app, expectedStartSeq);
+ if (reason != null) {
+ Slog.w(TAG_PROCESSES, app + " start not valid, killing pid=" + pid
+ + ", " + reason);
+ app.pendingStart = false;
+ Process.killProcessQuiet(pid);
+ Process.killProcessGroup(app.uid, app.pid);
+ return false;
+ }
+ mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
+ checkTime(app.startTime, "startProcess: done updating battery stats");
+
+ EventLog.writeEvent(EventLogTags.AM_PROC_START,
+ UserHandle.getUserId(app.startUid), pid, app.startUid,
+ app.processName, app.hostingType,
+ app.hostingNameStr != null ? app.hostingNameStr : "");
+
+ try {
+ AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
+ app.seInfo, app.info.sourceDir, pid);
+ } catch (RemoteException ex) {
+ // Ignore
+ }
+
+ if (app.persistent) {
+ Watchdog.getInstance().processStarted(app.processName, pid);
+ }
+
+ checkTime(app.startTime, "startProcess: building log message");
+ StringBuilder buf = mStringBuilder;
+ buf.setLength(0);
+ buf.append("Start proc ");
+ buf.append(pid);
+ buf.append(':');
+ buf.append(app.processName);
+ buf.append('/');
+ UserHandle.formatUid(buf, app.startUid);
+ if (app.isolatedEntryPoint != null) {
+ buf.append(" [");
+ buf.append(app.isolatedEntryPoint);
+ buf.append("]");
+ }
+ buf.append(" for ");
+ buf.append(app.hostingType);
+ if (app.hostingNameStr != null) {
+ buf.append(" ");
+ buf.append(app.hostingNameStr);
+ }
+ Slog.i(TAG, buf.toString());
+ app.setPid(pid);
+ app.usingWrapper = usingWrapper;
+ app.pendingStart = false;
+ checkTime(app.startTime, "startProcess: starting to update pids map");
+ ProcessRecord oldApp;
+ synchronized (mPidsSelfLocked) {
+ oldApp = mPidsSelfLocked.get(pid);
+ }
+ // If there is already an app occupying that pid that hasn't been cleaned up
+ if (oldApp != null && !app.isolated) {
+ // Clean up anything relating to this pid first
+ Slog.w(TAG, "Reusing pid " + pid
+ + " while app is still mapped to it");
+ cleanUpApplicationRecordLocked(oldApp, false, false, -1,
+ true /*replacingPid*/);
+ }
+ synchronized (mPidsSelfLocked) {
+ this.mPidsSelfLocked.put(pid, app);
+ if (!procAttached) {
+ Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
+ msg.obj = app;
+ mHandler.sendMessageDelayed(msg, usingWrapper
+ ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
+ }
+ }
+ checkTime(app.startTime, "startProcess: done updating pids map");
+ return true;
+ }
+
void updateUsageStats(ActivityRecord component, boolean resumed) {
if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
"updateUsageStats: comp=" + component + "res=" + resumed);
@@ -5894,7 +6055,7 @@
return;
}
- if (app != null) {
+ if (app != null && app.pid > 0) {
ArrayList<Integer> firstPids = new ArrayList<Integer>();
firstPids.add(app.pid);
dumpStackTraces(tracesPath, firstPids, null, null, true /* useTombstoned */);
@@ -5948,7 +6109,7 @@
}
@Override
- public boolean clearApplicationUserData(final String packageName,
+ public boolean clearApplicationUserData(final String packageName, boolean keepState,
final IPackageDataObserver observer, int userId) {
enforceNotIsolatedCaller("clearApplicationUserData");
int uid = Binder.getCallingUid();
@@ -6052,14 +6213,27 @@
pm.clearApplicationUserData(packageName, localObserver, resolvedUserId);
if (appInfo != null) {
- synchronized (this) {
- // Remove all permissions granted from/to this package
- removeUriPermissionsForPackageLocked(packageName, resolvedUserId, true);
+ // Restore already established notification state and permission grants,
+ // so it told us to keep those intact -- it's about to emplace app data
+ // that is appropriate for those bits of system state.
+ if (!keepState) {
+ synchronized (this) {
+ // Remove all permissions granted from/to this package
+ removeUriPermissionsForPackageLocked(packageName, resolvedUserId, true);
+ }
+
+ // Reset notification state
+ INotificationManager inm = NotificationManager.getService();
+ inm.clearData(packageName, appInfo.uid, uid == appInfo.uid);
}
- // Reset notification settings.
- INotificationManager inm = NotificationManager.getService();
- inm.clearData(packageName, appInfo.uid, uid == appInfo.uid);
+ // Clear its scheduled jobs
+ JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
+ js.cancelJobsForUid(appInfo.uid, "clear data");
+
+ // Clear its pending alarms
+ AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class);
+ ami.removeAlarmsForUid(uid);
}
} catch (RemoteException e) {
}
@@ -6856,13 +7030,19 @@
mHeavyWeightProcess = null;
}
boolean needRestart = false;
- if (app.pid > 0 && app.pid != MY_PID) {
+ if ((app.pid > 0 && app.pid != MY_PID) || (app.pid == 0 && app.pendingStart)) {
int pid = app.pid;
- synchronized (mPidsSelfLocked) {
- mPidsSelfLocked.remove(pid);
- mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
+ if (pid > 0) {
+ synchronized (mPidsSelfLocked) {
+ mPidsSelfLocked.remove(pid);
+ mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
+ }
+ mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
+ if (app.isolated) {
+ mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
+ getPackageManagerInternalLocked().removeIsolatedUid(app.uid);
+ }
}
- mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
boolean willRestart = false;
if (app.persistent && !app.isolated) {
if (!callerWillRestart) {
@@ -6872,10 +7052,6 @@
}
}
app.kill(reason, true);
- if (app.isolated) {
- mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
- getPackageManagerInternalLocked().removeIsolatedUid(app.uid);
- }
handleAppDiedLocked(app, willRestart, allowRestart);
if (willRestart) {
removeLruProcessLocked(app);
@@ -6949,7 +7125,7 @@
}
private final boolean attachApplicationLocked(IApplicationThread thread,
- int pid) {
+ int pid, int callingUid, long startSeq) {
// Find the application record that is being attached... either via
// the pid if we are running in multiple processes, or just pull the
@@ -6964,6 +7140,17 @@
app = null;
}
+ // It's possible that process called attachApplication before we got a chance to
+ // update the internal state.
+ if (app == null && startSeq > 0) {
+ final ProcessRecord pending = mPendingStarts.get(startSeq);
+ if (pending != null && pending.startUid == callingUid
+ && handleProcessStartedLocked(pending, pid, pending.usingWrapper,
+ startSeq, true)) {
+ app = pending;
+ }
+ }
+
if (app == null) {
Slog.w(TAG, "No pending application record for pid " + pid
+ " (IApplicationThread " + thread + "); dropping process");
@@ -7258,11 +7445,12 @@
}
@Override
- public final void attachApplication(IApplicationThread thread) {
+ public final void attachApplication(IApplicationThread thread, long startSeq) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
- attachApplicationLocked(thread, callingPid);
+ attachApplicationLocked(thread, callingPid, callingUid, startSeq);
Binder.restoreCallingIdentity(origId);
}
}
@@ -10571,7 +10759,7 @@
stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
}
stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents,
- true /* sendNonResizeableNotification */);
+ false /* enteringSplitScreenMode */);
return windowingMode != task.getWindowingMode();
} finally {
Binder.restoreCallingIdentity(ident);
@@ -11326,7 +11514,11 @@
private final long[] mProcessStateStatsLongs = new long[1];
- boolean isProcessAliveLocked(ProcessRecord proc) {
+ private boolean isProcessAliveLocked(ProcessRecord proc) {
+ if (proc.pid <= 0) {
+ if (DEBUG_OOM_ADJ) Slog.d(TAG, "Process hasn't started yet: " + proc);
+ return false;
+ }
if (proc.procStatFile == null) {
proc.procStatFile = "/proc/" + proc.pid + "/stat";
}
@@ -12412,7 +12604,8 @@
if (wasAwake != isAwake) {
// Also update state in a special way for running foreground services UI.
mServices.updateScreenStateLocked(isAwake);
- sendNotifyVrManagerOfSleepState(!isAwake);
+ mHandler.obtainMessage(DISPATCH_SCREEN_AWAKE_MSG, isAwake ? 1 : 0, 0)
+ .sendToTarget();
}
}
}
@@ -12568,7 +12761,9 @@
Binder.restoreCallingIdentity(ident);
}
}
- sendNotifyVrManagerOfKeyguardState(showing);
+
+ mHandler.obtainMessage(DISPATCH_SCREEN_KEYGUARD_MSG, showing ? 1 : 0, 0)
+ .sendToTarget();
}
@Override
@@ -13951,8 +14146,7 @@
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord proc = mLruProcesses.get(i);
if (proc.notCachedSinceIdle) {
- if (proc.setProcState != ActivityManager.PROCESS_STATE_TOP_SLEEPING
- && proc.setProcState >= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+ if (proc.setProcState >= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
&& proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) {
if (doKilling && proc.initialIdlePss != 0
&& proc.lastPss > ((proc.initialIdlePss*3)/2)) {
@@ -15884,6 +16078,14 @@
}
pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
}
+ if (dumpAll && mPendingStarts.size() > 0) {
+ if (needSep) pw.println();
+ needSep = true;
+ pw.println(" mPendingStarts: ");
+ for (int i = 0, len = mPendingStarts.size(); i < len; ++i ) {
+ pw.println(" " + mPendingStarts.keyAt(i) + ": " + mPendingStarts.valueAt(i));
+ }
+ }
if (dumpPackage == null) {
pw.println(" mGlobalConfiguration: " + getGlobalConfiguration());
mStackSupervisor.dumpDisplayConfigs(pw, " ");
@@ -16918,7 +17120,7 @@
}
for (int i=mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord proc = mLruProcesses.get(i);
- if (proc.pid == pid) {
+ if (proc.pid > 0 && proc.pid == pid) {
procs.add(proc);
} else if (allPkgs && proc.pkgList != null
&& proc.pkgList.containsKey(args[start])) {
@@ -17039,20 +17241,24 @@
}
}
+ private static void sortMemItems(List<MemItem> items) {
+ Collections.sort(items, new Comparator<MemItem>() {
+ @Override
+ public int compare(MemItem lhs, MemItem rhs) {
+ if (lhs.pss < rhs.pss) {
+ return 1;
+ } else if (lhs.pss > rhs.pss) {
+ return -1;
+ }
+ return 0;
+ }
+ });
+ }
+
static final void dumpMemItems(PrintWriter pw, String prefix, String tag,
ArrayList<MemItem> items, boolean sort, boolean isCompact, boolean dumpSwapPss) {
if (sort && !isCompact) {
- Collections.sort(items, new Comparator<MemItem>() {
- @Override
- public int compare(MemItem lhs, MemItem rhs) {
- if (lhs.pss < rhs.pss) {
- return 1;
- } else if (lhs.pss > rhs.pss) {
- return -1;
- }
- return 0;
- }
- });
+ sortMemItems(items);
}
for (int i=0; i<items.size(); i++) {
@@ -17080,6 +17286,33 @@
}
}
+ static final void dumpMemItems(ProtoOutputStream proto, long fieldId, String tag,
+ ArrayList<MemItem> items, boolean sort, boolean dumpSwapPss) {
+ if (sort) {
+ sortMemItems(items);
+ }
+
+ for (int i=0; i<items.size(); i++) {
+ MemItem mi = items.get(i);
+ final long token = proto.start(fieldId);
+
+ proto.write(MemInfoProto.MemItem.TAG, tag);
+ proto.write(MemInfoProto.MemItem.LABEL, mi.shortLabel);
+ proto.write(MemInfoProto.MemItem.IS_PROC, mi.isProc);
+ proto.write(MemInfoProto.MemItem.ID, mi.id);
+ proto.write(MemInfoProto.MemItem.HAS_ACTIVITIES, mi.hasActivities);
+ proto.write(MemInfoProto.MemItem.PSS_KB, mi.pss);
+ if (dumpSwapPss) {
+ proto.write(MemInfoProto.MemItem.SWAP_PSS_KB, mi.swapPss);
+ }
+ if (mi.subitems != null) {
+ dumpMemItems(proto, MemInfoProto.MemItem.SUB_ITEMS, mi.shortLabel, mi.subitems,
+ true, dumpSwapPss);
+ }
+ proto.end(token);
+ }
+ }
+
// These are in KB.
static final long[] DUMP_MEM_BUCKETS = new long[] {
5*1024, 7*1024, 10*1024, 15*1024, 20*1024, 30*1024, 40*1024, 80*1024,
@@ -17291,7 +17524,7 @@
ArrayList<ProcessRecord> procs = collectProcesses(pw, opti, opts.packages, args);
if (opts.dumpProto) {
- dumpApplicationMemoryUsage(fd, pw, opts, innerArgs, brief, procs);
+ dumpApplicationMemoryUsage(fd, opts, innerArgs, brief, procs);
} else {
dumpApplicationMemoryUsage(fd, pw, prefix, opts, innerArgs, brief, procs, categoryPw);
}
@@ -17781,7 +18014,7 @@
}
}
- private final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw,
+ private final void dumpApplicationMemoryUsage(FileDescriptor fd,
MemoryUsageDumpOptions opts, String[] innerArgs, boolean brief,
ArrayList<ProcessRecord> procs) {
final long uptimeMs = SystemClock.uptimeMillis();
@@ -17823,8 +18056,8 @@
final int pid = r.pid;
final long nToken = proto.start(MemInfoProto.NATIVE_PROCESSES);
- proto.write(MemInfoProto.NativeProcess.PID, pid);
- proto.write(MemInfoProto.NativeProcess.PROCESS_NAME, r.baseName);
+ proto.write(MemInfoProto.ProcessMemory.PID, pid);
+ proto.write(MemInfoProto.ProcessMemory.PROCESS_NAME, r.baseName);
if (mi == null) {
mi = new Debug.MemoryInfo();
@@ -17850,8 +18083,332 @@
return;
}
- // TODO: finish
- pw.println("Java processes aren't implemented yet. Have a coffee instead! :]");
+ if (!brief && !opts.oomOnly && (procs.size() == 1 || opts.isCheckinRequest || opts.packages)) {
+ opts.dumpDetails = true;
+ }
+
+ ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+ proto.write(MemInfoProto.UPTIME_DURATION_MS, uptimeMs);
+ proto.write(MemInfoProto.ELAPSED_REALTIME_MS, realtimeMs);
+
+ ArrayList<MemItem> procMems = new ArrayList<MemItem>();
+ final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>();
+ long nativePss = 0;
+ long nativeSwapPss = 0;
+ long dalvikPss = 0;
+ long dalvikSwapPss = 0;
+ long[] dalvikSubitemPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
+ EmptyArray.LONG;
+ long[] dalvikSubitemSwapPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
+ EmptyArray.LONG;
+ long otherPss = 0;
+ long otherSwapPss = 0;
+ long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
+ long[] miscSwapPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
+
+ long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length];
+ long oomSwapPss[] = new long[DUMP_MEM_OOM_LABEL.length];
+ ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])
+ new ArrayList[DUMP_MEM_OOM_LABEL.length];
+
+ long totalPss = 0;
+ long totalSwapPss = 0;
+ long cachedPss = 0;
+ long cachedSwapPss = 0;
+ boolean hasSwapPss = false;
+
+ Debug.MemoryInfo mi = null;
+ for (int i = procs.size() - 1 ; i >= 0 ; i--) {
+ final ProcessRecord r = procs.get(i);
+ final IApplicationThread thread;
+ final int pid;
+ final int oomAdj;
+ final boolean hasActivities;
+ synchronized (this) {
+ thread = r.thread;
+ pid = r.pid;
+ oomAdj = r.getSetAdjWithServices();
+ hasActivities = r.activities.size() > 0;
+ }
+ if (thread == null) {
+ continue;
+ }
+ if (mi == null) {
+ mi = new Debug.MemoryInfo();
+ }
+ if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
+ Debug.getMemoryInfo(pid, mi);
+ hasSwapPss = mi.hasSwappedOutPss;
+ } else {
+ mi.dalvikPss = (int) Debug.getPss(pid, tmpLong, null);
+ mi.dalvikPrivateDirty = (int) tmpLong[0];
+ }
+ if (opts.dumpDetails) {
+ if (opts.localOnly) {
+ final long aToken = proto.start(MemInfoProto.APP_PROCESSES);
+ final long mToken = proto.start(MemInfoProto.AppData.PROCESS_MEMORY);
+ proto.write(MemInfoProto.ProcessMemory.PID, pid);
+ proto.write(MemInfoProto.ProcessMemory.PROCESS_NAME, r.processName);
+ ActivityThread.dumpMemInfoTable(proto, mi, opts.dumpDalvik,
+ opts.dumpSummaryOnly, 0, 0, 0, 0, 0, 0);
+ proto.end(mToken);
+ proto.end(aToken);
+ } else {
+ try {
+ ByteTransferPipe tp = new ByteTransferPipe();
+ try {
+ thread.dumpMemInfoProto(tp.getWriteFd(),
+ mi, opts.dumpFullDetails, opts.dumpDalvik, opts.dumpSummaryOnly,
+ opts.dumpUnreachable, innerArgs);
+ proto.write(MemInfoProto.APP_PROCESSES, tp.get());
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Got IOException!", e);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Got RemoteException!", e);
+ }
+ }
+ }
+
+ final long myTotalPss = mi.getTotalPss();
+ final long myTotalUss = mi.getTotalUss();
+ final long myTotalSwapPss = mi.getTotalSwappedOutPss();
+
+ synchronized (this) {
+ if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
+ // Record this for posterity if the process has been stable.
+ r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true, r.pkgList);
+ }
+ }
+
+ if (!opts.isCheckinRequest && mi != null) {
+ totalPss += myTotalPss;
+ totalSwapPss += myTotalSwapPss;
+ MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
+ (hasActivities ? " / activities)" : ")"), r.processName, myTotalPss,
+ myTotalSwapPss, pid, hasActivities);
+ procMems.add(pssItem);
+ procMemsMap.put(pid, pssItem);
+
+ nativePss += mi.nativePss;
+ nativeSwapPss += mi.nativeSwappedOutPss;
+ dalvikPss += mi.dalvikPss;
+ dalvikSwapPss += mi.dalvikSwappedOutPss;
+ for (int j=0; j<dalvikSubitemPss.length; j++) {
+ dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
+ dalvikSubitemSwapPss[j] +=
+ mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
+ }
+ otherPss += mi.otherPss;
+ otherSwapPss += mi.otherSwappedOutPss;
+ for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
+ long mem = mi.getOtherPss(j);
+ miscPss[j] += mem;
+ otherPss -= mem;
+ mem = mi.getOtherSwappedOutPss(j);
+ miscSwapPss[j] += mem;
+ otherSwapPss -= mem;
+ }
+
+ if (oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+ cachedPss += myTotalPss;
+ cachedSwapPss += myTotalSwapPss;
+ }
+
+ for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) {
+ if (oomIndex == (oomPss.length - 1)
+ || (oomAdj >= DUMP_MEM_OOM_ADJ[oomIndex]
+ && oomAdj < DUMP_MEM_OOM_ADJ[oomIndex + 1])) {
+ oomPss[oomIndex] += myTotalPss;
+ oomSwapPss[oomIndex] += myTotalSwapPss;
+ if (oomProcs[oomIndex] == null) {
+ oomProcs[oomIndex] = new ArrayList<MemItem>();
+ }
+ oomProcs[oomIndex].add(pssItem);
+ break;
+ }
+ }
+ }
+ }
+
+ long nativeProcTotalPss = 0;
+
+ if (procs.size() > 1 && !opts.packages) {
+ // If we are showing aggregations, also look for native processes to
+ // include so that our aggregations are more accurate.
+ updateCpuStatsNow();
+ mi = null;
+ synchronized (mProcessCpuTracker) {
+ final int N = mProcessCpuTracker.countStats();
+ for (int i=0; i<N; i++) {
+ ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
+ if (st.vsize > 0 && procMemsMap.indexOfKey(st.pid) < 0) {
+ if (mi == null) {
+ mi = new Debug.MemoryInfo();
+ }
+ if (!brief && !opts.oomOnly) {
+ Debug.getMemoryInfo(st.pid, mi);
+ } else {
+ mi.nativePss = (int)Debug.getPss(st.pid, tmpLong, null);
+ mi.nativePrivateDirty = (int)tmpLong[0];
+ }
+
+ final long myTotalPss = mi.getTotalPss();
+ final long myTotalSwapPss = mi.getTotalSwappedOutPss();
+ totalPss += myTotalPss;
+ nativeProcTotalPss += myTotalPss;
+
+ MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
+ st.name, myTotalPss, mi.getSummaryTotalSwapPss(), st.pid, false);
+ procMems.add(pssItem);
+
+ nativePss += mi.nativePss;
+ nativeSwapPss += mi.nativeSwappedOutPss;
+ dalvikPss += mi.dalvikPss;
+ dalvikSwapPss += mi.dalvikSwappedOutPss;
+ for (int j=0; j<dalvikSubitemPss.length; j++) {
+ dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
+ dalvikSubitemSwapPss[j] +=
+ mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
+ }
+ otherPss += mi.otherPss;
+ otherSwapPss += mi.otherSwappedOutPss;
+ for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
+ long mem = mi.getOtherPss(j);
+ miscPss[j] += mem;
+ otherPss -= mem;
+ mem = mi.getOtherSwappedOutPss(j);
+ miscSwapPss[j] += mem;
+ otherSwapPss -= mem;
+ }
+ oomPss[0] += myTotalPss;
+ oomSwapPss[0] += myTotalSwapPss;
+ if (oomProcs[0] == null) {
+ oomProcs[0] = new ArrayList<MemItem>();
+ }
+ oomProcs[0].add(pssItem);
+ }
+ }
+ }
+
+ ArrayList<MemItem> catMems = new ArrayList<MemItem>();
+
+ catMems.add(new MemItem("Native", "Native", nativePss, nativeSwapPss, -1));
+ final int dalvikId = -2;
+ catMems.add(new MemItem("Dalvik", "Dalvik", dalvikPss, dalvikSwapPss, dalvikId));
+ catMems.add(new MemItem("Unknown", "Unknown", otherPss, otherSwapPss, -3));
+ for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
+ String label = Debug.MemoryInfo.getOtherLabel(j);
+ catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], j));
+ }
+ if (dalvikSubitemPss.length > 0) {
+ // Add dalvik subitems.
+ for (MemItem memItem : catMems) {
+ int memItemStart = 0, memItemEnd = 0;
+ if (memItem.id == dalvikId) {
+ memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_START;
+ memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_END;
+ } else if (memItem.id == Debug.MemoryInfo.OTHER_DALVIK_OTHER) {
+ memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_OTHER_START;
+ memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_OTHER_END;
+ } else if (memItem.id == Debug.MemoryInfo.OTHER_DEX) {
+ memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DEX_START;
+ memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DEX_END;
+ } else if (memItem.id == Debug.MemoryInfo.OTHER_ART) {
+ memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_ART_START;
+ memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_ART_END;
+ } else {
+ continue; // No subitems, continue.
+ }
+ memItem.subitems = new ArrayList<MemItem>();
+ for (int j=memItemStart; j<=memItemEnd; j++) {
+ final String name = Debug.MemoryInfo.getOtherLabel(
+ Debug.MemoryInfo.NUM_OTHER_STATS + j);
+ memItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j],
+ dalvikSubitemSwapPss[j], j));
+ }
+ }
+ }
+
+ ArrayList<MemItem> oomMems = new ArrayList<MemItem>();
+ for (int j=0; j<oomPss.length; j++) {
+ if (oomPss[j] != 0) {
+ String label = opts.isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j]
+ : DUMP_MEM_OOM_LABEL[j];
+ MemItem item = new MemItem(label, label, oomPss[j], oomSwapPss[j],
+ DUMP_MEM_OOM_ADJ[j]);
+ item.subitems = oomProcs[j];
+ oomMems.add(item);
+ }
+ }
+
+ opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && totalSwapPss != 0;
+ if (!opts.oomOnly) {
+ dumpMemItems(proto, MemInfoProto.TOTAL_PSS_BY_PROCESS, "proc",
+ procMems, true, opts.dumpSwapPss);
+ }
+ dumpMemItems(proto, MemInfoProto.TOTAL_PSS_BY_OOM_ADJUSTMENT, "oom",
+ oomMems, false, opts.dumpSwapPss);
+ if (!brief && !opts.oomOnly) {
+ dumpMemItems(proto, MemInfoProto.TOTAL_PSS_BY_CATEGORY, "cat",
+ catMems, true, opts.dumpSwapPss);
+ }
+ MemInfoReader memInfo = new MemInfoReader();
+ memInfo.readMemInfo();
+ if (nativeProcTotalPss > 0) {
+ synchronized (this) {
+ final long cachedKb = memInfo.getCachedSizeKb();
+ final long freeKb = memInfo.getFreeSizeKb();
+ final long zramKb = memInfo.getZramTotalSizeKb();
+ final long kernelKb = memInfo.getKernelUsedSizeKb();
+ EventLogTags.writeAmMeminfo(cachedKb*1024, freeKb*1024, zramKb*1024,
+ kernelKb*1024, nativeProcTotalPss*1024);
+ mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
+ nativeProcTotalPss);
+ }
+ }
+ if (!brief) {
+ proto.write(MemInfoProto.TOTAL_RAM_KB, memInfo.getTotalSizeKb());
+ proto.write(MemInfoProto.STATUS, mLastMemoryLevel);
+ proto.write(MemInfoProto.CACHED_PSS_KB, cachedPss);
+ proto.write(MemInfoProto.CACHED_KERNEL_KB, memInfo.getCachedSizeKb());
+ proto.write(MemInfoProto.FREE_KB, memInfo.getFreeSizeKb());
+ }
+ long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
+ - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
+ - memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb();
+ proto.write(MemInfoProto.USED_PSS_KB, totalPss - cachedPss);
+ proto.write(MemInfoProto.USED_KERNEL_KB, memInfo.getKernelUsedSizeKb());
+ proto.write(MemInfoProto.LOST_RAM_KB, lostRAM);
+ if (!brief) {
+ if (memInfo.getZramTotalSizeKb() != 0) {
+ proto.write(MemInfoProto.TOTAL_ZRAM_KB, memInfo.getZramTotalSizeKb());
+ proto.write(MemInfoProto.ZRAM_PHYSICAL_USED_IN_SWAP_KB,
+ memInfo.getSwapTotalSizeKb() - memInfo.getSwapFreeSizeKb());
+ proto.write(MemInfoProto.TOTAL_ZRAM_SWAP_KB, memInfo.getSwapTotalSizeKb());
+ }
+ final long[] ksm = getKsmInfo();
+ proto.write(MemInfoProto.KSM_SHARING_KB, ksm[KSM_SHARING]);
+ proto.write(MemInfoProto.KSM_SHARED_KB, ksm[KSM_SHARED]);
+ proto.write(MemInfoProto.KSM_UNSHARED_KB, ksm[KSM_UNSHARED]);
+ proto.write(MemInfoProto.KSM_VOLATILE_KB, ksm[KSM_VOLATILE]);
+
+ proto.write(MemInfoProto.TUNING_MB, ActivityManager.staticGetMemoryClass());
+ proto.write(MemInfoProto.TUNING_LARGE_MB, ActivityManager.staticGetLargeMemoryClass());
+ proto.write(MemInfoProto.OOM_KB,
+ mProcessList.getMemLevel(ProcessList.CACHED_APP_MAX_ADJ) / 1024);
+ proto.write(MemInfoProto.RESTORE_LIMIT_KB,
+ mProcessList.getCachedRestoreThresholdKb());
+
+ proto.write(MemInfoProto.IS_LOW_RAM_DEVICE, ActivityManager.isLowRamDeviceStatic());
+ proto.write(MemInfoProto.IS_HIGH_END_GFX, ActivityManager.isHighEndGfx());
+ }
+ }
+
+ proto.flush();
}
private void appendBasicMemEntry(StringBuilder sb, int oomAdj, int procState, long pss,
@@ -18344,7 +18901,7 @@
for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) {
ProcessChangeItem item = mPendingProcessChanges.get(i);
- if (item.pid == app.pid) {
+ if (app.pid > 0 && item.pid == app.pid) {
mPendingProcessChanges.remove(i);
mAvailProcessChanges.add(item);
}
@@ -18396,6 +18953,7 @@
ProcessList.remove(app.pid);
}
addProcessNameLocked(app);
+ app.pendingStart = false;
startProcessLocked(app, "restart", app.processName);
return true;
} else if (app.pid > 0 && app.pid != MY_PID) {
@@ -21133,7 +21691,7 @@
int procState;
boolean foregroundActivities = false;
mTmpBroadcastQueue.clear();
- if (app == TOP_APP) {
+ if (PROCESS_STATE_CUR_TOP == ActivityManager.PROCESS_STATE_TOP && app == TOP_APP) {
// The last app on the list is the foreground app.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
@@ -21169,6 +21727,13 @@
procState = ActivityManager.PROCESS_STATE_SERVICE;
if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Making exec-service: " + app);
//Slog.i(TAG, "EXEC " + (app.execServicesFg ? "FG" : "BG") + ": " + app);
+ } else if (app == TOP_APP) {
+ adj = ProcessList.FOREGROUND_APP_ADJ;
+ schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+ app.adjType = "top-sleeping";
+ foregroundActivities = true;
+ procState = PROCESS_STATE_CUR_TOP;
+ if (DEBUG_OOM_ADJ_REASON) Slog.d(TAG, "Making top: " + app);
} else {
// As far as we know the process is empty. We may change our mind later.
schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
@@ -22617,7 +23182,7 @@
if (app.curProcState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
isInteraction = true;
app.fgInteractionTime = 0;
- } else if (app.curProcState <= ActivityManager.PROCESS_STATE_TOP_SLEEPING) {
+ } else if (app.curProcState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
if (app.fgInteractionTime == 0) {
app.fgInteractionTime = nowElapsed;
isInteraction = false;
@@ -23051,7 +23616,8 @@
break;
}
}
- } else if (app.curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
+ } else if (app.curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+ && !app.killedByAm) {
if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
&& app.thread != null) {
try {
@@ -23588,7 +24154,7 @@
+ ")\n");
if (app.pid > 0 && app.pid != MY_PID) {
app.kill("empty", false);
- } else {
+ } else if (app.thread != null) {
try {
app.thread.scheduleExit();
} catch (Exception e) {
@@ -24104,7 +24670,7 @@
}
@Override
- public int startIsolatedProcess(String entryPoint, String[] entryPointArgs,
+ public boolean startIsolatedProcess(String entryPoint, String[] entryPointArgs,
String processName, String abiOverride, int uid, Runnable crashHandler) {
return ActivityManagerService.this.startIsolatedProcess(entryPoint, entryPointArgs,
processName, abiOverride, uid, crashHandler);
@@ -24392,6 +24958,13 @@
}
}
+ @Override
+ public void notifyActiveVoiceInteractionServiceChanged(ComponentName component) {
+ synchronized (ActivityManagerService.this) {
+ mActiveVoiceInteractionServiceComponent = component;
+ }
+ }
+
/**
* Called after virtual display Id is updated by
* {@link com.android.server.vr.Vr2dDisplay} with a specific
@@ -24478,6 +25051,31 @@
public boolean isRuntimeRestarted() {
return mSystemServiceManager.isRuntimeRestarted();
}
+
+ @Override
+ public boolean hasRunningActivity(int uid, @Nullable String packageName) {
+ if (packageName == null) return false;
+
+ synchronized (ActivityManagerService.this) {
+ for (int i = 0; i < mLruProcesses.size(); i++) {
+ final ProcessRecord processRecord = mLruProcesses.get(i);
+ if (processRecord.uid == uid) {
+ for (int j = 0; j < processRecord.activities.size(); j++) {
+ final ActivityRecord activityRecord = processRecord.activities.get(j);
+ if (packageName.equals(activityRecord.packageName)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void registerScreenObserver(ScreenObserver observer) {
+ mScreenObservers.add(observer);
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 979323f..4f60e17 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -48,6 +48,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ShellCommand;
+import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -69,7 +70,9 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityManager.RESIZE_MODE_USER;
@@ -126,7 +129,7 @@
if (cmd == null) {
return handleDefaultCommands(cmd);
}
- PrintWriter pw = getOutPrintWriter();
+ final PrintWriter pw = getOutPrintWriter();
try {
switch (cmd) {
case "start":
@@ -1326,65 +1329,95 @@
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq) throws RemoteException {
synchronized (this) {
- mPw.print(uid);
- mPw.print(" procstate ");
- mPw.print(ProcessList.makeProcStateString(procState));
- mPw.print(" seq ");
- mPw.println(procStateSeq);
- mPw.flush();
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ mPw.print(uid);
+ mPw.print(" procstate ");
+ mPw.print(ProcessList.makeProcStateString(procState));
+ mPw.print(" seq ");
+ mPw.println(procStateSeq);
+ mPw.flush();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
}
}
@Override
public void onUidGone(int uid, boolean disabled) throws RemoteException {
synchronized (this) {
- mPw.print(uid);
- mPw.print(" gone");
- if (disabled) {
- mPw.print(" disabled");
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ mPw.print(uid);
+ mPw.print(" gone");
+ if (disabled) {
+ mPw.print(" disabled");
+ }
+ mPw.println();
+ mPw.flush();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
}
- mPw.println();
- mPw.flush();
}
}
@Override
public void onUidActive(int uid) throws RemoteException {
synchronized (this) {
- mPw.print(uid);
- mPw.println(" active");
- mPw.flush();
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ mPw.print(uid);
+ mPw.println(" active");
+ mPw.flush();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
}
}
@Override
public void onUidIdle(int uid, boolean disabled) throws RemoteException {
synchronized (this) {
- mPw.print(uid);
- mPw.print(" idle");
- if (disabled) {
- mPw.print(" disabled");
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ mPw.print(uid);
+ mPw.print(" idle");
+ if (disabled) {
+ mPw.print(" disabled");
+ }
+ mPw.println();
+ mPw.flush();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
}
- mPw.println();
- mPw.flush();
}
}
@Override
public void onUidCachedChanged(int uid, boolean cached) throws RemoteException {
synchronized (this) {
- mPw.print(uid);
- mPw.println(cached ? " cached" : " uncached");
- mPw.flush();
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ mPw.print(uid);
+ mPw.println(cached ? " cached" : " uncached");
+ mPw.flush();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
}
}
@Override
public void onOomAdjMessage(String msg) {
synchronized (this) {
- mPw.print("# ");
- mPw.println(msg);
- mPw.flush();
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ mPw.print("# ");
+ mPw.println(msg);
+ mPw.flush();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
}
}
@@ -1867,10 +1900,24 @@
String value = getNextArgRequired();
int bucket = bucketNameToBucketValue(value);
if (bucket < 0) return -1;
+ boolean multiple = peekNextArg() != null;
+
IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
Context.USAGE_STATS_SERVICE));
- usm.setAppStandbyBucket(packageName, bucketNameToBucketValue(value), userId);
+ if (!multiple) {
+ usm.setAppStandbyBucket(packageName, bucketNameToBucketValue(value), userId);
+ } else {
+ HashMap<String, Integer> buckets = new HashMap<>();
+ buckets.put(packageName, bucket);
+ while ((packageName = getNextArg()) != null) {
+ value = getNextArgRequired();
+ bucket = bucketNameToBucketValue(value);
+ if (bucket < 0) continue;
+ buckets.put(packageName, bucket);
+ }
+ usm.setAppStandbyBuckets(buckets, userId);
+ }
return 0;
}
@@ -1886,12 +1933,21 @@
return -1;
}
}
- String packageName = getNextArgRequired();
+ String packageName = getNextArg();
IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
Context.USAGE_STATS_SERVICE));
- int bucket = usm.getAppStandbyBucket(packageName, null, userId);
- pw.println(bucket);
+ if (packageName != null) {
+ int bucket = usm.getAppStandbyBucket(packageName, null, userId);
+ pw.println(bucket);
+ } else {
+ Map<String, Integer> buckets = (Map<String, Integer>) usm.getAppStandbyBuckets(
+ SHELL_PACKAGE_NAME, userId);
+ for (Map.Entry<String, Integer> entry: buckets.entrySet()) {
+ pw.print(entry.getKey()); pw.print(": ");
+ pw.println(entry.getValue());
+ }
+ }
return 0;
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 60a6236..69f6d5e 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1050,11 +1050,7 @@
* @return whether the given package name can launch an assist activity.
*/
private boolean canLaunchAssistActivity(String packageName) {
- if (service.mAssistUtils == null) {
- return false;
- }
-
- final ComponentName assistComponent = service.mAssistUtils.getActiveServiceComponentName();
+ final ComponentName assistComponent = service.mActiveVoiceInteractionServiceComponent;
if (assistComponent != null) {
return assistComponent.getPackageName().equals(packageName);
}
@@ -2735,7 +2731,8 @@
* {@link #mShowWhenLocked}.
*/
boolean canShowWhenLocked() {
- return mShowWhenLocked || service.mWindowManager.containsShowWhenLockedWindow(appToken);
+ return !inMultiWindowMode() && (mShowWhenLocked
+ || service.mWindowManager.containsShowWhenLockedWindow(appToken));
}
void setTurnScreenOn(boolean turnScreenOn) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index cf40be5..10c801d 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -484,20 +484,24 @@
@Override
public void setWindowingMode(int windowingMode) {
setWindowingMode(windowingMode, false /* animate */, true /* showRecents */,
- true /* sendNonResizeableNotification */);
+ false /* enteringSplitScreenMode */);
}
void setWindowingMode(int preferredWindowingMode, boolean animate, boolean showRecents,
- boolean sendNonResizeableNotification) {
+ boolean enteringSplitScreenMode) {
+ final boolean creating = mWindowContainerController == null;
final int currentMode = getWindowingMode();
final ActivityDisplay display = getDisplay();
final TaskRecord topTask = topTask();
final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack();
mTmpOptions.setLaunchWindowingMode(preferredWindowingMode);
- // Need to make sure windowing mode is supported.
- int windowingMode = display.resolveWindowingMode(
- null /* ActivityRecord */, mTmpOptions, topTask, getActivityType());
+ // Need to make sure windowing mode is supported. If we in the process of creating the stack
+ // no need to resolve the windowing mode again as it is already resolved to the right mode.
+ int windowingMode = creating
+ ? preferredWindowingMode
+ : display.resolveWindowingMode(
+ null /* ActivityRecord */, mTmpOptions, topTask, getActivityType());
if (splitScreenStack == this && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
// Resolution to split-screen secondary for the primary split-screen stack means we want
// to go fullscreen.
@@ -506,14 +510,19 @@
final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryStack();
+ // Don't send non-resizeable notifications if the windowing mode changed was a side effect
+ // of us entering split-screen mode.
+ final boolean sendNonResizeableNotification = !enteringSplitScreenMode;
// Take any required action due to us not supporting the preferred windowing mode.
- if (sendNonResizeableNotification
- && windowingMode != preferredWindowingMode && isActivityTypeStandardOrUndefined()) {
- if (alreadyInSplitScreenMode
- && (preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
- // Looks like we can't launch in split screen mode, go ahead an dismiss split-screen
- // and display a warning toast about it.
+ if (alreadyInSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
+ && sendNonResizeableNotification && isActivityTypeStandardOrUndefined()) {
+ final boolean preferredSplitScreen =
+ preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ if (preferredSplitScreen || creating) {
+ // Looks like we can't launch in split screen mode or the stack we are launching
+ // doesn't support split-screen mode, go ahead an dismiss split-screen and display a
+ // warning toast about it.
mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
display.getSplitScreenPrimaryStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
}
@@ -544,7 +553,7 @@
}
super.setWindowingMode(windowingMode);
- if (mWindowContainerController == null) {
+ if (creating) {
// Nothing else to do if we don't have a window container yet. E.g. call from ctor.
return;
}
@@ -594,16 +603,22 @@
// the one where the home stack is visible since recents isn't visible yet, but the
// divider will be off. I think we should just make the initial bounds that of home
// so that the divider matches and remove this logic.
- display.getOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
- ACTIVITY_TYPE_RECENTS, true /* onTop */);
+ final ActivityStack recentStack = display.getOrCreateStack(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_RECENTS,
+ true /* onTop */);
+ recentStack.moveToFront("setWindowingMode");
// If task moved to docked stack - show recents if needed.
mService.mWindowManager.showRecentApps(false /* fromHome */);
}
wm.continueSurfaceLayout();
}
- mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ // Don't ensure visible activities if the windowing mode change was a side effect of us
+ // entering split-screen mode.
+ if (!enteringSplitScreenMode) {
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ }
}
@Override
@@ -1668,7 +1683,7 @@
continue;
}
- if (!r.visible && r != starting) {
+ if (!r.visibleIgnoringKeyguard && r != starting) {
// Also ignore invisible activities that are not the currently starting
// activity (about to be visible).
continue;
@@ -1796,6 +1811,7 @@
boolean behindFullscreenActivity = !stackShouldBeVisible;
boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this)
&& (isInStackLocked(starting) == null);
+ final boolean isTopFullscreenStack = getDisplay().isTopFullscreenStack(this);
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -1817,7 +1833,7 @@
// Now check whether it's really visible depending on Keyguard state.
final boolean reallyVisible = checkKeyguardVisibility(r,
- visibleIgnoringKeyguard, isTop);
+ visibleIgnoringKeyguard, isTop && isTopFullscreenStack);
if (visibleIgnoringKeyguard) {
behindFullscreenActivity = updateBehindFullscreen(!stackShouldBeVisible,
behindFullscreenActivity, r);
@@ -1943,13 +1959,11 @@
*
* @return true if {@param r} is visible taken Keyguard state into account, false otherwise
*/
- boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible,
- boolean isTop) {
- final boolean isInPinnedStack = r.inPinnedWindowingMode();
+ boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible, boolean isTop) {
final boolean keyguardShowing = mStackSupervisor.getKeyguardController().isKeyguardShowing(
mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
final boolean keyguardLocked = mStackSupervisor.getKeyguardController().isKeyguardLocked();
- final boolean showWhenLocked = r.canShowWhenLocked() && !isInPinnedStack;
+ final boolean showWhenLocked = r.canShowWhenLocked();
final boolean dismissKeyguard = r.hasDismissKeyguardWindows();
if (shouldBeVisible) {
if (dismissKeyguard && mTopDismissingKeyguardActivity == null) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index edaa511..0a42aa9 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2312,7 +2312,7 @@
if (displayId == INVALID_DISPLAY) {
displayId = candidateDisplayId;
}
- if (displayId != INVALID_DISPLAY) {
+ if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
if (r != null) {
// TODO: This should also take in the windowing mode and activity type into account.
stack = (T) getValidLaunchStackOnDisplay(displayId, r);
@@ -2341,7 +2341,7 @@
}
if (stack != null) {
display = stack.getDisplay();
- if (display != null) {
+ if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
final int windowingMode =
display.resolveWindowingMode(r, options, candidateTask, activityType);
if (stack.isCompatible(windowingMode, activityType)) {
@@ -2351,6 +2351,7 @@
}
if (display == null
+ || !canLaunchOnDisplay(r, display.mDisplayId)
// TODO: Can be removed once we figure-out how non-standard types should launch
// outside the default display.
|| (activityType != ACTIVITY_TYPE_STANDARD
@@ -2361,6 +2362,14 @@
return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
}
+ /** @return true if activity record is null or can be launched on provided display. */
+ private boolean canLaunchOnDisplay(ActivityRecord r, int displayId) {
+ if (r == null) {
+ return true;
+ }
+ return r.canBeLaunchedOnDisplay(displayId);
+ }
+
/**
* Get a topmost stack on the display, that is a valid launch stack for specified activity.
* If there is no such stack, new dynamic stack can be created.
@@ -4248,22 +4257,15 @@
// Handle incorrect launch/move to secondary display if needed.
if (isSecondaryDisplayPreferred) {
- final boolean launchOnSecondaryDisplayFailed;
final int actualDisplayId = task.getStack().mDisplayId;
if (!task.canBeLaunchedOnDisplay(actualDisplayId)) {
- // The task landed on an inappropriate display somehow, move it to the default
- // display.
- // TODO(multi-display): Find proper stack for the task on the default display.
- mService.setTaskWindowingMode(task.taskId,
- WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */);
- launchOnSecondaryDisplayFailed = true;
- } else {
- // The task might have landed on a display different from requested.
- launchOnSecondaryDisplayFailed = actualDisplayId == DEFAULT_DISPLAY
- || (preferredDisplayId != INVALID_DISPLAY
- && preferredDisplayId != actualDisplayId);
+ throw new IllegalStateException("Task resolved to incompatible display");
}
- if (launchOnSecondaryDisplayFailed) {
+ // The task might have landed on a display different from requested.
+ // TODO(multi-display): Find proper stack for the task on the default display.
+ mService.setTaskWindowingMode(task.taskId,
+ WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */);
+ if (preferredDisplayId != actualDisplayId) {
// Display a warning toast that we tried to put a non-resizeable task on a secondary
// display with config different from global config.
mService.mTaskChangeNotificationController
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 69cc3c7..abdbfad 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1812,6 +1812,9 @@
}
}
}
+ // Need to update mTargetStack because if task was moved out of it, the original stack may
+ // be destroyed.
+ mTargetStack = intentActivity.getStack();
if (!mMovedToFront && mDoResume) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + mTargetStack
+ " from " + intentActivity);
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index d0bc33a..35465a7 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -521,13 +521,13 @@
*
* @param app The ProcessRecord in which the error occurred.
* @param condition Crashing, Application Not Responding, etc. Values are defined in
- * ActivityManager.AppErrorStateInfo
+ * ActivityManager.ProcessErrorStateInfo
* @param activity The activity associated with the crash, if known.
* @param shortMsg Short message describing the crash.
* @param longMsg Long message describing the crash.
* @param stackTrace Full crash stack trace, may be null.
*
- * @return Returns a fully-formed AppErrorStateInfo record.
+ * @return Returns a fully-formed ProcessErrorStateInfo record.
*/
private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app,
int condition, String activity, String shortMsg, String longMsg, String stackTrace) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 87690d1..93fb3e3 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1335,8 +1335,7 @@
null, mStats.mHandler, null, mUserManagerUserInfoProvider);
checkinStats.readSummaryFromParcel(in);
in.recycle();
- checkinStats.dumpProtoLocked(mContext, fd, apps, flags,
- historyStart);
+ checkinStats.dumpProtoLocked(mContext, fd, apps, flags);
mStats.mCheckinFile.delete();
return;
}
@@ -1349,7 +1348,7 @@
}
if (DBG) Slog.d(TAG, "begin dumpProtoLocked from UID " + Binder.getCallingUid());
synchronized (mStats) {
- mStats.dumpProtoLocked(mContext, fd, apps, flags, historyStart);
+ mStats.dumpProtoLocked(mContext, fd, apps, flags);
if (writeData) {
mStats.writeAsyncLocked();
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index aa82d00..ea90db3 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -322,7 +322,7 @@
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
boolean didSomething = false;
final BroadcastRecord br = mPendingBroadcast;
- if (br != null && br.curApp.pid == app.pid) {
+ if (br != null && br.curApp.pid > 0 && br.curApp.pid == app.pid) {
if (br.curApp != app) {
Slog.e(TAG, "App mismatch when sending pending broadcast to "
+ app.processName + ", intended target is " + br.curApp.processName);
@@ -876,9 +876,16 @@
+ mPendingBroadcast.curApp);
boolean isDead;
- synchronized (mService.mPidsSelfLocked) {
- ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
- isDead = proc == null || proc.crashing;
+ if (mPendingBroadcast.curApp.pid > 0) {
+ synchronized (mService.mPidsSelfLocked) {
+ ProcessRecord proc = mService.mPidsSelfLocked.get(
+ mPendingBroadcast.curApp.pid);
+ isDead = proc == null || proc.crashing;
+ }
+ } else {
+ final ProcessRecord proc = mService.mProcessNames.get(
+ mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
+ isDead = proc == null || !proc.pendingStart;
}
if (!isDead) {
// It's still alive, so keep waiting
diff --git a/services/core/java/com/android/server/am/ClientLifecycleManager.java b/services/core/java/com/android/server/am/ClientLifecycleManager.java
index cc70f18..014f708 100644
--- a/services/core/java/com/android/server/am/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/am/ClientLifecycleManager.java
@@ -43,7 +43,8 @@
*/
void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
transaction.schedule();
- transaction.recycle();
+ // TODO: b/70616950
+ //transaction.recycle();
}
/**
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 6fb3dbb..ab5d64c 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -364,9 +364,6 @@
case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
procState = "FGS ";
break;
- case ActivityManager.PROCESS_STATE_TOP_SLEEPING:
- procState = "TPSL";
- break;
case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
procState = "IMPF";
break;
@@ -379,15 +376,18 @@
case ActivityManager.PROCESS_STATE_BACKUP:
procState = "BKUP";
break;
- case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT:
- procState = "HVY ";
- break;
case ActivityManager.PROCESS_STATE_SERVICE:
procState = "SVC ";
break;
case ActivityManager.PROCESS_STATE_RECEIVER:
procState = "RCVR";
break;
+ case ActivityManager.PROCESS_STATE_TOP_SLEEPING:
+ procState = "TPSL";
+ break;
+ case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT:
+ procState = "HVY ";
+ break;
case ActivityManager.PROCESS_STATE_HOME:
procState = "HOME";
break;
@@ -485,14 +485,14 @@
PROC_MEM_TOP, // ActivityManager.PROCESS_STATE_TOP
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
- PROC_MEM_TOP, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_BACKUP
- PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
PROC_MEM_SERVICE, // ActivityManager.PROCESS_STATE_SERVICE
PROC_MEM_CACHED, // ActivityManager.PROCESS_STATE_RECEIVER
+ PROC_MEM_TOP, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
+ PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
PROC_MEM_CACHED, // ActivityManager.PROCESS_STATE_HOME
PROC_MEM_CACHED, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
PROC_MEM_CACHED, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
@@ -507,14 +507,14 @@
PSS_FIRST_TOP_INTERVAL, // ActivityManager.PROCESS_STATE_TOP
PSS_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
PSS_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
- PSS_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
PSS_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
PSS_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
PSS_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND
PSS_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_BACKUP
- PSS_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
PSS_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_SERVICE
PSS_FIRST_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_RECEIVER
+ PSS_FIRST_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
+ PSS_FIRST_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
PSS_FIRST_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_HOME
PSS_FIRST_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
PSS_FIRST_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
@@ -529,14 +529,14 @@
PSS_SHORT_INTERVAL, // ActivityManager.PROCESS_STATE_TOP
PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
- PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND
PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_BACKUP
- PSS_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
PSS_SAME_SERVICE_INTERVAL, // ActivityManager.PROCESS_STATE_SERVICE
PSS_SAME_SERVICE_INTERVAL, // ActivityManager.PROCESS_STATE_RECEIVER
+ PSS_SAME_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
+ PSS_SAME_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
PSS_SAME_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_HOME
PSS_SAME_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
PSS_SAME_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
@@ -545,20 +545,20 @@
PSS_SAME_CACHED_INTERVAL, // ActivityManager.PROCESS_STATE_CACHED_EMPTY
};
- private static final long[] sTestFirstAwakePssTimes = new long[] {
+ private static final long[] sTestFirstPssTimes = new long[] {
PSS_TEST_FIRST_TOP_INTERVAL, // ActivityManager.PROCESS_STATE_PERSISTENT
PSS_TEST_FIRST_TOP_INTERVAL, // ActivityManager.PROCESS_STATE_PERSISTENT_UI
PSS_TEST_FIRST_TOP_INTERVAL, // ActivityManager.PROCESS_STATE_TOP
- PSS_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
- PSS_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
- PSS_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
+ PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
+ PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND
PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_BACKUP
- PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_SERVICE
PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_RECEIVER
+ PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
+ PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_HOME
PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
@@ -567,20 +567,20 @@
PSS_TEST_FIRST_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_CACHED_EMPTY
};
- private static final long[] sTestSameAwakePssTimes = new long[] {
+ private static final long[] sTestSamePssTimes = new long[] {
PSS_TEST_SAME_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_PERSISTENT
PSS_TEST_SAME_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_PERSISTENT_UI
PSS_TEST_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_TOP
PSS_TEST_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
PSS_TEST_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
- PSS_TEST_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
PSS_TEST_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
PSS_TEST_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
PSS_TEST_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND
PSS_TEST_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_BACKUP
- PSS_TEST_SAME_IMPORTANT_INTERVAL, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
PSS_TEST_SAME_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_SERVICE
PSS_TEST_SAME_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_RECEIVER
+ PSS_TEST_SAME_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
+ PSS_TEST_SAME_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
PSS_TEST_SAME_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_HOME
PSS_TEST_SAME_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
PSS_TEST_SAME_BACKGROUND_INTERVAL, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
@@ -601,8 +601,8 @@
boolean sleeping, long now) {
final long[] table = test
? (first
- ? sTestFirstAwakePssTimes
- : sTestSameAwakePssTimes)
+ ? sTestFirstPssTimes
+ : sTestSamePssTimes)
: (first
? sFirstAwakePssTimes
: sSameAwakePssTimes);
@@ -628,6 +628,7 @@
/**
* Set the out-of-memory badness adjustment for a process.
+ * If {@code pid <= 0}, this method will be a no-op.
*
* @param pid The process identifier to set.
* @param uid The uid of the app
@@ -636,6 +637,10 @@
* {@hide}
*/
public static final void setOomAdj(int pid, int uid, int amt) {
+ // This indicates that the process is not started yet and so no need to proceed further.
+ if (pid <= 0) {
+ return;
+ }
if (amt == UNKNOWN_ADJ)
return;
@@ -657,6 +662,10 @@
* {@hide}
*/
public static final void remove(int pid) {
+ // This indicates that the process is not started yet and so no need to proceed further.
+ if (pid <= 0) {
+ return;
+ }
ByteBuffer buf = ByteBuffer.allocate(4 * 2);
buf.putInt(LMK_PROCREMOVE);
buf.putInt(pid);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 71d6604..a1e5947 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -196,6 +196,9 @@
String shortStringName; // caching of toShortString() result.
String stringName; // caching of toString() result.
+ boolean pendingStart; // Process start is pending.
+ long startSeq; // Seq no. indicating the latest process start associated with
+ // this process record.
// These reports are generated & stored when an app gets into an error condition.
// They will be "null" when all is OK.
@@ -211,6 +214,23 @@
// App is allowed to manage whitelists such as temporary Power Save mode whitelist.
boolean whitelistManager;
+ // Params used in starting this process.
+ String hostingType;
+ String hostingNameStr;
+ String seInfo;
+ long startTime;
+ // This will be same as {@link #uid} usually except for some apps used during factory testing.
+ int startUid;
+
+ void setStartParams(int startUid, String hostingType, String hostingNameStr, String seInfo,
+ long startTime) {
+ this.startUid = startUid;
+ this.hostingType = hostingType;
+ this.hostingNameStr = hostingNameStr;
+ this.seInfo = seInfo;
+ this.startTime = startTime;
+ }
+
void dump(PrintWriter pw, String prefix) {
final long nowUptime = SystemClock.uptimeMillis();
@@ -348,6 +368,10 @@
if (hasStartedServices) {
pw.print(prefix); pw.print("hasStartedServices="); pw.println(hasStartedServices);
}
+ if (pendingStart) {
+ pw.print(prefix); pw.print("pendingStart="); pw.println(pendingStart);
+ }
+ pw.print(prefix); pw.print("startSeq="); pw.println(startSeq);
if (setProcState > ActivityManager.PROCESS_STATE_SERVICE) {
pw.print(prefix); pw.print("lastCpuTime="); pw.print(lastCpuTime);
if (lastCpuTime > 0) {
@@ -627,9 +651,13 @@
if (noisy) {
Slog.i(TAG, "Killing " + toShortString() + " (adj " + setAdj + "): " + reason);
}
- EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
- Process.killProcessQuiet(pid);
- ActivityManagerService.killProcessGroup(uid, pid);
+ if (pid > 0) {
+ EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
+ Process.killProcessQuiet(pid);
+ ActivityManagerService.killProcessGroup(uid, pid);
+ } else {
+ pendingStart = false;
+ }
if (!persistent) {
killed = true;
killedByAm = true;
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index abb296e..2de84ab 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -662,7 +662,7 @@
* task to be trimmed as a result of that add.
*/
private boolean canAddTaskWithoutTrim(TaskRecord task) {
- return findTrimIndexForAddTask(task) == -1;
+ return findRemoveIndexForAddTask(task) == -1;
}
/**
@@ -896,7 +896,7 @@
}
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: trimming tasks for " + task);
- trimForAddTask(task);
+ removeForAddTask(task);
task.inRecents = true;
if (!isAffiliated || needAffiliationFix) {
@@ -1175,8 +1175,8 @@
* If needed, remove oldest existing entries in recents that are for the same kind
* of task as the given one.
*/
- private void trimForAddTask(TaskRecord task) {
- final int removeIndex = findTrimIndexForAddTask(task);
+ private void removeForAddTask(TaskRecord task) {
+ final int removeIndex = findRemoveIndexForAddTask(task);
if (removeIndex == -1) {
// Nothing to trim
return;
@@ -1187,7 +1187,7 @@
// callbacks here.
final TaskRecord removedTask = mTasks.remove(removeIndex);
if (removedTask != task) {
- notifyTaskRemoved(removedTask, TRIMMED);
+ notifyTaskRemoved(removedTask, !TRIMMED);
if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming task=" + removedTask
+ " for addition of task=" + task);
}
@@ -1198,7 +1198,7 @@
* Find the task that would be removed if the given {@param task} is added to the recent tasks
* list (if any).
*/
- private int findTrimIndexForAddTask(TaskRecord task) {
+ private int findRemoveIndexForAddTask(TaskRecord task) {
int recentsCount = mTasks.size();
final Intent intent = task.intent;
final boolean document = intent != null && intent.isDocument();
@@ -1241,7 +1241,6 @@
// don't need to trim it.
continue;
} else if (maxRecents > 0) {
- // Otherwise only trim if we are over our max recents for this task
--maxRecents;
if (!sameIntent || multiTasksAllowed) {
// We don't want to trim if we are not over the max allowed entries and
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 48737a5..91b3315 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -623,6 +623,9 @@
if (toStack == sourceStack) {
return false;
}
+ if (!canBeLaunchedOnDisplay(toStack.mDisplayId)) {
+ return false;
+ }
final int toStackWindowingMode = toStack.getWindowingMode();
final ActivityRecord topActivity = getTopActivity();
@@ -2251,7 +2254,7 @@
String callingPackage = "";
int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
boolean supportsPictureInPicture = false;
- Rect bounds = null;
+ Rect lastNonFullscreenBounds = null;
int minWidth = INVALID_MIN_SIZE;
int minHeight = INVALID_MIN_SIZE;
int persistTaskVersion = 0;
@@ -2336,7 +2339,7 @@
supportsPictureInPicture = Boolean.parseBoolean(attrValue);
break;
case ATTR_NON_FULLSCREEN_BOUNDS:
- bounds = Rect.unflattenFromString(attrValue);
+ lastNonFullscreenBounds = Rect.unflattenFromString(attrValue);
break;
case ATTR_MIN_WIDTH:
minWidth = Integer.parseInt(attrValue);
@@ -2376,8 +2379,7 @@
activities.add(activity);
}
} else {
- Slog.e(TAG, "restoreTask: Unexpected name=" + name);
- XmlUtils.skipCurrentTag(in);
+ handleUnknownTag(name, in);
}
}
}
@@ -2432,7 +2434,8 @@
taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid,
callingPackage, resizeMode, supportsPictureInPicture, realActivitySuspended,
userSetupComplete, minWidth, minHeight);
- task.updateOverrideConfiguration(bounds);
+ task.mLastNonFullscreenBounds = lastNonFullscreenBounds;
+ task.setBounds(lastNonFullscreenBounds);
for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
activities.get(activityNdx).setTask(task);
@@ -2441,5 +2444,11 @@
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task);
return task;
}
+
+ void handleUnknownTag(String name, XmlPullParser in)
+ throws IOException, XmlPullParserException {
+ Slog.e(TAG, "restoreTask: Unexpected name=" + name);
+ XmlUtils.skipCurrentTag(in);
+ }
}
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 6bd599b..14260c5 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -926,10 +926,9 @@
final Integer userIdInt = userId;
mUserLru.remove(userIdInt);
mUserLru.add(userIdInt);
-
- if (unlockListener != null) {
- uss.mUnlockProgress.addListener(unlockListener);
- }
+ }
+ if (unlockListener != null) {
+ uss.mUnlockProgress.addListener(unlockListener);
}
if (updateUmState) {
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index efa0bf8..2d7a6ad 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3884,7 +3884,8 @@
IsInCall = telecomManager.isInCall();
Binder.restoreCallingIdentity(ident);
- return (IsInCall || getMode() == AudioManager.MODE_IN_COMMUNICATION);
+ return (IsInCall || getMode() == AudioManager.MODE_IN_COMMUNICATION ||
+ getMode() == AudioManager.MODE_IN_CALL);
}
/**
diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
index 8fdbcf6c..3064144 100644
--- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
@@ -16,6 +16,7 @@
package com.android.server.broadcastradio;
+import android.annotation.NonNull;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -28,14 +29,16 @@
import com.android.server.SystemService;
import java.util.List;
+import java.util.Objects;
+import java.util.OptionalInt;
public class BroadcastRadioService extends SystemService {
private final ServiceImpl mServiceImpl = new ServiceImpl();
- /**
- * This field is used by native code, do not access or modify.
- */
- private final long mNativeContext = nativeInit();
+ private final com.android.server.broadcastradio.hal1.BroadcastRadioService mHal1 =
+ new com.android.server.broadcastradio.hal1.BroadcastRadioService();
+ private final com.android.server.broadcastradio.hal2.BroadcastRadioService mHal2 =
+ new com.android.server.broadcastradio.hal2.BroadcastRadioService();
private final Object mLock = new Object();
private List<RadioManager.ModuleProperties> mModules = null;
@@ -45,22 +48,18 @@
}
@Override
- protected void finalize() throws Throwable {
- nativeFinalize(mNativeContext);
- super.finalize();
- }
-
- private native long nativeInit();
- private native void nativeFinalize(long nativeContext);
- private native List<RadioManager.ModuleProperties> nativeLoadModules(long nativeContext);
- private native Tuner nativeOpenTuner(long nativeContext, int moduleId,
- RadioManager.BandConfig config, boolean withAudio, ITunerCallback callback);
-
- @Override
public void onStart() {
publishBinderService(Context.RADIO_SERVICE, mServiceImpl);
}
+ /**
+ * Finds next available index for newly loaded modules.
+ */
+ private static int getNextId(@NonNull List<RadioManager.ModuleProperties> modules) {
+ OptionalInt max = modules.stream().mapToInt(RadioManager.ModuleProperties::getId).max();
+ return max.isPresent() ? max.getAsInt() + 1 : 0;
+ }
+
private class ServiceImpl extends IRadioService.Stub {
private void enforcePolicyAccess() {
if (PackageManager.PERMISSION_GRANTED != getContext().checkCallingPermission(
@@ -75,11 +74,8 @@
synchronized (mLock) {
if (mModules != null) return mModules;
- mModules = nativeLoadModules(mNativeContext);
- if (mModules == null) {
- throw new ParcelableException(new NullPointerException(
- "couldn't load radio modules"));
- }
+ mModules = mHal1.loadModules();
+ mModules.addAll(mHal2.loadModules(getNextId(mModules)));
return mModules;
}
@@ -93,7 +89,11 @@
throw new IllegalArgumentException("Callback must not be empty");
}
synchronized (mLock) {
- return nativeOpenTuner(mNativeContext, moduleId, bandConfig, withAudio, callback);
+ if (mHal2.hasModule(moduleId)) {
+ throw new RuntimeException("Not implemented");
+ } else {
+ return mHal1.openTuner(moduleId, bandConfig, withAudio, callback);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
new file mode 100644
index 0000000..e8ac547
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
@@ -0,0 +1,66 @@
+/**
+ * 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.broadcastradio.hal1;
+
+import android.annotation.NonNull;
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.radio.IRadioService;
+import android.hardware.radio.ITuner;
+import android.hardware.radio.ITunerCallback;
+import android.hardware.radio.RadioManager;
+import android.os.ParcelableException;
+
+import com.android.server.SystemService;
+
+import java.util.List;
+import java.util.Objects;
+
+public class BroadcastRadioService {
+ /**
+ * This field is used by native code, do not access or modify.
+ */
+ private final long mNativeContext = nativeInit();
+
+ private final Object mLock = new Object();
+
+ @Override
+ protected void finalize() throws Throwable {
+ nativeFinalize(mNativeContext);
+ super.finalize();
+ }
+
+ private native long nativeInit();
+ private native void nativeFinalize(long nativeContext);
+ private native List<RadioManager.ModuleProperties> nativeLoadModules(long nativeContext);
+ private native Tuner nativeOpenTuner(long nativeContext, int moduleId,
+ RadioManager.BandConfig config, boolean withAudio, ITunerCallback callback);
+
+ public @NonNull List<RadioManager.ModuleProperties> loadModules() {
+ synchronized (mLock) {
+ return Objects.requireNonNull(nativeLoadModules(mNativeContext));
+ }
+ }
+
+ public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig,
+ boolean withAudio, @NonNull ITunerCallback callback) {
+ synchronized (mLock) {
+ return nativeOpenTuner(mNativeContext, moduleId, bandConfig, withAudio, callback);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/Convert.java b/services/core/java/com/android/server/broadcastradio/hal1/Convert.java
similarity index 97%
rename from services/core/java/com/android/server/broadcastradio/Convert.java
rename to services/core/java/com/android/server/broadcastradio/hal1/Convert.java
index 125554f..80c7762 100644
--- a/services/core/java/com/android/server/broadcastradio/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/Convert.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.broadcastradio;
+package com.android.server.broadcastradio.hal1;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/broadcastradio/Tuner.java b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
similarity index 99%
rename from services/core/java/com/android/server/broadcastradio/Tuner.java
rename to services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
index 2ea4271..cce534d 100644
--- a/services/core/java/com/android/server/broadcastradio/Tuner.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.broadcastradio;
+package com.android.server.broadcastradio.hal1;
import android.annotation.NonNull;
import android.graphics.Bitmap;
diff --git a/services/core/java/com/android/server/broadcastradio/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
similarity index 98%
rename from services/core/java/com/android/server/broadcastradio/TunerCallback.java
rename to services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
index 2460c67..673ff88 100644
--- a/services/core/java/com/android/server/broadcastradio/TunerCallback.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.broadcastradio;
+package com.android.server.broadcastradio.hal1;
import android.annotation.NonNull;
import android.hardware.radio.ITuner;
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
new file mode 100644
index 0000000..7629477
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -0,0 +1,79 @@
+/**
+ * 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.broadcastradio.hal2;
+
+import android.annotation.NonNull;
+import android.hardware.radio.RadioManager;
+import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class BroadcastRadioService {
+ private static final String TAG = "BcRadio2Srv";
+
+ private final Map<Integer, RadioModule> mModules = new HashMap<>();
+
+ private static @NonNull List<String> listByInterface(@NonNull String fqName) {
+ try {
+ IServiceManager manager = IServiceManager.getService();
+ if (manager == null) {
+ Slog.e(TAG, "Failed to get HIDL Service Manager");
+ return Collections.emptyList();
+ }
+
+ List<String> list = manager.listByInterface(fqName);
+ if (list == null) {
+ Slog.e(TAG, "Didn't get interface list from HIDL Service Manager");
+ return Collections.emptyList();
+ }
+ return list;
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed fetching interface list", ex);
+ return Collections.emptyList();
+ }
+ }
+
+ public @NonNull Collection<RadioManager.ModuleProperties> loadModules(int idx) {
+ Slog.v(TAG, "loadModules(" + idx + ")");
+
+ for (String serviceName : listByInterface(IBroadcastRadio.kInterfaceName)) {
+ Slog.v(TAG, "checking service: " + serviceName);
+
+ RadioModule module = RadioModule.tryLoadingModule(idx, serviceName);
+ if (module != null) {
+ Slog.i(TAG, "loaded broadcast radio module " + idx + ": " +
+ serviceName + " (HAL 2.0)");
+ mModules.put(idx++, module);
+ }
+ }
+
+ return mModules.values().stream().map(module -> module.mProperties).
+ collect(Collectors.toList());
+ }
+
+ public boolean hasModule(int id) {
+ return mModules.containsKey(id);
+ }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
new file mode 100644
index 0000000..c3394e9
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -0,0 +1,132 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.broadcastradio.hal2;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.broadcastradio.V2_0.Properties;
+import android.hardware.broadcastradio.V2_0.VendorKeyValue;
+import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioManager;
+import android.util.Slog;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+class Convert {
+ private static final String TAG = "BcRadio2Srv.convert";
+
+ private static @NonNull Map<String, String>
+ vendorInfoFromHal(@Nullable List<VendorKeyValue> info) {
+ if (info == null) return Collections.emptyMap();
+
+ Map<String, String> map = new HashMap<>();
+ for (VendorKeyValue kvp : info) {
+ if (kvp.key == null || kvp.value == null) {
+ Slog.w(TAG, "VendorKeyValue contains null pointers");
+ continue;
+ }
+ map.put(kvp.key, kvp.value);
+ }
+
+ return map;
+ }
+
+ private static @NonNull int[]
+ identifierTypesToProgramTypes(@NonNull int[] idTypes) {
+ Set<Integer> pTypes = new HashSet<>();
+
+ for (int idType : idTypes) {
+ switch (idType) {
+ case ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY:
+ case ProgramSelector.IDENTIFIER_TYPE_RDS_PI:
+ // TODO(b/69958423): verify AM/FM with region info
+ pTypes.add(ProgramSelector.PROGRAM_TYPE_AM);
+ pTypes.add(ProgramSelector.PROGRAM_TYPE_FM);
+ break;
+ case ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT:
+ // TODO(b/69958423): verify AM/FM with region info
+ pTypes.add(ProgramSelector.PROGRAM_TYPE_AM_HD);
+ pTypes.add(ProgramSelector.PROGRAM_TYPE_FM_HD);
+ break;
+ case ProgramSelector.IDENTIFIER_TYPE_DAB_SIDECC:
+ case ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE:
+ case ProgramSelector.IDENTIFIER_TYPE_DAB_SCID:
+ case ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY:
+ pTypes.add(ProgramSelector.PROGRAM_TYPE_DAB);
+ break;
+ case ProgramSelector.IDENTIFIER_TYPE_DRMO_SERVICE_ID:
+ case ProgramSelector.IDENTIFIER_TYPE_DRMO_FREQUENCY:
+ pTypes.add(ProgramSelector.PROGRAM_TYPE_DRMO);
+ break;
+ case ProgramSelector.IDENTIFIER_TYPE_SXM_SERVICE_ID:
+ case ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL:
+ pTypes.add(ProgramSelector.PROGRAM_TYPE_SXM);
+ break;
+ default:
+ break;
+ }
+ if (idType >= ProgramSelector.IDENTIFIER_TYPE_VENDOR_PRIMARY_START
+ && idType <= ProgramSelector.IDENTIFIER_TYPE_VENDOR_PRIMARY_END) {
+ pTypes.add(idType);
+ }
+ }
+
+ return pTypes.stream().mapToInt(Integer::intValue).toArray();
+ }
+
+ static @NonNull RadioManager.ModuleProperties
+ propertiesFromHal(int id, @NonNull String serviceName, Properties prop) {
+ Objects.requireNonNull(prop);
+
+ // TODO(b/69958423): implement region info
+ RadioManager.BandDescriptor[] bands = new RadioManager.BandDescriptor[0];
+
+ int[] supportedIdentifierTypes = prop.supportedIdentifierTypes.stream().
+ mapToInt(Integer::intValue).toArray();
+ int[] supportedProgramTypes = identifierTypesToProgramTypes(supportedIdentifierTypes);
+
+ return new RadioManager.ModuleProperties(
+ id,
+ serviceName,
+
+ // There is no Class concept in HAL 2.0.
+ RadioManager.CLASS_AM_FM,
+
+ prop.maker,
+ prop.product,
+ prop.version,
+ prop.serial,
+
+ /* HAL 2.0 only supports single tuner and audio source per
+ * HAL implementation instance. */
+ 1, // numTuners
+ 1, // numAudioSources
+ false, // isCaptureSupported
+
+ bands,
+ true, // isBgScanSupported is deprecated
+ supportedProgramTypes,
+ supportedIdentifierTypes,
+ 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
new file mode 100644
index 0000000..34c1b0c
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -0,0 +1,54 @@
+/**
+ * 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.broadcastradio.hal2;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.radio.RadioManager;
+import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.Objects;
+
+class RadioModule {
+ private static final String TAG = "BcRadio2Srv.module";
+
+ @NonNull private final IBroadcastRadio mService;
+ @NonNull public final RadioManager.ModuleProperties mProperties;
+
+ private RadioModule(@NonNull IBroadcastRadio service,
+ @NonNull RadioManager.ModuleProperties properties) {
+ mProperties = Objects.requireNonNull(properties);
+ mService = Objects.requireNonNull(service);
+ }
+
+ public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName) {
+ try {
+ IBroadcastRadio service = IBroadcastRadio.getService();
+ if (service == null) return null;
+
+ RadioManager.ModuleProperties prop =
+ Convert.propertiesFromHal(idx, fqName, service.getProperties());
+
+ return new RadioModule(service, prop);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "failed to load module " + fqName, ex);
+ return null;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/OWNERS b/services/core/java/com/android/server/connectivity/OWNERS
index 74f39a1..6f77e04 100644
--- a/services/core/java/com/android/server/connectivity/OWNERS
+++ b/services/core/java/com/android/server/connectivity/OWNERS
@@ -2,4 +2,6 @@
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 c4e6ff6..6280edb 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -644,6 +644,11 @@
int userId = UserHandle.getCallingUserId();
final int callingUid = Binder.getCallingUid();
+ if (request.isPeriodic()) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
+ "no permission to write the sync settings");
+ }
+
long identityToken = clearCallingIdentity();
try {
SyncStorageEngine.EndPoint info;
@@ -653,8 +658,6 @@
info = new SyncStorageEngine.EndPoint(account, provider, userId);
if (request.isPeriodic()) {
// Remove periodic sync.
- mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
- "no permission to write the sync settings");
getSyncManager().removePeriodicSync(info, extras,
"cancelRequest() by uid=" + callingUid);
}
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
index 07f04b1..29b322e 100644
--- a/services/core/java/com/android/server/content/SyncJobService.java
+++ b/services/core/java/com/android/server/content/SyncJobService.java
@@ -120,6 +120,7 @@
synchronized (jobParamsMap) {
JobParameters params = jobParamsMap.get(jobId);
mLogger.log("callJobFinished()",
+ " jobid=", jobId,
" needsReschedule=", needsReschedule,
" ", mLogger.jobParametersToString(params),
" why=", why);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index bdfd4bd..965159b 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -565,7 +565,7 @@
mLogger = SyncLogger.getInstance();
- SyncStorageEngine.init(context);
+ SyncStorageEngine.init(context, BackgroundThread.get().getLooper());
mSyncStorageEngine = SyncStorageEngine.getSingleton();
mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() {
@Override
@@ -735,15 +735,15 @@
}
public void onStartUser(int userHandle) {
- mLogger.log("onStartUser: user=", userHandle);
+ mSyncHandler.post(() -> mLogger.log("onStartUser: user=", userHandle));
}
public void onUnlockUser(int userHandle) {
- mLogger.log("onUnlockUser: user=", userHandle);
+ mSyncHandler.post(() -> mLogger.log("onUnlockUser: user=", userHandle));
}
public void onStopUser(int userHandle) {
- mLogger.log("onStopUser: user=", userHandle);
+ mSyncHandler.post(() -> mLogger.log("onStopUser: user=", userHandle));
}
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 3591871..e498666 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -36,6 +36,7 @@
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.RemoteCallbackList;
@@ -69,7 +70,7 @@
*
* @hide
*/
-public class SyncStorageEngine extends Handler {
+public class SyncStorageEngine {
private static final String TAG = "SyncManager";
private static final String TAG_FILE = "SyncManagerFile";
@@ -462,7 +463,10 @@
private boolean mGrantSyncAdaptersAccountAccess;
- private SyncStorageEngine(Context context, File dataDir) {
+ private final MyHandler mHandler;
+
+ private SyncStorageEngine(Context context, File dataDir, Looper looper) {
+ mHandler = new MyHandler(looper);
mContext = context;
sSyncStorageEngine = this;
@@ -491,15 +495,15 @@
}
public static SyncStorageEngine newTestInstance(Context context) {
- return new SyncStorageEngine(context, context.getFilesDir());
+ return new SyncStorageEngine(context, context.getFilesDir(), Looper.getMainLooper());
}
- public static void init(Context context) {
+ public static void init(Context context, Looper looper) {
if (sSyncStorageEngine != null) {
return;
}
File dataDir = Environment.getDataDirectory();
- sSyncStorageEngine = new SyncStorageEngine(context, dataDir);
+ sSyncStorageEngine = new SyncStorageEngine(context, dataDir, looper);
}
public static SyncStorageEngine getSingleton() {
@@ -527,14 +531,21 @@
}
}
- @Override public void handleMessage(Message msg) {
- if (msg.what == MSG_WRITE_STATUS) {
- synchronized (mAuthorities) {
- writeStatusLocked();
- }
- } else if (msg.what == MSG_WRITE_STATISTICS) {
- synchronized (mAuthorities) {
- writeStatisticsLocked();
+ private class MyHandler extends Handler {
+ public MyHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_WRITE_STATUS) {
+ synchronized (mAuthorities) {
+ writeStatusLocked();
+ }
+ } else if (msg.what == MSG_WRITE_STATISTICS) {
+ synchronized (mAuthorities) {
+ writeStatisticsLocked();
+ }
}
}
}
@@ -1202,14 +1213,14 @@
if (writeStatusNow) {
writeStatusLocked();
- } else if (!hasMessages(MSG_WRITE_STATUS)) {
- sendMessageDelayed(obtainMessage(MSG_WRITE_STATUS),
+ } else if (!mHandler.hasMessages(MSG_WRITE_STATUS)) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_WRITE_STATUS),
WRITE_STATUS_DELAY);
}
if (writeStatisticsNow) {
writeStatisticsLocked();
- } else if (!hasMessages(MSG_WRITE_STATISTICS)) {
- sendMessageDelayed(obtainMessage(MSG_WRITE_STATISTICS),
+ } else if (!mHandler.hasMessages(MSG_WRITE_STATISTICS)) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_WRITE_STATISTICS),
WRITE_STATISTICS_DELAY);
}
}
@@ -2102,7 +2113,7 @@
// The file is being written, so we don't need to have a scheduled
// write until the next change.
- removeMessages(MSG_WRITE_STATUS);
+ mHandler.removeMessages(MSG_WRITE_STATUS);
FileOutputStream fos = null;
try {
@@ -2210,7 +2221,7 @@
// The file is being written, so we don't need to have a scheduled
// write until the next change.
- removeMessages(MSG_WRITE_STATISTICS);
+ mHandler.removeMessages(MSG_WRITE_STATISTICS);
FileOutputStream fos = null;
try {
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 9a6e609..6c5bfc7 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -24,6 +24,7 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.display.BrightnessConfiguration;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -34,7 +35,6 @@
import android.util.EventLog;
import android.util.MathUtils;
import android.util.Slog;
-import android.util.Spline;
import android.util.TimeUtils;
import java.io.PrintWriter;
@@ -76,9 +76,8 @@
// The light sensor, or null if not available or needed.
private final Sensor mLightSensor;
- // The auto-brightness spline adjustment.
- // The brightness values have been scaled to a range of 0..1.
- private final Spline mScreenAutoBrightnessSpline;
+ // The mapper to translate ambient lux to screen brightness in the range [0, 1.0].
+ private final BrightnessMappingStrategy mBrightnessMapper;
// The minimum and maximum screen brightnesses.
private final int mScreenBrightnessRangeMinimum;
@@ -186,7 +185,7 @@
private float mBrightnessAdjustmentSampleOldGamma;
public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
- SensorManager sensorManager, Spline autoBrightnessSpline, int lightSensorWarmUpTime,
+ SensorManager sensorManager, BrightnessMappingStrategy mapper, int lightSensorWarmUpTime,
int brightnessMin, int brightnessMax, float dozeScaleFactor,
int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
@@ -194,7 +193,7 @@
HysteresisLevels dynamicHysteresis) {
mCallbacks = callbacks;
mSensorManager = sensorManager;
- mScreenAutoBrightnessSpline = autoBrightnessSpline;
+ mBrightnessMapper = mapper;
mScreenBrightnessRangeMinimum = brightnessMin;
mScreenBrightnessRangeMaximum = brightnessMax;
mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
@@ -228,15 +227,16 @@
return mScreenAutoBrightness;
}
- public void configure(boolean enable, float adjustment, boolean dozing,
- boolean userInitiatedChange) {
+ public void configure(boolean enable, @Nullable BrightnessConfiguration configuration,
+ float adjustment, boolean dozing, boolean userInitiatedChange) {
// While dozing, the application processor may be suspended which will prevent us from
// receiving new information from the light sensor. On some devices, we may be able to
// switch to a wake-up light sensor instead but for now we will simply disable the sensor
// and hold onto the last computed screen auto brightness. We save the dozing flag for
// debugging purposes.
mDozing = dozing;
- boolean changed = setLightSensorEnabled(enable && !dozing);
+ boolean changed = setBrightnessConfiguration(configuration);
+ changed |= setLightSensorEnabled(enable && !dozing);
if (enable && !dozing && userInitiatedChange) {
prepareBrightnessAdjustmentSample();
}
@@ -246,10 +246,13 @@
}
}
+ public boolean setBrightnessConfiguration(BrightnessConfiguration configuration) {
+ return mBrightnessMapper.setBrightnessConfiguration(configuration);
+ }
+
public void dump(PrintWriter pw) {
pw.println();
pw.println("Automatic Brightness Controller Configuration:");
- pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
@@ -274,9 +277,13 @@
mInitialHorizonAmbientLightRingBuffer);
pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
pw.println(" mScreenAutoBrightnessAdjustment=" + mScreenAutoBrightnessAdjustment);
- pw.println(" mScreenAutoBrightnessAdjustmentMaxGamma=" + mScreenAutoBrightnessAdjustmentMaxGamma);
+ pw.println(" mScreenAutoBrightnessAdjustmentMaxGamma="
+ + mScreenAutoBrightnessAdjustmentMaxGamma);
pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
pw.println(" mDozing=" + mDozing);
+
+ pw.println();
+ mBrightnessMapper.dump(pw);
}
private boolean setLightSensorEnabled(boolean enable) {
@@ -533,7 +540,7 @@
return;
}
- float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
+ float value = mBrightnessMapper.getBrightness(mAmbientLux);
float gamma = 1.0f;
if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
new file mode 100644
index 0000000..3b9d40f
--- /dev/null
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -0,0 +1,289 @@
+/*
+ * 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.display;
+
+import android.annotation.Nullable;
+import android.hardware.display.BrightnessConfiguration;
+import android.os.PowerManager;
+import android.util.MathUtils;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.Spline;
+
+import com.android.internal.util.Preconditions;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+
+/**
+ * A utility to map from an ambient brightness to a display's "backlight" brightness based on the
+ * available display information and brightness configuration.
+ *
+ * Note that without a mapping from the nits to a display backlight level, any
+ * {@link BrightnessConfiguration}s that are set are just ignored.
+ */
+public abstract class BrightnessMappingStrategy {
+ private static final String TAG = "BrightnessMappingStrategy";
+ private static final boolean DEBUG = false;
+
+ @Nullable
+ public static BrightnessMappingStrategy create(
+ float[] luxLevels, int[] brightnessLevelsBacklight, float[] brightnessLevelsNits,
+ float[] nitsRange, int[] backlightRange) {
+ if (isValidMapping(nitsRange, backlightRange)
+ && isValidMapping(luxLevels, brightnessLevelsNits)) {
+ BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
+ builder.setCurve(luxLevels, brightnessLevelsNits);
+ return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange);
+ } else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) {
+ return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight);
+ } else {
+ return null;
+ }
+ }
+
+ private static boolean isValidMapping(float[] x, float[] y) {
+ if (x == null || y == null || x.length == 0 || y.length == 0) {
+ return false;
+ }
+ if (x.length != y.length) {
+ return false;
+ }
+ final int N = x.length;
+ float prevX = x[0];
+ float prevY = y[0];
+ if (prevX < 0 || prevY < 0 || Float.isNaN(prevX) || Float.isNaN(prevY)) {
+ return false;
+ }
+ for (int i = 1; i < N; i++) {
+ if (prevX >= x[i] || prevY > y[i]) {
+ return false;
+ }
+ if (Float.isNaN(x[i]) || Float.isNaN(y[i])) {
+ return false;
+ }
+ prevX = x[i];
+ prevY = y[i];
+ }
+ return true;
+ }
+
+ private static boolean isValidMapping(float[] x, int[] y) {
+ if (x == null || y == null || x.length == 0 || y.length == 0) {
+ return false;
+ }
+ if (x.length != y.length) {
+ return false;
+ }
+ final int N = x.length;
+ float prevX = x[0];
+ int prevY = y[0];
+ if (prevX < 0 || prevY < 0 || Float.isNaN(prevX)) {
+ return false;
+ }
+ for (int i = 1; i < N; i++) {
+ if (prevX >= x[i] || prevY > y[i]) {
+ return false;
+ }
+ if (Float.isNaN(x[i])) {
+ return false;
+ }
+ prevX = x[i];
+ prevY = y[i];
+ }
+ return true;
+ }
+
+ /**
+ * Sets the {@link BrightnessConfiguration}.
+ *
+ * @param config The new configuration. If {@code null} is passed, the default configuration is
+ * used.
+ * @return Whether the brightness configuration has changed.
+ */
+ public abstract boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config);
+
+ /**
+ * Returns the desired brightness of the display based on the current ambient lux.
+ *
+ * The returned brightness will be in the range [0, 1.0], where 1.0 is the display at max
+ * brightness and 0 is the display at minimum brightness.
+ *
+ * @param lux The current ambient brightness in lux.
+ * @return The desired brightness of the display compressed to the range [0, 1.0].
+ */
+ public abstract float getBrightness(float lux);
+
+ public abstract void dump(PrintWriter pw);
+
+ private static float normalizeAbsoluteBrightness(int brightness) {
+ brightness = MathUtils.constrain(brightness,
+ PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
+ return (float) brightness / PowerManager.BRIGHTNESS_ON;
+ }
+
+
+ /**
+ * A {@link BrightnessMappingStrategy} that maps from ambient room brightness directly to the
+ * backlight of the display.
+ *
+ * Since we don't have information about the display's physical brightness, any brightness
+ * configurations that are set are just ignored.
+ */
+ private static class SimpleMappingStrategy extends BrightnessMappingStrategy {
+ private final Spline mSpline;
+
+ public SimpleMappingStrategy(float[] lux, int[] brightness) {
+ Preconditions.checkArgument(lux.length != 0 && brightness.length != 0,
+ "Lux and brightness arrays must not be empty!");
+ Preconditions.checkArgument(lux.length == brightness.length,
+ "Lux and brightness arrays must be the same length!");
+ Preconditions.checkArrayElementsInRange(lux, 0, Float.MAX_VALUE, "lux");
+ Preconditions.checkArrayElementsInRange(brightness,
+ 0, Integer.MAX_VALUE, "brightness");
+
+ final int N = brightness.length;
+ float[] x = new float[N];
+ float[] y = new float[N];
+ for (int i = 0; i < N; i++) {
+ x[i] = lux[i];
+ y[i] = normalizeAbsoluteBrightness(brightness[i]);
+ }
+
+ mSpline = Spline.createSpline(x, y);
+ if (DEBUG) {
+ Slog.d(TAG, "Auto-brightness spline: " + mSpline);
+ for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
+ Slog.d(TAG, String.format(" %7.1f: %7.1f", v, mSpline.interpolate(v)));
+ }
+ }
+ }
+
+ @Override
+ public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) {
+ Slog.e(TAG,
+ "setBrightnessConfiguration called on device without display information.");
+ return false;
+ }
+
+ @Override
+ public float getBrightness(float lux) {
+ return mSpline.interpolate(lux);
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.println("SimpleMappingStrategy");
+ pw.println(" mSpline=" + mSpline);
+ }
+ }
+
+ /** A {@link BrightnessMappingStrategy} that maps from ambient room brightness to the physical
+ * range of the display, rather than to the range of the backlight control (typically 0-255).
+ *
+ * By mapping through the physical brightness, the curve becomes portable across devices and
+ * gives us more resolution in the resulting mapping.
+ */
+ @VisibleForTesting
+ static class PhysicalMappingStrategy extends BrightnessMappingStrategy {
+ // The current brightness configuration.
+ private BrightnessConfiguration mConfig;
+
+ // A spline mapping from the current ambient light in lux to the desired display brightness
+ // in nits.
+ private Spline mBrightnessSpline;
+
+ // A spline mapping from nits to the corresponding backlight value, normalized to the range
+ // [0, 1.0].
+ private final Spline mBacklightSpline;
+
+ // The default brightness configuration.
+ private final BrightnessConfiguration mDefaultConfig;
+
+ public PhysicalMappingStrategy(BrightnessConfiguration config,
+ float[] nits, int[] backlight) {
+ Preconditions.checkArgument(nits.length != 0 && backlight.length != 0,
+ "Nits and backlight arrays must not be empty!");
+ Preconditions.checkArgument(nits.length == backlight.length,
+ "Nits and backlight arrays must be the same length!");
+ Preconditions.checkNotNull(config);
+ Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits");
+ Preconditions.checkArrayElementsInRange(backlight,
+ PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON, "backlight");
+
+ // Setup the backlight spline
+ final int N = nits.length;
+ float[] x = new float[N];
+ float[] y = new float[N];
+ for (int i = 0; i < N; i++) {
+ x[i] = nits[i];
+ y[i] = normalizeAbsoluteBrightness(backlight[i]);
+ }
+
+ mBacklightSpline = Spline.createSpline(x, y);
+ if (DEBUG) {
+ Slog.d(TAG, "Backlight spline: " + mBacklightSpline);
+ for (float v = 1f; v < nits[nits.length - 1] * 1.25f; v *= 1.25f) {
+ Slog.d(TAG, String.format(
+ " %7.1f: %7.1f", v, mBacklightSpline.interpolate(v)));
+ }
+ }
+
+ mDefaultConfig = config;
+ setBrightnessConfiguration(config);
+ }
+
+ @Override
+ public boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config) {
+ if (config == null) {
+ config = mDefaultConfig;
+ }
+ if (config.equals(mConfig)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Tried to set an identical brightness config, ignoring");
+ }
+ return false;
+ }
+
+ Pair<float[], float[]> curve = config.getCurve();
+ mBrightnessSpline = Spline.createSpline(curve.first /*lux*/, curve.second /*nits*/);
+ if (DEBUG) {
+ Slog.d(TAG, "Brightness spline: " + mBrightnessSpline);
+ final float[] lux = curve.first;
+ for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
+ Slog.d(TAG, String.format(
+ " %7.1f: %7.1f", v, mBrightnessSpline.interpolate(v)));
+ }
+ }
+ mConfig = config;
+ return true;
+ }
+
+ @Override
+ public float getBrightness(float lux) {
+ return mBacklightSpline.interpolate(mBrightnessSpline.interpolate(lux));
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.println("PhysicalMappingStrategy");
+ pw.println(" mConfig=" + mConfig);
+ pw.println(" mBrightnessSpline=" + mBrightnessSpline);
+ pw.println(" mBacklightSpline=" + mBacklightSpline);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 61b6fa0..42247f9 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -500,13 +500,30 @@
}
public void dump(PrintWriter pw) {
- synchronized (mEventsLock) {
- pw.println("BrightnessTracker state:");
- pw.println(" mEvents.size=" + mEvents.size());
- pw.println(" mEventsDirty=" + mEventsDirty);
- }
+ pw.println("BrightnessTracker state:");
synchronized (mDataCollectionLock) {
pw.println(" mLastSensorReadings.size=" + mLastSensorReadings.size());
+ if (!mLastSensorReadings.isEmpty()) {
+ pw.println(" mLastSensorReadings time span "
+ + mLastSensorReadings.peekFirst().timestamp + "->"
+ + mLastSensorReadings.peekLast().timestamp);
+ }
+ }
+ synchronized (mEventsLock) {
+ pw.println(" mEventsDirty=" + mEventsDirty);
+ pw.println(" mEvents.size=" + mEvents.size());
+ 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 + ", {");
+ for (int j = 0; j < events[i].luxValues.length; ++j){
+ if (j != 0) {
+ pw.print(", ");
+ }
+ pw.print("(" + events[i].luxValues[j] + "," + events[i].luxTimestamps[j] + ")");
+ }
+ pw.println("}");
+ }
}
}
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 75f3056..b3d309d 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -189,7 +189,7 @@
mController = new ColorDisplayController(getContext(), mCurrentUser);
mController.setListener(this);
- setCoefficientMatrix(getContext());
+ setCoefficientMatrix(getContext(), DisplayTransformManager.isNativeModeEnabled());
// Prepare color transformation matrix.
setMatrix(mController.getColorTemperature(), mMatrixNight);
@@ -287,17 +287,24 @@
}
@Override
- public void onDisplayColorModeChanged(int colorMode) {
- final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
- dtm.setColorMode(colorMode);
+ public void onDisplayColorModeChanged(int mode) {
+ // Cancel the night display tint animator if it's running.
+ if (mColorMatrixAnimator != null) {
+ mColorMatrixAnimator.cancel();
+ }
- setCoefficientMatrix(getContext());
+ setCoefficientMatrix(getContext(), mode == ColorDisplayController.COLOR_MODE_SATURATED);
setMatrix(mController.getColorTemperature(), mMatrixNight);
- applyTint(true);
+
+ final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+ dtm.setColorMode(mode, mIsActivated ? mMatrixNight : MATRIX_IDENTITY);
}
- private void setCoefficientMatrix(Context context) {
- final boolean isNative = DisplayTransformManager.isNativeModeEnabled();
+ /**
+ * Set coefficients based on native mode. Use DisplayTransformManager#isNativeModeEnabled while
+ * setting is stable; when setting is changing, pass native mode selection directly.
+ */
+ private void setCoefficientMatrix(Context context, boolean isNative) {
final String[] coefficients = context.getResources().getStringArray(isNative
? R.array.config_nightDisplayColorTemperatureCoefficientsNative
: R.array.config_nightDisplayColorTemperatureCoefficients);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c1bfa47..9b97934 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -29,6 +29,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -37,6 +38,7 @@
import android.graphics.Point;
import android.hardware.SensorManager;
import android.hardware.display.BrightnessChangeEvent;
+import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayViewport;
@@ -62,6 +64,7 @@
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
+import android.os.UserManager;
import android.text.TextUtils;
import android.util.IntArray;
import android.util.Slog;
@@ -70,6 +73,7 @@
import android.view.DisplayInfo;
import android.view.Surface;
+import com.android.internal.util.Preconditions;
import com.android.server.AnimationThread;
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
@@ -145,6 +149,7 @@
private static final int MSG_REQUEST_TRAVERSAL = 4;
private static final int MSG_UPDATE_VIEWPORT = 5;
private static final int MSG_REGISTER_BRIGHTNESS_TRACKER = 6;
+ private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 7;
private final Context mContext;
private final DisplayManagerHandler mHandler;
@@ -219,6 +224,9 @@
// The virtual display adapter, or null if not registered.
private VirtualDisplayAdapter mVirtualDisplayAdapter;
+ // The User ID of the current user
+ private @UserIdInt int mCurrentUserId;
+
// The stable device screen height and width. These are not tied to a specific display, even
// the default display, because they need to be stable over the course of the device's entire
// life, even if the default display changes (e.g. a new monitor is plugged into a PC-like
@@ -278,17 +286,18 @@
mDisplayAdapterListener = new DisplayAdapterListener();
mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_defaultDisplayDefaultColorMode);
+ com.android.internal.R.integer.config_defaultDisplayDefaultColorMode);
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
mBrightnessTracker = new BrightnessTracker(context, null);
+ mCurrentUserId = UserHandle.USER_SYSTEM;
}
public void setupSchedulerPolicies() {
// android.display and android.anim is critical to user experience and we should make sure
- // it is not in the default foregroup groups, add it to top-app to make sure it uses all the
- // cores and scheduling settings for top-app when it runs.
+ // it is not in the default foregroup groups, add it to top-app to make sure it uses all
+ // the cores and scheduling settings for top-app when it runs.
Process.setThreadGroupAndCpuset(DisplayThread.get().getThreadId(),
Process.THREAD_GROUP_TOP_APP);
Process.setThreadGroupAndCpuset(AnimationThread.get().getThreadId(),
@@ -342,6 +351,19 @@
}
}
+ @Override
+ public void onSwitchUser(@UserIdInt int newUserId) {
+ final int userSerial = getUserManager().getUserSerialNumber(newUserId);
+ synchronized (mSyncRoot) {
+ if (mCurrentUserId != newUserId) {
+ mCurrentUserId = newUserId;
+ BrightnessConfiguration config =
+ mPersistentDataStore.getBrightnessConfiguration(userSerial);
+ mDisplayPowerController.setBrightnessConfiguration(config);
+ }
+ }
+ }
+
// TODO: Use dependencies or a boot phase
public void windowManagerAndInputReady() {
synchronized (mSyncRoot) {
@@ -985,6 +1007,30 @@
}
}
+ private void setBrightnessConfigurationForUserInternal(
+ @NonNull BrightnessConfiguration c, @UserIdInt int userId) {
+ final int userSerial = getUserManager().getUserSerialNumber(userId);
+ synchronized (mSyncRoot) {
+ try {
+ mPersistentDataStore.setBrightnessConfigurationForUser(c, userSerial);
+ } finally {
+ mPersistentDataStore.saveIfNeeded();
+ }
+ if (userId == mCurrentUserId) {
+ mDisplayPowerController.setBrightnessConfiguration(c);
+ }
+ }
+ }
+
+ private void loadBrightnessConfiguration() {
+ synchronized (mSyncRoot) {
+ final int userSerial = getUserManager().getUserSerialNumber(mCurrentUserId);
+ BrightnessConfiguration config =
+ mPersistentDataStore.getBrightnessConfiguration(userSerial);
+ mDisplayPowerController.setBrightnessConfiguration(config);
+ }
+ }
+
// Updates all existing logical displays given the current set of display devices.
// Removes invalid logical displays.
// Sends notifications if needed.
@@ -1229,6 +1275,10 @@
return mProjectionService;
}
+ private UserManager getUserManager() {
+ return mContext.getSystemService(UserManager.class);
+ }
+
private void dumpInternal(PrintWriter pw) {
pw.println("DISPLAY MANAGER (dumpsys display)");
@@ -1371,6 +1421,10 @@
case MSG_REGISTER_BRIGHTNESS_TRACKER:
mBrightnessTracker.start();
break;
+
+ case MSG_LOAD_BRIGHTNESS_CONFIGURATION:
+ loadBrightnessConfiguration();
+ break;
}
}
}
@@ -1800,6 +1854,27 @@
}
}
+ @Override // Binder call
+ public void setBrightnessConfigurationForUser(
+ BrightnessConfiguration c, @UserIdInt int userId) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS,
+ "Permission required to change the display's brightness configuration");
+ if (userId != UserHandle.getCallingUserId()) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS,
+ "Permission required to change the display brightness"
+ + " configuration of another user");
+ }
+ Preconditions.checkNotNull(c);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ setBrightnessConfigurationForUserInternal(c, userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean validatePackageName(int uid, String packageName) {
if (packageName != null) {
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
@@ -1871,18 +1946,24 @@
mDisplayPowerController = new DisplayPowerController(
mContext, callbacks, handler, sensorManager, blanker);
}
+
+ mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATION);
}
@Override
public boolean requestPowerState(DisplayPowerRequest request,
boolean waitForNegativeProximity) {
- return mDisplayPowerController.requestPowerState(request,
- waitForNegativeProximity);
+ synchronized (mSyncRoot) {
+ return mDisplayPowerController.requestPowerState(request,
+ waitForNegativeProximity);
+ }
}
@Override
public boolean isProximitySensorAvailable() {
- return mDisplayPowerController.isProximitySensorAvailable();
+ synchronized (mSyncRoot) {
+ return mDisplayPowerController.isProximitySensorAvailable();
+ }
}
@Override
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 29a007a..a2d9548 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -26,10 +26,12 @@
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
@@ -93,6 +95,8 @@
private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2;
private static final int MSG_SCREEN_ON_UNBLOCKED = 3;
private static final int MSG_SCREEN_OFF_UNBLOCKED = 4;
+ private static final int MSG_CONFIGURE_BRIGHTNESS = 5;
+ private static final int MSG_USER_SWITCH = 6;
private static final int PROXIMITY_UNKNOWN = -1;
private static final int PROXIMITY_NEGATIVE = 0;
@@ -285,6 +289,14 @@
// The controller for the automatic brightness level.
private AutomaticBrightnessController mAutomaticBrightnessController;
+ // The default brightness configuration. Used for whenever we don't have a valid brightness
+ // configuration set. This is typically seen with users that don't have a brightness
+ // configuration that's different from the default.
+ private BrightnessConfiguration mDefaultBrightnessConfiguration;
+
+ // The current brightness configuration.
+ private BrightnessConfiguration mBrightnessConfiguration;
+
// Animators.
private ObjectAnimator mColorFadeOnAnimator;
private ObjectAnimator mColorFadeOffAnimator;
@@ -333,7 +345,8 @@
screenBrightnessSettingMinimum, mScreenBrightnessDimConfig),
mScreenBrightnessDarkConfig);
- mScreenBrightnessRangeMaximum = PowerManager.BRIGHTNESS_ON;
+ mScreenBrightnessRangeMaximum = clampAbsoluteBrightness(resources.getInteger(
+ com.android.internal.R.integer.config_screenBrightnessSettingMaximum));
mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
com.android.internal.R.bool.config_automatic_brightness_available);
@@ -348,60 +361,60 @@
mSkipScreenOnBrightnessRamp = resources.getBoolean(
com.android.internal.R.bool.config_skipScreenOnBrightnessRamp);
- int lightSensorRate = resources.getInteger(
- com.android.internal.R.integer.config_autoBrightnessLightSensorRate);
- int initialLightSensorRate = resources.getInteger(
- com.android.internal.R.integer.config_autoBrightnessInitialLightSensorRate);
- if (initialLightSensorRate == -1) {
- initialLightSensorRate = lightSensorRate;
- } else if (initialLightSensorRate > lightSensorRate) {
- Slog.w(TAG, "Expected config_autoBrightnessInitialLightSensorRate ("
- + initialLightSensorRate + ") to be less than or equal to "
- + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
- }
- long brighteningLightDebounce = resources.getInteger(
- com.android.internal.R.integer.config_autoBrightnessBrighteningLightDebounce);
- long darkeningLightDebounce = resources.getInteger(
- com.android.internal.R.integer.config_autoBrightnessDarkeningLightDebounce);
- boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
- com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
- int ambientLightHorizon = resources.getInteger(
- com.android.internal.R.integer.config_autoBrightnessAmbientLightHorizon);
- float autoBrightnessAdjustmentMaxGamma = resources.getFraction(
- com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,
- 1, 1);
-
- int[] brightLevels = resources.getIntArray(
- com.android.internal.R.array.config_dynamicHysteresisBrightLevels);
- int[] darkLevels = resources.getIntArray(
- com.android.internal.R.array.config_dynamicHysteresisDarkLevels);
- int[] luxLevels = resources.getIntArray(
- com.android.internal.R.array.config_dynamicHysteresisLuxLevels);
- HysteresisLevels dynamicHysteresis = new HysteresisLevels(
- brightLevels, darkLevels, luxLevels);
-
if (mUseSoftwareAutoBrightnessConfig) {
- int[] lux = resources.getIntArray(
- com.android.internal.R.array.config_autoBrightnessLevels);
- int[] screenBrightness = resources.getIntArray(
+ float[] luxLevels = getLuxLevels(resources.getIntArray(
+ com.android.internal.R.array.config_autoBrightnessLevels));
+ int[] backlightValues = resources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
- int lightSensorWarmUpTimeConfig = resources.getInteger(
- com.android.internal.R.integer.config_lightSensorWarmupTime);
+ float[] brightnessValuesNits = getFloatArray(resources.obtainTypedArray(
+ com.android.internal.R.array.config_autoBrightnessDisplayValuesNits));
+
+ final float screenMinimumNits = resources.getFloat(
+ com.android.internal.R.dimen.config_screenBrightnessMinimumNits);
+ final float screenMaximumNits = resources.getFloat(
+ com.android.internal.R.dimen.config_screenBrightnessMaximumNits);
+
final float dozeScaleFactor = resources.getFraction(
com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
1, 1);
- Spline screenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
- if (screenAutoBrightnessSpline == null) {
- Slog.e(TAG, "Error in config.xml. config_autoBrightnessLcdBacklightValues "
- + "(size " + screenBrightness.length + ") "
- + "must be monotic and have exactly one more entry than "
- + "config_autoBrightnessLevels (size " + lux.length + ") "
- + "which must be strictly increasing. "
- + "Auto-brightness will be disabled.");
- mUseSoftwareAutoBrightnessConfig = false;
- } else {
- int bottom = clampAbsoluteBrightness(screenBrightness[0]);
+ int[] brightLevels = resources.getIntArray(
+ com.android.internal.R.array.config_dynamicHysteresisBrightLevels);
+ int[] darkLevels = resources.getIntArray(
+ com.android.internal.R.array.config_dynamicHysteresisDarkLevels);
+ int[] luxHysteresisLevels = resources.getIntArray(
+ com.android.internal.R.array.config_dynamicHysteresisLuxLevels);
+ HysteresisLevels dynamicHysteresis = new HysteresisLevels(
+ brightLevels, darkLevels, luxHysteresisLevels);
+
+ long brighteningLightDebounce = resources.getInteger(
+ com.android.internal.R.integer.config_autoBrightnessBrighteningLightDebounce);
+ long darkeningLightDebounce = resources.getInteger(
+ com.android.internal.R.integer.config_autoBrightnessDarkeningLightDebounce);
+ boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
+ com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
+ int ambientLightHorizon = resources.getInteger(
+ com.android.internal.R.integer.config_autoBrightnessAmbientLightHorizon);
+ float autoBrightnessAdjustmentMaxGamma = resources.getFraction(
+ com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,
+ 1, 1);
+
+ int lightSensorWarmUpTimeConfig = resources.getInteger(
+ com.android.internal.R.integer.config_lightSensorWarmupTime);
+ int lightSensorRate = resources.getInteger(
+ com.android.internal.R.integer.config_autoBrightnessLightSensorRate);
+ int initialLightSensorRate = resources.getInteger(
+ com.android.internal.R.integer.config_autoBrightnessInitialLightSensorRate);
+ if (initialLightSensorRate == -1) {
+ initialLightSensorRate = lightSensorRate;
+ } else if (initialLightSensorRate > lightSensorRate) {
+ Slog.w(TAG, "Expected config_autoBrightnessInitialLightSensorRate ("
+ + initialLightSensorRate + ") to be less than or equal to "
+ + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
+ }
+
+ if (backlightValues != null && backlightValues.length > 0) {
+ final int bottom = backlightValues[0];
if (mScreenBrightnessDarkConfig > bottom) {
Slog.w(TAG, "config_screenBrightnessDark (" + mScreenBrightnessDarkConfig
+ ") should be less than or equal to the first value of "
@@ -411,13 +424,24 @@
if (bottom < screenBrightnessRangeMinimum) {
screenBrightnessRangeMinimum = bottom;
}
+ }
+
+ float[] nitsRange = { screenMinimumNits, screenMaximumNits };
+ int[] backlightRange = { screenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum };
+
+ BrightnessMappingStrategy mapper = BrightnessMappingStrategy.create(
+ luxLevels, backlightValues, brightnessValuesNits,
+ nitsRange, backlightRange);
+ if (mapper != null) {
mAutomaticBrightnessController = new AutomaticBrightnessController(this,
- handler.getLooper(), sensorManager, screenAutoBrightnessSpline,
- lightSensorWarmUpTimeConfig, screenBrightnessRangeMinimum,
- mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
- initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
+ handler.getLooper(), sensorManager, mapper, lightSensorWarmUpTimeConfig,
+ screenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum,
+ dozeScaleFactor, lightSensorRate, initialLightSensorRate,
+ brighteningLightDebounce, darkeningLightDebounce,
autoBrightnessResetAmbientLuxAfterWarmUp, ambientLightHorizon,
autoBrightnessAdjustmentMaxGamma, dynamicHysteresis);
+ } else {
+ mUseSoftwareAutoBrightnessConfig = false;
}
}
@@ -444,6 +468,25 @@
}
+ private static float[] getLuxLevels(int[] lux) {
+ // The first control point is implicit and always at 0 lux.
+ float[] levels = new float[lux.length + 1];
+ for (int i = 0; i < lux.length; i++) {
+ levels[i + 1] = (float) lux[i];
+ }
+ return levels;
+ }
+
+ private static float[] getFloatArray(TypedArray array) {
+ final int N = array.length();
+ float[] vals = new float[N];
+ for (int i = 0; i < N; i++) {
+ vals[i] = array.getFloat(i, -1.0f);
+ }
+ array.recycle();
+ return vals;
+ }
+
/**
* Returns true if the proximity sensor screen-off function is available.
*/
@@ -512,7 +555,6 @@
if (!mPendingUpdatePowerStateLocked) {
mPendingUpdatePowerStateLocked = true;
Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
- msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
}
@@ -691,8 +733,8 @@
final boolean userInitiatedChange = autoBrightnessAdjustmentChanged
&& mPowerRequest.brightnessSetByUser;
mAutomaticBrightnessController.configure(autoBrightnessEnabled,
- mPowerRequest.screenAutoBrightnessAdjustment, state != Display.STATE_ON,
- userInitiatedChange);
+ mBrightnessConfiguration, mPowerRequest.screenAutoBrightnessAdjustment,
+ state != Display.STATE_ON, userInitiatedChange);
}
// Apply brightness boost.
@@ -874,6 +916,11 @@
sendUpdatePowerState();
}
+ public void setBrightnessConfiguration(BrightnessConfiguration c) {
+ Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS, c);
+ msg.sendToTarget();
+ }
+
private void blockScreenOn() {
if (mPendingScreenOnUnblocker == null) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
@@ -1241,7 +1288,6 @@
// Need to wait a little longer.
// Debounce again later. We continue holding a wake lock while waiting.
Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED);
- msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime);
}
}
@@ -1402,39 +1448,6 @@
}
}
- private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) {
- if (lux == null || lux.length == 0 || brightness == null || brightness.length == 0) {
- Slog.e(TAG, "Could not create auto-brightness spline.");
- return null;
- }
- try {
- final int n = brightness.length;
- float[] x = new float[n];
- float[] y = new float[n];
- y[0] = normalizeAbsoluteBrightness(brightness[0]);
- for (int i = 1; i < n; i++) {
- x[i] = lux[i - 1];
- y[i] = normalizeAbsoluteBrightness(brightness[i]);
- }
-
- Spline spline = Spline.createSpline(x, y);
- if (DEBUG) {
- Slog.d(TAG, "Auto-brightness spline: " + spline);
- for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
- Slog.d(TAG, String.format(" %7.1f: %7.1f", v, spline.interpolate(v)));
- }
- }
- return spline;
- } catch (IllegalArgumentException ex) {
- Slog.e(TAG, "Could not create auto-brightness spline.", ex);
- return null;
- }
- }
-
- private static float normalizeAbsoluteBrightness(int value) {
- return (float)clampAbsoluteBrightness(value) / PowerManager.BRIGHTNESS_ON;
- }
-
private static int clampAbsoluteBrightness(int value) {
return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
}
@@ -1467,6 +1480,11 @@
updatePowerState();
}
break;
+ case MSG_CONFIGURE_BRIGHTNESS:
+ BrightnessConfiguration c = (BrightnessConfiguration) msg.obj;
+ mBrightnessConfiguration = c != null ? c : mDefaultBrightnessConfiguration;
+ updatePowerState();
+ break;
}
}
}
@@ -1492,17 +1510,14 @@
@Override
public void onScreenOn() {
Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this);
- msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
}
private final class ScreenOffUnblocker implements WindowManagerPolicy.ScreenOffListener {
-
@Override
public void onScreenOff() {
Message msg = mHandler.obtainMessage(MSG_SCREEN_OFF_UNBLOCKED, this);
- msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
}
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index 338e331..000fcf3 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -222,7 +222,7 @@
return SystemProperties.getBoolean(PERSISTENT_PROPERTY_NATIVE_MODE, false);
}
- public boolean setColorMode(int colorMode) {
+ public boolean setColorMode(int colorMode, float[] nightDisplayMatrix) {
if (colorMode == ColorDisplayController.COLOR_MODE_NATURAL) {
applySaturation(COLOR_SATURATION_NATURAL);
setNativeMode(false);
@@ -233,6 +233,7 @@
applySaturation(COLOR_SATURATION_NATURAL);
setNativeMode(true);
}
+ setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, nightDisplayMatrix);
updateConfiguration();
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 34c8e22..49b4465 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -24,12 +24,17 @@
import org.xmlpull.v1.XmlSerializer;
import android.graphics.Point;
+import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.WifiDisplay;
import android.util.AtomicFile;
import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Pair;
import android.util.Xml;
import android.view.Display;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
@@ -37,10 +42,12 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import libcore.io.IoUtils;
@@ -57,14 +64,22 @@
* <wifi-display deviceAddress="00:00:00:00:00:00" deviceName="XXXX" deviceAlias="YYYY" />
* <remembered-wifi-displays>
* <display-states>
- * <display>
+ * <display unique-id="XXXXXXX">
* <color-mode>0</color-mode>
* </display>
* </display-states>
* <stable-device-values>
- * <stable-display-height>1920<stable-display-height>
- * <stable-display-width>1080<stable-display-width>
+ * <stable-display-height>1920</stable-display-height>
+ * <stable-display-width>1080</stable-display-width>
* </stable-device-values>
+ * <brightness-configurations>
+ * <brightness-configuration user-id="0">
+ * <brightness-curve>
+ * <brightness-point lux="0" nits="13.25"/>
+ * <brightness-point lux="20" nits="35.94"/>
+ * </brightness-curve>
+ * </brightness-configuration>
+ * </brightness-configurations>
* </display-manager-state>
* </code>
*
@@ -73,6 +88,31 @@
final class PersistentDataStore {
static final String TAG = "DisplayManager";
+ private static final String TAG_DISPLAY_MANAGER_STATE = "display-manager-state";
+
+ private static final String TAG_REMEMBERED_WIFI_DISPLAYS = "remembered-wifi-displays";
+ private static final String TAG_WIFI_DISPLAY = "wifi-display";
+ private static final String ATTR_DEVICE_ADDRESS = "deviceAddress";
+ private static final String ATTR_DEVICE_NAME = "deviceName";
+ private static final String ATTR_DEVICE_ALIAS = "deviceAlias";
+
+ private static final String TAG_DISPLAY_STATES = "display-states";
+ private static final String TAG_DISPLAY = "display";
+ private static final String TAG_COLOR_MODE = "color-mode";
+ private static final String ATTR_UNIQUE_ID = "unique-id";
+
+ private static final String TAG_STABLE_DEVICE_VALUES = "stable-device-values";
+ private static final String TAG_STABLE_DISPLAY_HEIGHT = "stable-display-height";
+ private static final String TAG_STABLE_DISPLAY_WIDTH = "stable-display-width";
+
+ private static final String TAG_BRIGHTNESS_CONFIGURATIONS = "brightness-configurations";
+ private static final String TAG_BRIGHTNESS_CONFIGURATION = "brightness-configuration";
+ private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
+ private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
+ private static final String ATTR_USER_SERIAL = "user-serial";
+ private static final String ATTR_LUX = "lux";
+ private static final String ATTR_NITS = "nits";
+
// Remembered Wifi display devices.
private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
@@ -83,8 +123,8 @@
// Display values which should be stable across the device's lifetime.
private final StableDeviceValues mStableDeviceValues = new StableDeviceValues();
- // The atomic file used to safely read or write the file.
- private final AtomicFile mAtomicFile;
+ // Brightness configuration by user
+ private BrightnessConfigurations mBrightnessConfigurations = new BrightnessConfigurations();
// True if the data has been loaded.
private boolean mLoaded;
@@ -92,8 +132,16 @@
// True if there are changes to be saved.
private boolean mDirty;
+ // The interface for methods which should be replaced by the test harness.
+ private Injector mInjector;
+
public PersistentDataStore() {
- mAtomicFile = new AtomicFile(new File("/data/system/display-manager-state.xml"));
+ this(new Injector());
+ }
+
+ @VisibleForTesting
+ PersistentDataStore(Injector injector) {
+ mInjector = injector;
}
public void saveIfNeeded() {
@@ -225,6 +273,18 @@
}
}
+ public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userSerial) {
+ loadIfNeeded();
+ if (mBrightnessConfigurations.setBrightnessConfigurationForUser(c, userSerial)) {
+ setDirty();
+ }
+ }
+
+ public BrightnessConfiguration getBrightnessConfiguration(int userSerial) {
+ loadIfNeeded();
+ return mBrightnessConfigurations.getBrightnessConfiguration(userSerial);
+ }
+
private DisplayState getDisplayState(String uniqueId, boolean createIfAbsent) {
loadIfNeeded();
DisplayState state = mDisplayStates.get(uniqueId);
@@ -256,7 +316,7 @@
final InputStream is;
try {
- is = mAtomicFile.openRead();
+ is = mInjector.openRead();
} catch (FileNotFoundException ex) {
return;
}
@@ -278,9 +338,9 @@
}
private void save() {
- final FileOutputStream os;
+ final OutputStream os;
try {
- os = mAtomicFile.startWrite();
+ os = mInjector.startWrite();
boolean success = false;
try {
XmlSerializer serializer = new FastXmlSerializer();
@@ -289,11 +349,7 @@
serializer.flush();
success = true;
} finally {
- if (success) {
- mAtomicFile.finishWrite(os);
- } else {
- mAtomicFile.failWrite(os);
- }
+ mInjector.finishWrite(os, success);
}
} catch (IOException ex) {
Slog.w(TAG, "Failed to save display manager persistent store data.", ex);
@@ -302,18 +358,21 @@
private void loadFromXml(XmlPullParser parser)
throws IOException, XmlPullParserException {
- XmlUtils.beginDocument(parser, "display-manager-state");
+ XmlUtils.beginDocument(parser, TAG_DISPLAY_MANAGER_STATE);
final int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- if (parser.getName().equals("remembered-wifi-displays")) {
+ if (parser.getName().equals(TAG_REMEMBERED_WIFI_DISPLAYS)) {
loadRememberedWifiDisplaysFromXml(parser);
}
- if (parser.getName().equals("display-states")) {
+ if (parser.getName().equals(TAG_DISPLAY_STATES)) {
loadDisplaysFromXml(parser);
}
- if (parser.getName().equals("stable-device-values")) {
+ if (parser.getName().equals(TAG_STABLE_DEVICE_VALUES)) {
mStableDeviceValues.loadFromXml(parser);
}
+ if (parser.getName().equals(TAG_BRIGHTNESS_CONFIGURATIONS)) {
+ mBrightnessConfigurations.loadFromXml(parser);
+ }
}
}
@@ -321,10 +380,10 @@
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- if (parser.getName().equals("wifi-display")) {
- String deviceAddress = parser.getAttributeValue(null, "deviceAddress");
- String deviceName = parser.getAttributeValue(null, "deviceName");
- String deviceAlias = parser.getAttributeValue(null, "deviceAlias");
+ if (parser.getName().equals(TAG_WIFI_DISPLAY)) {
+ String deviceAddress = parser.getAttributeValue(null, ATTR_DEVICE_ADDRESS);
+ String deviceName = parser.getAttributeValue(null, ATTR_DEVICE_NAME);
+ String deviceAlias = parser.getAttributeValue(null, ATTR_DEVICE_ALIAS);
if (deviceAddress == null || deviceName == null) {
throw new XmlPullParserException(
"Missing deviceAddress or deviceName attribute on wifi-display.");
@@ -345,8 +404,8 @@
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- if (parser.getName().equals("display")) {
- String uniqueId = parser.getAttributeValue(null, "unique-id");
+ if (parser.getName().equals(TAG_DISPLAY)) {
+ String uniqueId = parser.getAttributeValue(null, ATTR_UNIQUE_ID);
if (uniqueId == null) {
throw new XmlPullParserException(
"Missing unique-id attribute on display.");
@@ -365,32 +424,35 @@
private void saveToXml(XmlSerializer serializer) throws IOException {
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
- serializer.startTag(null, "display-manager-state");
- serializer.startTag(null, "remembered-wifi-displays");
+ serializer.startTag(null, TAG_DISPLAY_MANAGER_STATE);
+ serializer.startTag(null, TAG_REMEMBERED_WIFI_DISPLAYS);
for (WifiDisplay display : mRememberedWifiDisplays) {
- serializer.startTag(null, "wifi-display");
- serializer.attribute(null, "deviceAddress", display.getDeviceAddress());
- serializer.attribute(null, "deviceName", display.getDeviceName());
+ serializer.startTag(null, TAG_WIFI_DISPLAY);
+ serializer.attribute(null, ATTR_DEVICE_ADDRESS, display.getDeviceAddress());
+ serializer.attribute(null, ATTR_DEVICE_NAME, display.getDeviceName());
if (display.getDeviceAlias() != null) {
- serializer.attribute(null, "deviceAlias", display.getDeviceAlias());
+ serializer.attribute(null, ATTR_DEVICE_ALIAS, display.getDeviceAlias());
}
- serializer.endTag(null, "wifi-display");
+ serializer.endTag(null, TAG_WIFI_DISPLAY);
}
- serializer.endTag(null, "remembered-wifi-displays");
- serializer.startTag(null, "display-states");
+ serializer.endTag(null, TAG_REMEMBERED_WIFI_DISPLAYS);
+ serializer.startTag(null, TAG_DISPLAY_STATES);
for (Map.Entry<String, DisplayState> entry : mDisplayStates.entrySet()) {
final String uniqueId = entry.getKey();
final DisplayState state = entry.getValue();
- serializer.startTag(null, "display");
- serializer.attribute(null, "unique-id", uniqueId);
+ serializer.startTag(null, TAG_DISPLAY);
+ serializer.attribute(null, ATTR_UNIQUE_ID, uniqueId);
state.saveToXml(serializer);
- serializer.endTag(null, "display");
+ serializer.endTag(null, TAG_DISPLAY);
}
- serializer.endTag(null, "display-states");
- serializer.startTag(null, "stable-device-values");
+ serializer.endTag(null, TAG_DISPLAY_STATES);
+ serializer.startTag(null, TAG_STABLE_DEVICE_VALUES);
mStableDeviceValues.saveToXml(serializer);
- serializer.endTag(null, "stable-device-values");
- serializer.endTag(null, "display-manager-state");
+ serializer.endTag(null, TAG_STABLE_DEVICE_VALUES);
+ serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
+ mBrightnessConfigurations.saveToXml(serializer);
+ serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
+ serializer.endTag(null, TAG_DISPLAY_MANAGER_STATE);
serializer.endDocument();
}
@@ -411,6 +473,8 @@
}
pw.println(" StableDeviceValues:");
mStableDeviceValues.dump(pw, " ");
+ pw.println(" BrightnessConfigurations:");
+ mBrightnessConfigurations.dump(pw, " ");
}
private static final class DisplayState {
@@ -433,7 +497,7 @@
final int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- if (parser.getName().equals("color-mode")) {
+ if (parser.getName().equals(TAG_COLOR_MODE)) {
String value = parser.nextText();
mColorMode = Integer.parseInt(value);
}
@@ -441,9 +505,9 @@
}
public void saveToXml(XmlSerializer serializer) throws IOException {
- serializer.startTag(null, "color-mode");
+ serializer.startTag(null, TAG_COLOR_MODE);
serializer.text(Integer.toString(mColorMode));
- serializer.endTag(null, "color-mode");
+ serializer.endTag(null, TAG_COLOR_MODE);
}
public void dump(final PrintWriter pw, final String prefix) {
@@ -472,10 +536,10 @@
final int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
switch (parser.getName()) {
- case "stable-display-width":
+ case TAG_STABLE_DISPLAY_WIDTH:
mWidth = loadIntValue(parser);
break;
- case "stable-display-height":
+ case TAG_STABLE_DISPLAY_HEIGHT:
mHeight = loadIntValue(parser);
break;
}
@@ -494,12 +558,12 @@
public void saveToXml(XmlSerializer serializer) throws IOException {
if (mWidth > 0 && mHeight > 0) {
- serializer.startTag(null, "stable-display-width");
+ serializer.startTag(null, TAG_STABLE_DISPLAY_WIDTH);
serializer.text(Integer.toString(mWidth));
- serializer.endTag(null, "stable-display-width");
- serializer.startTag(null, "stable-display-height");
+ serializer.endTag(null, TAG_STABLE_DISPLAY_WIDTH);
+ serializer.startTag(null, TAG_STABLE_DISPLAY_HEIGHT);
serializer.text(Integer.toString(mHeight));
- serializer.endTag(null, "stable-display-height");
+ serializer.endTag(null, TAG_STABLE_DISPLAY_HEIGHT);
}
}
@@ -508,4 +572,158 @@
pw.println(prefix + "StableDisplayHeight=" + mHeight);
}
}
+
+ private static final class BrightnessConfigurations {
+ // Maps from a user ID to the users' given brightness configuration
+ private SparseArray<BrightnessConfiguration> mConfigurations;
+
+ public BrightnessConfigurations() {
+ mConfigurations = new SparseArray<>();
+ }
+
+ private boolean setBrightnessConfigurationForUser(BrightnessConfiguration c,
+ int userSerial) {
+ BrightnessConfiguration currentConfig = mConfigurations.get(userSerial);
+ if (currentConfig == null || !currentConfig.equals(c)) {
+ mConfigurations.put(userSerial, c);
+ return true;
+ }
+ return false;
+ }
+
+ public BrightnessConfiguration getBrightnessConfiguration(int userSerial) {
+ return mConfigurations.get(userSerial);
+ }
+
+ public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if (TAG_BRIGHTNESS_CONFIGURATION.equals(parser.getName())) {
+ int userSerial;
+ try {
+ userSerial = Integer.parseInt(
+ parser.getAttributeValue(null, ATTR_USER_SERIAL));
+ } catch (NumberFormatException nfe) {
+ userSerial= -1;
+ Slog.e(TAG, "Failed to read in brightness configuration", nfe);
+ }
+
+ try {
+ BrightnessConfiguration config = loadConfigurationFromXml(parser);
+ if (userSerial>= 0 && config != null) {
+ mConfigurations.put(userSerial, config);
+ }
+ } catch (IllegalArgumentException iae) {
+ Slog.e(TAG, "Failed to load brightness configuration!", iae);
+ }
+ }
+ }
+ }
+
+ private static BrightnessConfiguration loadConfigurationFromXml(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
+ Pair<float[], float[]> curve = loadCurveFromXml(parser, builder);
+ builder.setCurve(curve.first /*lux*/, curve.second /*nits*/);
+ }
+ }
+ return builder.build();
+ }
+
+ private static Pair<float[], float[]> loadCurveFromXml(XmlPullParser parser,
+ BrightnessConfiguration.Builder builder)
+ throws IOException, XmlPullParserException {
+ final int outerDepth = parser.getDepth();
+ List<Float> luxLevels = new ArrayList<>();
+ List<Float> nitLevels = new ArrayList<>();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if (TAG_BRIGHTNESS_POINT.equals(parser.getName())) {
+ luxLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_LUX)));
+ nitLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_NITS)));
+ }
+ }
+ final int N = luxLevels.size();
+ float[] lux = new float[N];
+ float[] nits = new float[N];
+ for (int i = 0; i < N; i++) {
+ lux[i] = luxLevels.get(i);
+ nits[i] = nitLevels.get(i);
+ }
+ return Pair.create(lux, nits);
+ }
+
+ private static float loadFloat(String val) {
+ try {
+ return Float.parseFloat(val);
+ } catch (NullPointerException | NumberFormatException e) {
+ Slog.e(TAG, "Failed to parse float loading brightness config", e);
+ return Float.NEGATIVE_INFINITY;
+ }
+ }
+
+ public void saveToXml(XmlSerializer serializer) throws IOException {
+ for (int i = 0; i < mConfigurations.size(); i++) {
+ final int userSerial= mConfigurations.keyAt(i);
+ final BrightnessConfiguration config = mConfigurations.valueAt(i);
+
+ serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATION);
+ serializer.attribute(null, ATTR_USER_SERIAL, Integer.toString(userSerial));
+ saveConfigurationToXml(serializer, config);
+ serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION);
+ }
+ }
+
+ private static void saveConfigurationToXml(XmlSerializer serializer,
+ BrightnessConfiguration config) throws IOException {
+ serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
+ final Pair<float[], float[]> curve = config.getCurve();
+ for (int i = 0; i < curve.first.length; i++) {
+ serializer.startTag(null, TAG_BRIGHTNESS_POINT);
+ serializer.attribute(null, ATTR_LUX, Float.toString(curve.first[i]));
+ serializer.attribute(null, ATTR_NITS, Float.toString(curve.second[i]));
+ serializer.endTag(null, TAG_BRIGHTNESS_POINT);
+ }
+ serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
+ }
+
+ public void dump(final PrintWriter pw, final String prefix) {
+ for (int i = 0; i < mConfigurations.size(); i++) {
+ final int userSerial= mConfigurations.keyAt(i);
+ pw.println(prefix + "User " + userSerial + ":");
+ pw.println(prefix + " " + mConfigurations.valueAt(i));
+ }
+ }
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ private final AtomicFile mAtomicFile;
+
+ public Injector() {
+ mAtomicFile = new AtomicFile(new File("/data/system/display-manager-state.xml"));
+ }
+
+ public InputStream openRead() throws FileNotFoundException {
+ return mAtomicFile.openRead();
+ }
+
+ public OutputStream startWrite() throws IOException {
+ return mAtomicFile.startWrite();
+ }
+
+ public void finishWrite(OutputStream os, boolean success) {
+ if (!(os instanceof FileOutputStream)) {
+ throw new IllegalArgumentException("Unexpected OutputStream as argument: " + os);
+ }
+ FileOutputStream fos = (FileOutputStream) os;
+ if (success) {
+ mAtomicFile.finishWrite(fos);
+ } else {
+ mAtomicFile.failWrite(fos);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/dreams/OWNERS b/services/core/java/com/android/server/dreams/OWNERS
new file mode 100644
index 0000000..3c9bbf8
--- /dev/null
+++ b/services/core/java/com/android/server/dreams/OWNERS
@@ -0,0 +1,3 @@
+dsandler@google.com
+michaelwr@google.com
+roosa@google.com
diff --git a/services/core/java/com/android/server/job/JobSchedulerInternal.java b/services/core/java/com/android/server/job/JobSchedulerInternal.java
index 9bcf208..c97eeaf 100644
--- a/services/core/java/com/android/server/job/JobSchedulerInternal.java
+++ b/services/core/java/com/android/server/job/JobSchedulerInternal.java
@@ -44,6 +44,11 @@
List<JobInfo> getSystemScheduledPendingJobs();
/**
+ * Cancel the jobs for a given uid (e.g. when app data is cleared)
+ */
+ void cancelJobsForUid(int uid, String reason);
+
+ /**
* These are for activity manager to communicate to use what is currently performing backups.
*/
void addBackingUpUid(int uid);
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index f2a1c47..bcb57ef 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -279,8 +279,8 @@
private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
private static final long DEFAULT_STANDBY_HEARTBEAT_TIME = 11 * 60 * 1000L;
- private static final int DEFAULT_STANDBY_WORKING_BEATS = 5; // ~ 1 hour, with 11-min beats
- private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 31; // ~ 6 hours
+ private static final int DEFAULT_STANDBY_WORKING_BEATS = 11; // ~ 2 hours, with 11min beats
+ private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours
private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours
/**
@@ -1997,6 +1997,11 @@
}
@Override
+ public void cancelJobsForUid(int uid, String reason) {
+ JobSchedulerService.this.cancelJobsForUid(uid, reason);
+ }
+
+ @Override
public void addBackingUpUid(int uid) {
synchronized (mLock) {
// No need to actually do anything here, since for a full backup the
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
index 41d9feb..9640e04 100644
--- a/services/core/java/com/android/server/location/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -179,7 +179,7 @@
}
/**
- * Handles a nanoapp load event.
+ * Notifies the client of a nanoapp load event if the connection is open.
*
* @param nanoAppId the ID of the nanoapp that was loaded.
*/
@@ -195,7 +195,7 @@
}
/**
- * Handles a nanoapp unload event.
+ * Notifies the client of a nanoapp unload event if the connection is open.
*
* @param nanoAppId the ID of the nanoapp that was unloaded.
*/
@@ -211,7 +211,7 @@
}
/**
- * Handles a hub reset for this client.
+ * Notifies the client of a hub reset event if the connection is open.
*/
/* package */ void onHubReset() {
if (mConnectionOpen.get()) {
@@ -223,4 +223,21 @@
}
}
}
+
+ /**
+ * Notifies the client of a nanoapp abort event if the connection is open.
+ *
+ * @param nanoAppId the ID of the nanoapp that aborted
+ * @param abortCode the nanoapp specific abort code
+ */
+ /* package */ void onNanoAppAborted(long nanoAppId, int abortCode) {
+ if (mConnectionOpen.get()) {
+ try {
+ mCallbackInterface.onNanoAppAborted(nanoAppId, abortCode);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling onNanoAppAborted on client"
+ + " (host endpoint ID = " + mHostEndPointId + ")", e);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index d58a746..60b5b1f 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -149,35 +149,38 @@
}
/**
- * Handles a nanoapp load event.
- *
- * @param contextHubId the ID of the hub where the nanoapp was loaded.
- * @param nanoAppId the ID of the nanoapp that was loaded.
+ * @param contextHubId the ID of the hub where the nanoapp was loaded
+ * @param nanoAppId the ID of the nanoapp that was loaded
*/
/* package */ void onNanoAppLoaded(int contextHubId, long nanoAppId) {
forEachClientOfHub(contextHubId, client -> client.onNanoAppLoaded(nanoAppId));
}
/**
- * Handles a nanoapp unload event.
- *
- * @param contextHubId the ID of the hub where the nanoapp was unloaded.
- * @param nanoAppId the ID of the nanoapp that was unloaded.
+ * @param contextHubId the ID of the hub where the nanoapp was unloaded
+ * @param nanoAppId the ID of the nanoapp that was unloaded
*/
/* package */ void onNanoAppUnloaded(int contextHubId, long nanoAppId) {
forEachClientOfHub(contextHubId, client -> client.onNanoAppUnloaded(nanoAppId));
}
/**
- * Handles a hub reset.
- *
- * @param contextHubId the ID of the hub that has reset.
+ * @param contextHubId the ID of the hub that has reset
*/
/* package */ void onHubReset(int contextHubId) {
forEachClientOfHub(contextHubId, client -> client.onHubReset());
}
/**
+ * @param contextHubId the ID of the hub that contained the nanoapp that aborted
+ * @param nanoAppId the ID of the nanoapp that aborted
+ * @param abortCode the nanoapp specific abort code
+ */
+ /* package */ void onNanoAppAborted(int contextHubId, long nanoAppId, int abortCode) {
+ forEachClientOfHub(contextHubId, client -> client.onNanoAppAborted(nanoAppId, abortCode));
+ }
+
+ /**
* Creates a new ContextHubClientBroker object for a client and registers it with the client
* manager.
*
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index cd4bb14..26edf62 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -55,7 +55,6 @@
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
-import java.util.concurrent.ConcurrentHashMap;
/**
* @hide
@@ -76,26 +75,12 @@
public static final int MSG_QUERY_MEMORY = 6;
public static final int MSG_HUB_RESET = 7;
- private static final String PRE_LOADED_GENERIC_UNKNOWN = "Preloaded app, unknown";
- private static final String PRE_LOADED_APP_NAME = PRE_LOADED_GENERIC_UNKNOWN;
- private static final String PRE_LOADED_APP_PUBLISHER = PRE_LOADED_GENERIC_UNKNOWN;
- private static final int PRE_LOADED_APP_MEM_REQ = 0;
-
private static final int OS_APP_INSTANCE = -1;
private final Context mContext;
- // TODO(b/69270990): Remove once old ContextHubManager API is deprecated
- // Service cache maintaining of instance ID to nanoapp infos
- private final ConcurrentHashMap<Integer, NanoAppInstanceInfo> mNanoAppHash =
- new ConcurrentHashMap<>();
- // The next available instance ID (managed by the service) to assign to a nanoapp
- private int mNextAvailableInstanceId = 0;
- // A map of the long nanoapp ID to instance ID managed by the service
- private final ConcurrentHashMap<Long, Integer> mNanoAppIdToInstanceMap =
- new ConcurrentHashMap<>();
-
- private final ContextHubInfo[] mContextHubInfo;
+ private final Map<Integer, ContextHubInfo> mContextHubIdToInfoMap;
+ private final List<ContextHubInfo> mContextHubInfoList;
private final RemoteCallbackList<IContextHubCallback> mCallbacksList =
new RemoteCallbackList<>();
@@ -111,6 +96,9 @@
// The default client for old API clients
private final Map<Integer, IContextHubClient> mDefaultClientMap;
+ // The manager for the internal nanoapp state cache
+ private final NanoAppStateManager mNanoAppStateManager = new NanoAppStateManager();
+
/**
* Class extending the callback to register with a Context Hub.
*/
@@ -154,13 +142,15 @@
if (mContextHubProxy == null) {
mTransactionManager = null;
mClientManager = null;
- mDefaultClientMap = Collections.EMPTY_MAP;
- mContextHubInfo = new ContextHubInfo[0];
+ mDefaultClientMap = Collections.emptyMap();
+ mContextHubIdToInfoMap = Collections.emptyMap();
+ mContextHubInfoList = Collections.emptyList();
return;
}
mClientManager = new ContextHubClientManager(mContext, mContextHubProxy);
- mTransactionManager = new ContextHubTransactionManager(mContextHubProxy, mClientManager);
+ mTransactionManager = new ContextHubTransactionManager(
+ mContextHubProxy, mClientManager, mNanoAppStateManager);
List<ContextHub> hubList;
try {
@@ -169,20 +159,16 @@
Log.e(TAG, "RemoteException while getting Context Hub info", e);
hubList = Collections.emptyList();
}
- mContextHubInfo = ContextHubServiceUtil.createContextHubInfoArray(hubList);
+ mContextHubIdToInfoMap = Collections.unmodifiableMap(
+ ContextHubServiceUtil.createContextHubInfoMap(hubList));
+ mContextHubInfoList = new ArrayList<>(mContextHubIdToInfoMap.values());
HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
- for (ContextHubInfo contextHubInfo : mContextHubInfo) {
- int contextHubId = contextHubInfo.getId();
-
+ for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
IContextHubClient client = mClientManager.registerClient(
createDefaultClientCallback(contextHubId), contextHubId);
defaultClientMap.put(contextHubId, client);
- }
- mDefaultClientMap = Collections.unmodifiableMap(defaultClientMap);
- for (ContextHubInfo contextHubInfo : mContextHubInfo) {
- int contextHubId = contextHubInfo.getId();
try {
mContextHubProxy.registerCallback(
contextHubId, new ContextHubServiceCallback(contextHubId));
@@ -190,18 +176,12 @@
Log.e(TAG, "RemoteException while registering service callback for hub (ID = "
+ contextHubId + ")", e);
}
- }
- // Do a query to initialize the service cache list of nanoapps
- // TODO(b/69270990): Remove this when old API is deprecated
- for (ContextHubInfo contextHubInfo : mContextHubInfo) {
- queryNanoAppsInternal(contextHubInfo.getId());
+ // Do a query to initialize the service cache list of nanoapps
+ // TODO(b/69270990): Remove this when old API is deprecated
+ queryNanoAppsInternal(contextHubId);
}
-
- for (int i = 0; i < mContextHubInfo.length; i++) {
- Log.d(TAG, "ContextHub[" + i + "] id: " + mContextHubInfo[i].getId()
- + ", name: " + mContextHubInfo[i].getName());
- }
+ mDefaultClientMap = Collections.unmodifiableMap(defaultClientMap);
}
/**
@@ -214,12 +194,11 @@
return new IContextHubClientCallback.Stub() {
@Override
public void onMessageFromNanoApp(NanoAppMessage message) {
- int nanoAppInstanceId =
- mNanoAppIdToInstanceMap.containsKey(message.getNanoAppId()) ?
- mNanoAppIdToInstanceMap.get(message.getNanoAppId()) : -1;
+ int nanoAppHandle = mNanoAppStateManager.getNanoAppHandle(
+ contextHubId, message.getNanoAppId());
onMessageReceiptOldApi(
- message.getMessageType(), contextHubId, nanoAppInstanceId,
+ message.getMessageType(), contextHubId, nanoAppHandle,
message.getMessageBody());
}
@@ -280,27 +259,29 @@
@Override
public int[] getContextHubHandles() throws RemoteException {
checkPermissions();
- int[] returnArray = new int[mContextHubInfo.length];
-
- Log.d(TAG, "System supports " + returnArray.length + " hubs");
- for (int i = 0; i < returnArray.length; ++i) {
- returnArray[i] = i;
- Log.d(TAG, String.format("Hub %s is mapped to %d",
- mContextHubInfo[i].getName(), returnArray[i]));
- }
-
- return returnArray;
+ return ContextHubServiceUtil.createPrimitiveIntArray(mContextHubIdToInfoMap.keySet());
}
@Override
- public ContextHubInfo getContextHubInfo(int contextHubId) throws RemoteException {
+ public ContextHubInfo getContextHubInfo(int contextHubHandle) throws RemoteException {
checkPermissions();
- if (!(contextHubId >= 0 && contextHubId < mContextHubInfo.length)) {
- Log.e(TAG, "Invalid context hub handle " + contextHubId);
- return null; // null means fail
+ if (!mContextHubIdToInfoMap.containsKey(contextHubHandle)) {
+ Log.e(TAG, "Invalid Context Hub handle " + contextHubHandle + " in getContextHubInfo");
+ return null;
}
- return mContextHubInfo[contextHubId];
+ return mContextHubIdToInfoMap.get(contextHubHandle);
+ }
+
+ /**
+ * Returns a List of ContextHubInfo object describing the available hubs.
+ *
+ * @return the List of ContextHubInfo objects
+ */
+ @Override
+ public List<ContextHubInfo> getContextHubs() throws RemoteException {
+ checkPermissions();
+ return mContextHubInfoList;
}
/**
@@ -328,15 +309,13 @@
* Creates an internal unload transaction callback to be used for old API clients
*
* @param contextHubId the ID of the hub to unload the nanoapp
- * @param nanoAppId the ID of the nanoapp to unload
* @return the callback interface
*/
- private IContextHubTransactionCallback createUnloadTransactionCallback(
- int contextHubId, long nanoAppId) {
+ private IContextHubTransactionCallback createUnloadTransactionCallback(int contextHubId) {
return new IContextHubTransactionCallback.Stub() {
@Override
public void onTransactionComplete(int result) {
- handleUnloadResponseOldApi(contextHubId, result, nanoAppId);
+ handleUnloadResponseOldApi(contextHubId, result);
}
@Override
@@ -365,112 +344,74 @@
};
}
- /**
- * Adds a new transaction to the transaction manager queue
- *
- * @param transaction the transaction to add
- * @return the result of adding the transaction
- */
- private int addTransaction(ContextHubServiceTransaction transaction) {
- int result = Result.OK;
- try {
- mTransactionManager.addTransaction(transaction);
- } catch (IllegalStateException e) {
- Log.e(TAG, e.getMessage());
- result = Result.TRANSACTION_PENDING; /* failed */
- }
-
- return result;
- }
-
@Override
- public int loadNanoApp(int contextHubId, NanoApp app) throws RemoteException {
+ public int loadNanoApp(int contextHubHandle, NanoApp nanoApp) throws RemoteException {
checkPermissions();
if (mContextHubProxy == null) {
return -1;
}
-
- if (!(contextHubId >= 0 && contextHubId < mContextHubInfo.length)) {
- Log.e(TAG, "Invalid contextHubhandle " + contextHubId);
+ if (!isValidContextHubId(contextHubHandle)) {
+ Log.e(TAG, "Invalid Context Hub handle " + contextHubHandle + " in loadNanoApp");
return -1;
}
- if (app == null) {
- Log.e(TAG, "Invalid null app");
+ if (nanoApp == null) {
+ Log.e(TAG, "NanoApp cannot be null in loadNanoApp");
return -1;
}
// Create an internal IContextHubTransactionCallback for the old API clients
- NanoAppBinary nanoAppBinary = new NanoAppBinary(app.getAppBinary());
+ NanoAppBinary nanoAppBinary = new NanoAppBinary(nanoApp.getAppBinary());
IContextHubTransactionCallback onCompleteCallback =
- createLoadTransactionCallback(contextHubId, nanoAppBinary);
+ createLoadTransactionCallback(contextHubHandle, nanoAppBinary);
ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction(
- contextHubId, nanoAppBinary, onCompleteCallback);
+ contextHubHandle, nanoAppBinary, onCompleteCallback);
- int result = addTransaction(transaction);
- if (result != Result.OK) {
- Log.e(TAG, "Failed to load nanoapp with error code " + result);
- return -1;
- }
-
- // Do not add an entry to mNanoAppInstance Hash yet. The HAL may reject the app
+ mTransactionManager.addTransaction(transaction);
return 0;
}
@Override
- public int unloadNanoApp(int nanoAppInstanceHandle) throws RemoteException {
+ public int unloadNanoApp(int nanoAppHandle) throws RemoteException {
checkPermissions();
if (mContextHubProxy == null) {
return -1;
}
- NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstanceHandle);
+ NanoAppInstanceInfo info =
+ mNanoAppStateManager.getNanoAppInstanceInfo(nanoAppHandle);
if (info == null) {
- Log.e(TAG, "Cannot find app with handle " + nanoAppInstanceHandle);
- return -1; //means failed
+ Log.e(TAG, "Invalid nanoapp handle " + nanoAppHandle + " in unloadNanoApp");
+ return -1;
}
int contextHubId = info.getContexthubId();
long nanoAppId = info.getAppId();
IContextHubTransactionCallback onCompleteCallback =
- createUnloadTransactionCallback(contextHubId, nanoAppId);
+ createUnloadTransactionCallback(contextHubId);
ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction(
contextHubId, nanoAppId, onCompleteCallback);
- int result = addTransaction(transaction);
- if (result != Result.OK) {
- Log.e(TAG, "Failed to unload nanoapp with error code " + result);
- return -1;
- }
-
- // Do not add an entry to mNanoAppInstance Hash yet. The HAL may reject the app
+ mTransactionManager.addTransaction(transaction);
return 0;
}
@Override
- public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppInstanceHandle)
- throws RemoteException {
+ public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) throws RemoteException {
checkPermissions();
- // This assumes that all the nanoAppInfo is current. This is reasonable
- // for the use cases for tightly controlled nanoApps.
- if (mNanoAppHash.containsKey(nanoAppInstanceHandle)) {
- return mNanoAppHash.get(nanoAppInstanceHandle);
- } else {
- Log.e(TAG, "Could not find nanoApp with handle " + nanoAppInstanceHandle);
- return null;
- }
+
+ return mNanoAppStateManager.getNanoAppInstanceInfo(nanoAppHandle);
}
@Override
- public int[] findNanoAppOnHub(int hubHandle, NanoAppFilter filter) throws RemoteException {
+ public int[] findNanoAppOnHub(
+ int contextHubHandle, NanoAppFilter filter) throws RemoteException {
checkPermissions();
- ArrayList<Integer> foundInstances = new ArrayList<Integer>();
- for (Integer nanoAppInstance : mNanoAppHash.keySet()) {
- NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstance);
-
+ ArrayList<Integer> foundInstances = new ArrayList<>();
+ for (NanoAppInstanceInfo info : mNanoAppStateManager.getNanoAppInstanceInfoCollection()) {
if (filter.testMatch(info)) {
- foundInstances.add(nanoAppInstance);
+ foundInstances.add(info.getHandle());
}
}
@@ -478,12 +419,6 @@
for (int i = 0; i < foundInstances.size(); i++) {
retArray[i] = foundInstances.get(i).intValue();
}
-
- if (retArray.length == 0) {
- Log.d(TAG, "No nanoapps found on hub ID " + hubHandle + " using NanoAppFilter: "
- + filter);
- }
-
return retArray;
}
@@ -495,6 +430,8 @@
*
* @param contextHubId the ID of the hub to do the query
* @return the result of the query
+ *
+ * @throws IllegalStateException if the transaction queue is full
*/
private int queryNanoAppsInternal(int contextHubId) {
if (mContextHubProxy == null) {
@@ -506,33 +443,34 @@
ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
contextHubId, onCompleteCallback);
- return addTransaction(transaction);
+ mTransactionManager.addTransaction(transaction);
+ return Result.OK;
}
@Override
- public int sendMessage(
- int hubHandle, int nanoAppHandle, ContextHubMessage msg) throws RemoteException {
+ public int sendMessage(int contextHubHandle, int nanoAppHandle, ContextHubMessage msg)
+ throws RemoteException {
checkPermissions();
if (mContextHubProxy == null) {
return -1;
}
if (msg == null) {
- Log.e(TAG, "ContextHubMessage cannot be null");
+ Log.e(TAG, "ContextHubMessage cannot be null in sendMessage");
return -1;
}
if (msg.getData() == null) {
- Log.e(TAG, "ContextHubMessage message body cannot be null");
+ Log.e(TAG, "ContextHubMessage message body cannot be null in sendMessage");
return -1;
}
- if (!mDefaultClientMap.containsKey(hubHandle)) {
- Log.e(TAG, "Hub with ID " + hubHandle + " does not exist");
+ if (!isValidContextHubId(contextHubHandle)) {
+ Log.e(TAG, "Invalid Context Hub handle " + contextHubHandle + " in sendMessage");
return -1;
}
boolean success = false;
if (nanoAppHandle == OS_APP_INSTANCE) {
if (msg.getMsgType() == MSG_QUERY_NANO_APPS) {
- success = (queryNanoAppsInternal(hubHandle) == Result.OK);
+ success = (queryNanoAppsInternal(contextHubHandle) == Result.OK);
} else {
Log.e(TAG, "Invalid OS message params of type " + msg.getMsgType());
}
@@ -542,11 +480,11 @@
NanoAppMessage message = NanoAppMessage.createMessageToNanoApp(
info.getAppId(), msg.getMsgType(), msg.getData());
- IContextHubClient client = mDefaultClientMap.get(hubHandle);
+ IContextHubClient client = mDefaultClientMap.get(contextHubHandle);
success = (client.sendMessageToNanoApp(message) ==
ContextHubTransaction.TRANSACTION_SUCCESS);
} else {
- Log.e(TAG, "Failed to send nanoapp message - nanoapp with instance ID "
+ Log.e(TAG, "Failed to send nanoapp message - nanoapp with handle "
+ nanoAppHandle + " does not exist.");
}
}
@@ -576,26 +514,11 @@
return;
}
- // NOTE: The legacy JNI code used to do a query right after a load success
- // to synchronize the service cache. Instead store the binary that was requested to
- // load to update the cache later without doing a query.
- int instanceId = 0;
- long nanoAppId = nanoAppBinary.getNanoAppId();
- int nanoAppVersion = nanoAppBinary.getNanoAppVersion();
- if (result == TransactionResult.SUCCESS) {
- if (mNanoAppIdToInstanceMap.containsKey(nanoAppId)) {
- instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
- } else {
- instanceId = mNextAvailableInstanceId++;
- mNanoAppIdToInstanceMap.put(nanoAppId, instanceId);
- }
-
- addAppInstance(contextHubId, instanceId, nanoAppId, nanoAppVersion);
- }
-
byte[] data = new byte[5];
data[0] = (byte) result;
- ByteBuffer.wrap(data, 1, 4).order(ByteOrder.nativeOrder()).putInt(instanceId);
+ int nanoAppHandle = mNanoAppStateManager.getNanoAppHandle(
+ contextHubId, nanoAppBinary.getNanoAppId());
+ ByteBuffer.wrap(data, 1, 4).order(ByteOrder.nativeOrder()).putInt(nanoAppHandle);
onMessageReceiptOldApi(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
}
@@ -605,14 +528,7 @@
*
* TODO(b/69270990): Remove this once the old APIs are obsolete.
*/
- private void handleUnloadResponseOldApi(
- int contextHubId, int result, long nanoAppId) {
- if (result == TransactionResult.SUCCESS) {
- int instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
- deleteAppInstance(instanceId);
- mNanoAppIdToInstanceMap.remove(nanoAppId);
- }
-
+ private void handleUnloadResponseOldApi(int contextHubId, int result) {
byte[] data = new byte[1];
data[0] = (byte) result;
onMessageReceiptOldApi(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
@@ -655,7 +571,7 @@
* @param abortCode the nanoapp-specific abort code
*/
private void handleAppAbortCallback(int contextHubId, long nanoAppId, int abortCode) {
- // TODO(b/31049861): Implement this
+ mClientManager.onNanoAppAborted(contextHubId, nanoAppId, abortCode);
}
/**
@@ -668,53 +584,16 @@
List<NanoAppState> nanoAppStateList =
ContextHubServiceUtil.createNanoAppStateList(nanoAppInfoList);
- updateServiceCache(contextHubId, nanoAppInfoList);
+ mNanoAppStateManager.updateCache(contextHubId, nanoAppInfoList);
mTransactionManager.onQueryResponse(nanoAppStateList);
}
/**
- * Updates the service's cache of the list of loaded nanoapps using a nanoapp list response.
- *
- * TODO(b/69270990): Remove this when the old API functionality is removed.
- *
- * @param contextHubId the ID of the hub the response came from
- * @param nanoAppInfoList the list of loaded nanoapps
- */
- private void updateServiceCache(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
- synchronized (mNanoAppHash) {
- for (int instanceId : mNanoAppHash.keySet()) {
- if (mNanoAppHash.get(instanceId).getContexthubId() == contextHubId) {
- deleteAppInstance(instanceId);
- }
- }
-
- for (HubAppInfo appInfo : nanoAppInfoList) {
- int instanceId;
- long nanoAppId = appInfo.appId;
- if (mNanoAppIdToInstanceMap.containsKey(nanoAppId)) {
- instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
- } else {
- instanceId = mNextAvailableInstanceId++;
- mNanoAppIdToInstanceMap.put(nanoAppId, instanceId);
- }
-
- addAppInstance(contextHubId, instanceId, nanoAppId, appInfo.version);
- }
- }
- }
-
- /**
* @param contextHubId the hub ID to validate
* @return {@code true} if the ID represents that of an available hub, {@code false} otherwise
*/
private boolean isValidContextHubId(int contextHubId) {
- for (ContextHubInfo hubInfo : mContextHubInfo) {
- if (hubInfo.getId() == contextHubId) {
- return true;
- }
- }
-
- return false;
+ return mContextHubIdToInfoMap.containsKey(contextHubId);
}
/**
@@ -745,11 +624,11 @@
/**
* Loads a nanoapp binary at the specified Context hub.
*
- * @param contextHubId the ID of the hub to load the binary
+ * @param contextHubId the ID of the hub to load the binary
* @param transactionCallback the client-facing transaction callback interface
- * @param nanoAppBinary the binary to load
+ * @param nanoAppBinary the binary to load
*
- * @throws RemoteException
+ * @throws IllegalStateException if the transaction queue is full
*/
@Override
public void loadNanoAppOnHub(
@@ -769,17 +648,17 @@
ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction(
contextHubId, nanoAppBinary, transactionCallback);
- addTransaction(transaction);
+ mTransactionManager.addTransaction(transaction);
}
/**
* Unloads a nanoapp from the specified Context Hub.
*
- * @param contextHubId the ID of the hub to unload the nanoapp
+ * @param contextHubId the ID of the hub to unload the nanoapp
* @param transactionCallback the client-facing transaction callback interface
- * @param nanoAppId the ID of the nanoapp to unload
+ * @param nanoAppId the ID of the nanoapp to unload
*
- * @throws RemoteException
+ * @throws IllegalStateException if the transaction queue is full
*/
@Override
public void unloadNanoAppFromHub(
@@ -793,16 +672,64 @@
ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction(
contextHubId, nanoAppId, transactionCallback);
- addTransaction(transaction);
+ mTransactionManager.addTransaction(transaction);
+ }
+
+ /**
+ * Enables a nanoapp at the specified Context Hub.
+ *
+ * @param contextHubId the ID of the hub to enable the nanoapp
+ * @param transactionCallback the client-facing transaction callback interface
+ * @param nanoAppId the ID of the nanoapp to enable
+ *
+ * @throws IllegalStateException if the transaction queue is full
+ */
+ @Override
+ public void enableNanoApp(
+ int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
+ throws RemoteException {
+ checkPermissions();
+ if (!checkHalProxyAndContextHubId(
+ contextHubId, transactionCallback, ContextHubTransaction.TYPE_ENABLE_NANOAPP)) {
+ return;
+ }
+
+ ContextHubServiceTransaction transaction = mTransactionManager.createEnableTransaction(
+ contextHubId, nanoAppId, transactionCallback);
+ mTransactionManager.addTransaction(transaction);
+ }
+
+ /**
+ * Disables a nanoapp at the specified Context Hub.
+ *
+ * @param contextHubId the ID of the hub to disable the nanoapp
+ * @param transactionCallback the client-facing transaction callback interface
+ * @param nanoAppId the ID of the nanoapp to disable
+ *
+ * @throws IllegalStateException if the transaction queue is full
+ */
+ @Override
+ public void disableNanoApp(
+ int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
+ throws RemoteException {
+ checkPermissions();
+ if (!checkHalProxyAndContextHubId(
+ contextHubId, transactionCallback, ContextHubTransaction.TYPE_DISABLE_NANOAPP)) {
+ return;
+ }
+
+ ContextHubServiceTransaction transaction = mTransactionManager.createDisableTransaction(
+ contextHubId, nanoAppId, transactionCallback);
+ mTransactionManager.addTransaction(transaction);
}
/**
* Queries for a list of nanoapps from the specified Context hub.
*
- * @param contextHubId the ID of the hub to query
+ * @param contextHubId the ID of the hub to query
* @param transactionCallback the client-facing transaction callback interface
*
- * @throws RemoteException
+ * @throws IllegalStateException if the transaction queue is full
*/
@Override
public void queryNanoApps(int contextHubId, IContextHubTransactionCallback transactionCallback)
@@ -813,9 +740,9 @@
return;
}
- ContextHubServiceTransaction transaction =
- mTransactionManager.createQueryTransaction(contextHubId, transactionCallback);
- addTransaction(transaction);
+ ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
+ contextHubId, transactionCallback);
+ mTransactionManager.addTransaction(transaction);
}
@Override
@@ -827,14 +754,14 @@
pw.println("");
// dump ContextHubInfo
pw.println("=================== CONTEXT HUBS ====================");
- for (int i = 0; i < mContextHubInfo.length; i++) {
- pw.println("Handle " + i + " : " + mContextHubInfo[i].toString());
+ for (ContextHubInfo hubInfo : mContextHubIdToInfoMap.values()) {
+ pw.println(hubInfo);
}
pw.println("");
pw.println("=================== NANOAPPS ====================");
// Dump nanoAppHash
- for (Integer nanoAppInstance : mNanoAppHash.keySet()) {
- pw.println(nanoAppInstance + " : " + mNanoAppHash.get(nanoAppInstance).toString());
+ for (NanoAppInstanceInfo info : mNanoAppStateManager.getNanoAppInstanceInfoCollection()) {
+ pw.println(info);
}
// dump eventLog
@@ -844,7 +771,8 @@
ContextHubServiceUtil.checkPermissions(mContext);
}
- private int onMessageReceiptOldApi(int msgType, int hubHandle, int appInstance, byte[] data) {
+ private int onMessageReceiptOldApi(
+ int msgType, int contextHubHandle, int appInstance, byte[] data) {
if (data == null) {
return -1;
}
@@ -852,7 +780,8 @@
int msgVersion = 0;
int callbacksCount = mCallbacksList.beginBroadcast();
Log.d(TAG, "Sending message " + msgType + " version " + msgVersion + " from hubHandle " +
- hubHandle + ", appInstance " + appInstance + ", callBackCount " + callbacksCount);
+ contextHubHandle + ", appInstance " + appInstance + ", callBackCount "
+ + callbacksCount);
if (callbacksCount < 1) {
Log.v(TAG, "No message callbacks registered.");
@@ -863,7 +792,7 @@
for (int i = 0; i < callbacksCount; ++i) {
IContextHubCallback callback = mCallbacksList.getBroadcastItem(i);
try {
- callback.onMessageReceipt(hubHandle, appInstance, msg);
+ callback.onMessageReceipt(contextHubHandle, appInstance, msg);
} catch (RemoteException e) {
Log.i(TAG, "Exception (" + e + ") calling remote callback (" + callback + ").");
continue;
@@ -908,40 +837,4 @@
return true;
}
-
- private int addAppInstance(int hubHandle, int appInstanceHandle, long appId, int appVersion) {
- // App Id encodes vendor & version
- NanoAppInstanceInfo appInfo = new NanoAppInstanceInfo();
-
- appInfo.setAppId(appId);
- appInfo.setAppVersion(appVersion);
- appInfo.setName(PRE_LOADED_APP_NAME);
- appInfo.setContexthubId(hubHandle);
- appInfo.setHandle(appInstanceHandle);
- appInfo.setPublisher(PRE_LOADED_APP_PUBLISHER);
- appInfo.setNeededExecMemBytes(PRE_LOADED_APP_MEM_REQ);
- appInfo.setNeededReadMemBytes(PRE_LOADED_APP_MEM_REQ);
- appInfo.setNeededWriteMemBytes(PRE_LOADED_APP_MEM_REQ);
-
- String action;
- if (mNanoAppHash.containsKey(appInstanceHandle)) {
- action = "Updated";
- } else {
- action = "Added";
- }
-
- mNanoAppHash.put(appInstanceHandle, appInfo);
- Log.d(TAG, action + " app instance " + appInstanceHandle + " with id 0x"
- + Long.toHexString(appId) + " version 0x" + Integer.toHexString(appVersion));
-
- return 0;
- }
-
- private int deleteAppInstance(int appInstanceHandle) {
- if (mNanoAppHash.remove(appInstanceHandle) == null) {
- return -1;
- }
-
- return 0;
- }
}
diff --git a/services/core/java/com/android/server/location/ContextHubServiceTransaction.java b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
index ce92f72..c1fc982 100644
--- a/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
+++ b/services/core/java/com/android/server/location/ContextHubServiceTransaction.java
@@ -61,7 +61,7 @@
*
* @param result the result of the transaction
*/
- /* package */ void onTransactionComplete(int result) {
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
}
/**
@@ -72,7 +72,8 @@
* @param result the result of the query
* @param nanoAppStateList the list of nanoapps given by the query response
*/
- /* package */ void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+ /* package */ void onQueryResponse(
+ @ContextHubTransaction.Result int result, List<NanoAppState> nanoAppStateList) {
}
/**
diff --git a/services/core/java/com/android/server/location/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
index 6faeb72..7a57dd3 100644
--- a/services/core/java/com/android/server/location/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
@@ -30,6 +30,9 @@
import android.hardware.location.NanoAppState;
import android.util.Log;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
@@ -43,19 +46,20 @@
+ HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
/**
- * Creates a ContextHubInfo array from an ArrayList of HIDL ContextHub objects.
+ * Creates a ConcurrentHashMap of the Context Hub ID to the ContextHubInfo object given an
+ * ArrayList of HIDL ContextHub objects.
*
* @param hubList the ContextHub ArrayList
- * @return the ContextHubInfo array
+ * @return the HashMap object
*/
/* package */
- static ContextHubInfo[] createContextHubInfoArray(List<ContextHub> hubList) {
- ContextHubInfo[] contextHubInfoList = new ContextHubInfo[hubList.size()];
- for (int i = 0; i < hubList.size(); i++) {
- contextHubInfoList[i] = new ContextHubInfo(hubList.get(i));
+ static HashMap<Integer, ContextHubInfo> createContextHubInfoMap(List<ContextHub> hubList) {
+ HashMap<Integer, ContextHubInfo> contextHubIdToInfoMap = new HashMap<>();
+ for (ContextHub contextHub : hubList) {
+ contextHubIdToInfoMap.put(contextHub.hubId, new ContextHubInfo(contextHub));
}
- return contextHubInfoList;
+ return contextHubIdToInfoMap;
}
/**
@@ -90,6 +94,22 @@
}
/**
+ * Creates a primitive integer array given a Collection<Integer>.
+ * @param collection the collection to iterate
+ * @return the primitive integer array
+ */
+ static int[] createPrimitiveIntArray(Collection<Integer> collection) {
+ int[] primitiveArray = new int[collection.size()];
+
+ int i = 0;
+ for (int contextHubId : collection) {
+ primitiveArray[i++] = contextHubId;
+ }
+
+ return primitiveArray;
+ }
+
+ /**
* Generates the Context Hub HAL's NanoAppBinary object from the client-facing
* android.hardware.location.NanoAppBinary object.
*
diff --git a/services/core/java/com/android/server/location/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
index 3fcc682..412d43d 100644
--- a/services/core/java/com/android/server/location/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
@@ -50,7 +50,7 @@
/*
* Maximum number of transaction requests that can be pending at a time
*/
- private static final int MAX_PENDING_REQUESTS = 10;
+ private static final int MAX_PENDING_REQUESTS = 10000;
/*
* The proxy to talk to the Context Hub
@@ -63,6 +63,11 @@
private final ContextHubClientManager mClientManager;
/*
+ * The nanoapp state manager for the service
+ */
+ private final NanoAppStateManager mNanoAppStateManager;
+
+ /*
* A queue containing the current transactions
*/
private final ArrayDeque<ContextHubServiceTransaction> mTransactionQueue = new ArrayDeque<>();
@@ -79,9 +84,11 @@
private ScheduledFuture<?> mTimeoutFuture = null;
/* package */ ContextHubTransactionManager(
- IContexthub contextHubProxy, ContextHubClientManager clientManager) {
+ IContexthub contextHubProxy, ContextHubClientManager clientManager,
+ NanoAppStateManager nanoAppStateManager) {
mContextHubProxy = contextHubProxy;
mClientManager = clientManager;
+ mNanoAppStateManager = nanoAppStateManager;
}
/**
@@ -112,10 +119,18 @@
}
@Override
- /* package */ void onTransactionComplete(int result) {
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ if (result == ContextHubTransaction.TRANSACTION_SUCCESS) {
+ // NOTE: The legacy JNI code used to do a query right after a load success
+ // to synchronize the service cache. Instead store the binary that was
+ // requested to load to update the cache later without doing a query.
+ mNanoAppStateManager.addNanoAppInstance(
+ contextHubId, nanoAppBinary.getNanoAppId(),
+ nanoAppBinary.getNanoAppVersion());
+ }
try {
onCompleteCallback.onTransactionComplete(result);
- if (result == Result.OK) {
+ if (result == ContextHubTransaction.TRANSACTION_SUCCESS) {
mClientManager.onNanoAppLoaded(contextHubId, nanoAppBinary.getNanoAppId());
}
} catch (RemoteException e) {
@@ -128,7 +143,7 @@
/**
* Creates a transaction for unloading a nanoapp.
*
- * @param contextHubId the ID of the hub to load the nanoapp to
+ * @param contextHubId the ID of the hub to unload the nanoapp from
* @param nanoAppId the ID of the nanoapp to unload
* @param onCompleteCallback the client on complete callback
* @return the generated transaction
@@ -150,10 +165,13 @@
}
@Override
- /* package */ void onTransactionComplete(int result) {
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ if (result == ContextHubTransaction.TRANSACTION_SUCCESS) {
+ mNanoAppStateManager.removeNanoAppInstance(contextHubId, nanoAppId);
+ }
try {
onCompleteCallback.onTransactionComplete(result);
- if (result == Result.OK) {
+ if (result == ContextHubTransaction.TRANSACTION_SUCCESS) {
mClientManager.onNanoAppUnloaded(contextHubId, nanoAppId);
}
} catch (RemoteException e) {
@@ -164,6 +182,76 @@
}
/**
+ * Creates a transaction for enabling a nanoapp.
+ *
+ * @param contextHubId the ID of the hub to enable the nanoapp on
+ * @param nanoAppId the ID of the nanoapp to enable
+ * @param onCompleteCallback the client on complete callback
+ * @return the generated transaction
+ */
+ /* package */ ContextHubServiceTransaction createEnableTransaction(
+ int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback) {
+ return new ContextHubServiceTransaction(
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_ENABLE_NANOAPP) {
+ @Override
+ /* package */ int onTransact() {
+ try {
+ return mContextHubProxy.enableNanoApp(
+ contextHubId, nanoAppId, this.getTransactionId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while trying to enable nanoapp with ID 0x" +
+ Long.toHexString(nanoAppId), e);
+ return Result.UNKNOWN_FAILURE;
+ }
+ }
+
+ @Override
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ try {
+ onCompleteCallback.onTransactionComplete(result);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling client onTransactionComplete", e);
+ }
+ }
+ };
+ }
+
+ /**
+ * Creates a transaction for disabling a nanoapp.
+ *
+ * @param contextHubId the ID of the hub to disable the nanoapp on
+ * @param nanoAppId the ID of the nanoapp to disable
+ * @param onCompleteCallback the client on complete callback
+ * @return the generated transaction
+ */
+ /* package */ ContextHubServiceTransaction createDisableTransaction(
+ int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback) {
+ return new ContextHubServiceTransaction(
+ mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_DISABLE_NANOAPP) {
+ @Override
+ /* package */ int onTransact() {
+ try {
+ return mContextHubProxy.disableNanoApp(
+ contextHubId, nanoAppId, this.getTransactionId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while trying to disable nanoapp with ID 0x" +
+ Long.toHexString(nanoAppId), e);
+ return Result.UNKNOWN_FAILURE;
+ }
+ }
+
+ @Override
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+ try {
+ onCompleteCallback.onTransactionComplete(result);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling client onTransactionComplete", e);
+ }
+ }
+ };
+ }
+
+ /**
* Creates a transaction for querying for a list of nanoapps.
*
* @param contextHubId the ID of the hub to query
@@ -185,12 +273,13 @@
}
@Override
- /* package */ void onTransactionComplete(int result) {
+ /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
onQueryResponse(result, Collections.emptyList());
}
@Override
- /* package */ void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+ /* package */ void onQueryResponse(
+ @ContextHubTransaction.Result int result, List<NanoAppState> nanoAppStateList) {
try {
onCompleteCallback.onQueryResponse(result, nanoAppStateList);
} catch (RemoteException e) {
@@ -204,7 +293,8 @@
* Adds a new transaction to the queue.
*
* If there was no pending transaction at the time, the transaction that was added will be
- * started in this method.
+ * started in this method. If there were too many transactions in the queue, an exception will
+ * be thrown.
*
* @param transaction the transaction to add
* @throws IllegalStateException if the queue is full
@@ -213,7 +303,7 @@
synchronized void addTransaction(
ContextHubServiceTransaction transaction) throws IllegalStateException {
if (mTransactionQueue.size() == MAX_PENDING_REQUESTS) {
- throw new IllegalStateException("Transaction transaction queue is full (capacity = "
+ throw new IllegalStateException("Transaction queue is full (capacity = "
+ MAX_PENDING_REQUESTS + ")");
}
mTransactionQueue.add(transaction);
@@ -227,7 +317,7 @@
* Handles a transaction response from a Context Hub.
*
* @param transactionId the transaction ID of the response
- * @param result the result of the transaction
+ * @param result the result of the transaction as defined by the HAL TransactionResult
*/
/* package */
synchronized void onTransactionResponse(int transactionId, int result) {
@@ -242,7 +332,10 @@
return;
}
- transaction.onTransactionComplete(result);
+ transaction.onTransactionComplete(
+ (result == TransactionResult.SUCCESS) ?
+ ContextHubTransaction.TRANSACTION_SUCCESS :
+ ContextHubTransaction.TRANSACTION_FAILED_AT_HUB);
removeTransactionAndStartNext();
}
@@ -263,7 +356,7 @@
return;
}
- transaction.onQueryResponse(TransactionResult.SUCCESS, nanoAppStateList);
+ transaction.onQueryResponse(ContextHubTransaction.TRANSACTION_SUCCESS, nanoAppStateList);
removeTransactionAndStartNext();
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 0b511f2..3bd6446 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -233,13 +233,13 @@
private static final int AGPS_SETID_TYPE_IMSI = 1;
private static final int AGPS_SETID_TYPE_MSISDN = 2;
- private static final int GPS_GEOFENCE_UNAVAILABLE = 1<<0L;
- private static final int GPS_GEOFENCE_AVAILABLE = 1<<1L;
+ private static final int GPS_GEOFENCE_UNAVAILABLE = 1 << 0L;
+ private static final int GPS_GEOFENCE_AVAILABLE = 1 << 1L;
// GPS Geofence errors. Should match GeofenceStatus enum in IGnssGeofenceCallback.hal.
private static final int GPS_GEOFENCE_OPERATION_SUCCESS = 0;
private static final int GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 100;
- private static final int GPS_GEOFENCE_ERROR_ID_EXISTS = -101;
+ private static final int GPS_GEOFENCE_ERROR_ID_EXISTS = -101;
private static final int GPS_GEOFENCE_ERROR_ID_UNKNOWN = -102;
private static final int GPS_GEOFENCE_ERROR_INVALID_TRANSITION = -103;
private static final int GPS_GEOFENCE_ERROR_GENERIC = -149;
@@ -253,6 +253,7 @@
private static class GpsRequest {
public ProviderRequest request;
public WorkSource source;
+
public GpsRequest(ProviderRequest request, WorkSource source) {
this.request = request;
this.source = source;
@@ -280,15 +281,15 @@
// how often to request NTP time, in milliseconds
// current setting 24 hours
- private static final long NTP_INTERVAL = 24*60*60*1000;
+ private static final long NTP_INTERVAL = 24 * 60 * 60 * 1000;
// how long to wait if we have a network error in NTP or XTRA downloading
// the initial value of the exponential backoff
// current setting - 5 minutes
- private static final long RETRY_INTERVAL = 5*60*1000;
+ private static final long RETRY_INTERVAL = 5 * 60 * 1000;
// how long to wait if we have a network error in NTP or XTRA downloading
// the max value of the exponential backoff
// current setting - 4 hours
- private static final long MAX_RETRY_INTERVAL = 4*60*60*1000;
+ private static final long MAX_RETRY_INTERVAL = 4 * 60 * 60 * 1000;
// Timeout when holding wakelocks for downloading XTRA data.
private static final long DOWNLOAD_XTRA_DATA_TIMEOUT_MS = 60 * 1000;
@@ -321,6 +322,9 @@
// requested frequency of fixes, in milliseconds
private int mFixInterval = 1000;
+ // true if low power mode for the GNSS chipset is part of the latest request.
+ private boolean mLowPowerMode = false;
+
// true if we started navigation
private boolean mStarted;
@@ -398,7 +402,6 @@
private final static String LPP_PROFILE = "persist.sys.gps.lpp";
-
private final PowerManager mPowerManager;
private final AlarmManager mAlarmManager;
private final PendingIntent mWakeupIntent;
@@ -459,26 +462,27 @@
*/
private final ConnectivityManager.NetworkCallback mNetworkConnectivityCallback =
new ConnectivityManager.NetworkCallback() {
- @Override
- public void onAvailable(Network network) {
- if (mInjectNtpTimePending == STATE_PENDING_NETWORK) {
- requestUtcTime();
- }
- if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) {
- if (mSupportsXtra) {
- // Download only if supported, (prevents an unneccesary on-boot download)
- xtraDownloadRequest();
+ @Override
+ public void onAvailable(Network network) {
+ if (mInjectNtpTimePending == STATE_PENDING_NETWORK) {
+ requestUtcTime();
+ }
+ if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) {
+ if (mSupportsXtra) {
+ // Download only if supported, (prevents an unneccesary on-boot
+ // download)
+ xtraDownloadRequest();
+ }
+ }
+ // Always on, notify HAL so it can get data it needs
+ sendMessage(UPDATE_NETWORK_STATE, 0 /*arg*/, network);
}
- }
- // Always on, notify HAL so it can get data it needs
- sendMessage(UPDATE_NETWORK_STATE, 0 /*arg*/, network);
- }
- @Override
- public void onLost(Network network) {
- sendMessage(UPDATE_NETWORK_STATE, 0 /*arg*/, network);
- }
- };
+ @Override
+ public void onLost(Network network) {
+ sendMessage(UPDATE_NETWORK_STATE, 0 /*arg*/, network);
+ }
+ };
/**
* Callback used to listen for availability of a requested SUPL connection.
@@ -487,20 +491,21 @@
*/
private final ConnectivityManager.NetworkCallback mSuplConnectivityCallback =
new ConnectivityManager.NetworkCallback() {
- @Override
- public void onAvailable(Network network) {
- // Specific to a change to a SUPL enabled network becoming ready
- sendMessage(UPDATE_NETWORK_STATE, 0 /*arg*/, network);
- }
+ @Override
+ public void onAvailable(Network network) {
+ // Specific to a change to a SUPL enabled network becoming ready
+ sendMessage(UPDATE_NETWORK_STATE, 0 /*arg*/, network);
+ }
- @Override
- public void onLost(Network network) {
- releaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN);
- }
- };
+ @Override
+ public void onLost(Network network) {
+ releaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN);
+ }
+ };
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override public void onReceive(Context context, Intent intent) {
+ @Override
+ public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (DEBUG) Log.d(TAG, "receive broadcast intent, action: " + action);
if (action == null) {
@@ -524,11 +529,11 @@
private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
new OnSubscriptionsChangedListener() {
- @Override
- public void onSubscriptionsChanged() {
- sendMessage(SUBSCRIPTION_OR_SIM_CHANGED, 0, null);
- }
- };
+ @Override
+ public void onSubscriptionsChanged() {
+ sendMessage(SUBSCRIPTION_OR_SIM_CHANGED, 0, null);
+ }
+ };
private void subscriptionOrSimChanged(Context context) {
if (DEBUG) Log.d(TAG, "received SIM related action: ");
@@ -599,8 +604,8 @@
String lpp_prof = SystemProperties.get(LPP_PROFILE);
if (!TextUtils.isEmpty(lpp_prof)) {
- // override default value of this if lpp_prof is not empty
- properties.setProperty("LPP_PROFILE", lpp_prof);
+ // override default value of this if lpp_prof is not empty
+ properties.setProperty("LPP_PROFILE", lpp_prof);
}
/*
* Overlay carrier properties from a debug configuration file.
@@ -608,7 +613,7 @@
loadPropertiesFromFile(DEBUG_PROPERTIES_FILE, properties);
// TODO: we should get rid of C2K specific setting.
setSuplHostPort(properties.getProperty("SUPL_HOST"),
- properties.getProperty("SUPL_PORT"));
+ properties.getProperty("SUPL_PORT"));
mC2KServerHost = properties.getProperty("C2K_HOST");
String portString = properties.getProperty("C2K_PORT");
if (mC2KServerHost != null && portString != null) {
@@ -625,24 +630,26 @@
put("SUPL_MODE", (val) -> native_set_supl_mode(val));
put("SUPL_ES", (val) -> native_set_supl_es(val));
put("LPP_PROFILE", (val) -> native_set_lpp_profile(val));
- put("A_GLONASS_POS_PROTOCOL_SELECT", (val) -> native_set_gnss_pos_protocol_select(val));
- put("USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL", (val) -> native_set_emergency_supl_pdn(val));
+ put("A_GLONASS_POS_PROTOCOL_SELECT",
+ (val) -> native_set_gnss_pos_protocol_select(val));
+ put("USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL",
+ (val) -> native_set_emergency_supl_pdn(val));
put("GPS_LOCK", (val) -> native_set_gps_lock(val));
}
};
- for(Entry<String, SetCarrierProperty> entry : map.entrySet()) {
+ for (Entry<String, SetCarrierProperty> entry : map.entrySet()) {
String propertyName = entry.getKey();
String propertyValueString = properties.getProperty(propertyName);
if (propertyValueString != null) {
try {
- int propertyValueInt = Integer.decode(propertyValueString);
- boolean result = entry.getValue().set(propertyValueInt);
- if (result == false) {
- Log.e(TAG, "Unable to set " + propertyName);
- }
+ int propertyValueInt = Integer.decode(propertyValueString);
+ boolean result = entry.getValue().set(propertyValueInt);
+ if (result == false) {
+ Log.e(TAG, "Unable to set " + propertyName);
+ }
} catch (NumberFormatException e) {
- Log.e(TAG, "unable to parse propertyName: " + propertyValueString);
+ Log.e(TAG, "unable to parse propertyName: " + propertyValueString);
}
}
}
@@ -663,7 +670,7 @@
}
private void loadPropertiesFromResource(Context context,
- Properties properties) {
+ Properties properties) {
String[] configValues = context.getResources().getStringArray(
com.android.internal.R.array.config_gpsParameters);
for (String item : configValues) {
@@ -681,7 +688,7 @@
}
private boolean loadPropertiesFromFile(String filename,
- Properties properties) {
+ Properties properties) {
try {
File file = new File(filename);
FileInputStream stream = null;
@@ -717,11 +724,11 @@
PowerManager.PARTIAL_WAKE_LOCK, DOWNLOAD_EXTRA_WAKELOCK_KEY);
mDownloadXtraWakeLock.setReferenceCounted(true);
- mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
- mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ mConnMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
// App ops service to keep track of who is accessing the GPS
mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(
@@ -744,8 +751,8 @@
// Create a GPS net-initiated handler.
mNIHandler = new GpsNetInitiatedHandler(context,
- mNetInitiatedListener,
- mSuplEsEnabled);
+ mNetInitiatedListener,
+ mSuplEsEnabled);
mListenerHelper = new GnssStatusListenerHelper(mHandler) {
@Override
@@ -766,8 +773,23 @@
}
@Override
- protected boolean registerWithService() {
- return native_start_measurement_collection();
+ protected int registerWithService() {
+ int devOptions = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED , 0);
+ int fullTrackingToggled = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING , 0);
+ boolean result = false;
+ if (devOptions == 1 /* Developer Mode enabled */
+ && fullTrackingToggled == 1 /* Raw Measurements Full Tracking enabled */) {
+ result = native_start_measurement_collection(true /* enableFullTracking */);
+ } else {
+ result = native_start_measurement_collection(false /* enableFullTracking */);
+ }
+ if (result) {
+ return RemoteListenerHelper.RESULT_SUCCESS;
+ } else {
+ return RemoteListenerHelper.RESULT_INTERNAL_ERROR;
+ }
}
@Override
@@ -788,8 +810,13 @@
}
@Override
- protected boolean registerWithService() {
- return native_start_navigation_message_collection();
+ protected int registerWithService() {
+ boolean result = native_start_navigation_message_collection();
+ if (result) {
+ return RemoteListenerHelper.RESULT_SUCCESS;
+ } else {
+ return RemoteListenerHelper.RESULT_INTERNAL_ERROR;
+ }
}
@Override
@@ -1116,9 +1143,9 @@
* Checks what SUPL mode to use, according to the AGPS mode as well as the
* allowed mode from properties.
*
- * @param properties GPS properties
+ * @param properties GPS properties
* @param agpsEnabled whether AGPS is enabled by settings value
- * @param singleShot whether "singleshot" is needed
+ * @param singleShot whether "singleshot" is needed
* @return SUPL mode (MSA vs MSB vs STANDALONE)
*/
private int getSuplMode(Properties properties, boolean agpsEnabled, boolean singleShot) {
@@ -1222,7 +1249,7 @@
}
private void updateStatus(int status, int svCount, int meanCn0, int maxCn0) {
- if (status != mStatus || svCount != mSvCount || meanCn0 != mMeanCn0 || maxCn0 != mMaxCn0 ) {
+ if (status != mStatus || svCount != mSvCount || meanCn0 != mMeanCn0 || maxCn0 != mMaxCn0) {
mStatus = status;
mSvCount = svCount;
mMeanCn0 = meanCn0;
@@ -1284,7 +1311,7 @@
updateClientUids(mWorkSource);
mFixInterval = (int) mProviderRequest.interval;
-
+ mLowPowerMode = (boolean) mProviderRequest.lowPowerMode;
// check for overflow
if (mFixInterval != mProviderRequest.interval) {
Log.w(TAG, "interval overflow: " + mProviderRequest.interval);
@@ -1293,14 +1320,22 @@
// apply request to GPS engine
if (mStarted && hasCapability(GPS_CAPABILITY_SCHEDULING)) {
- // change period
+ // change period and/or lowPowerMode
if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
- mFixInterval, 0, 0)) {
- Log.e(TAG, "set_position_mode failed in setMinTime()");
+ mFixInterval, 0, 0, mLowPowerMode)) {
+ Log.e(TAG, "set_position_mode failed in updateRequirements");
}
} else if (!mStarted) {
// start GPS
startNavigating(singleShot);
+ } else {
+ // GNSS Engine is already ON, but no GPS_CAPABILITY_SCHEDULING
+ mAlarmManager.cancel(mTimeoutIntent);
+ if (mFixInterval >= NO_FIX_TIMEOUT) {
+ // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
+ // and our fix interval is not short
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent); }
}
} else {
updateClientUids(new WorkSource());
@@ -1323,7 +1358,7 @@
// Update sources that were not previously tracked.
if (newWork != null) {
int lastuid = -1;
- for (int i=0; i<newWork.size(); i++) {
+ for (int i = 0; i < newWork.size(); i++) {
try {
int uid = newWork.get(i);
mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
@@ -1341,7 +1376,7 @@
// Update sources that are no longer tracked.
if (goneWork != null) {
int lastuid = -1;
- for (int i=0; i<goneWork.size(); i++) {
+ for (int i = 0; i < goneWork.size(); i++) {
try {
int uid = goneWork.get(i);
mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
@@ -1455,13 +1490,13 @@
boolean agpsEnabled =
(Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.ASSISTED_GPS_ENABLED, 1) != 0);
+ Settings.Global.ASSISTED_GPS_ENABLED, 1) != 0);
mPositionMode = getSuplMode(mProperties, agpsEnabled, singleShot);
if (DEBUG) {
String mode;
- switch(mPositionMode) {
+ switch (mPositionMode) {
case GPS_POSITION_MODE_STANDALONE:
mode = "standalone";
break;
@@ -1479,8 +1514,9 @@
}
int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
+ mLowPowerMode = (boolean) mProviderRequest.lowPowerMode;
if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
- interval, 0, 0)) {
+ interval, 0, 0, mLowPowerMode)) {
mStarted = false;
Log.e(TAG, "set_position_mode failed in startNavigating()");
return;
@@ -1578,7 +1614,7 @@
mLastFixTime = SystemClock.elapsedRealtime();
// report time to first fix
if (mTimeToFirstFix == 0 && hasLatLong) {
- mTimeToFirstFix = (int)(mLastFixTime - mFixRequestTime);
+ mTimeToFirstFix = (int) (mLastFixTime - mFixRequestTime);
if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix);
mGnssMetrics.logTimeToFirstFixMilliSecs(mTimeToFirstFix);
@@ -1604,12 +1640,12 @@
updateStatus(LocationProvider.AVAILABLE, mSvCount, mMeanCn0, mMaxCn0);
}
- if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted &&
- mFixInterval > GPS_POLLING_THRESHOLD_INTERVAL) {
+ if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted &&
+ mFixInterval > GPS_POLLING_THRESHOLD_INTERVAL) {
if (DEBUG) Log.d(TAG, "got fix, hibernating");
hibernate();
}
- }
+ }
/**
* called from native code to update our status
@@ -1650,10 +1686,10 @@
*/
private void reportSvStatus() {
int svCount = native_read_sv_status(mSvidWithFlags,
- mCn0s,
- mSvElevations,
- mSvAzimuths,
- mSvCarrierFreqs);
+ mCn0s,
+ mSvElevations,
+ mSvAzimuths,
+ mSvCarrierFreqs);
mListenerHelper.onSvStatusChanged(
svCount,
mSvidWithFlags,
@@ -1676,7 +1712,7 @@
if ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
++usedInFixCount;
if (mCn0s[i] > maxCn0) {
- maxCn0 = (int)mCn0s[i];
+ maxCn0 = (int) mCn0s[i];
}
meanCn0 += mCn0s[i];
}
@@ -1693,7 +1729,7 @@
((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) == 0
? "" : "U") +
((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY) == 0
- ? "" : "F"));
+ ? "" : "F"));
}
}
if (usedInFixCount > 0) {
@@ -1703,7 +1739,7 @@
updateStatus(mStatus, usedInFixCount, meanCn0, maxCn0);
if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
- SystemClock.elapsedRealtime() - mLastFixTime > RECENT_FIX_TIMEOUT) {
+ SystemClock.elapsedRealtime() - mLastFixTime > RECENT_FIX_TIMEOUT) {
// send an intent to notify that the GPS is no longer receiving fixes.
Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
@@ -1843,14 +1879,17 @@
* Returns the GNSS batching size
*/
int getSize();
+
/**
* Starts the hardware batching operation
*/
boolean start(long periodNanos, boolean wakeOnFifoFull);
+
/**
* Forces a flush of existing locations from the hardware batching
*/
void flush();
+
/**
* Stops the batching operation
*/
@@ -1866,6 +1905,7 @@
public int getSize() {
return native_get_batch_size();
}
+
@Override
public boolean start(long periodNanos, boolean wakeOnFifoFull) {
if (periodNanos <= 0) {
@@ -1875,10 +1915,12 @@
}
return native_start_batch(periodNanos, wakeOnFifoFull);
}
+
@Override
public void flush() {
native_flush_batch();
}
+
@Override
public boolean stop() {
return native_stop_batch();
@@ -1911,7 +1953,7 @@
private void enableBatching() {
if (!native_init_batching()) {
Log.e(TAG, "Failed to initialize GNSS batching");
- };
+ }
}
/**
@@ -1927,7 +1969,9 @@
*/
private void reportLocationBatch(Location[] locationArray) {
List<Location> locations = new ArrayList<>(Arrays.asList(locationArray));
- if(DEBUG) { Log.d(TAG, "Location batch of size " + locationArray.length + "reported"); }
+ if (DEBUG) {
+ Log.d(TAG, "Location batch of size " + locationArray.length + " reported");
+ }
try {
mILocationManager.reportLocationBatch(locations);
} catch (RemoteException e) {
@@ -1947,7 +1991,7 @@
* Converts the GPS HAL status to the internal Geofence Hardware status.
*/
private int getGeofenceStatus(int status) {
- switch(status) {
+ switch (status) {
case GPS_GEOFENCE_OPERATION_SUCCESS:
return GeofenceHardware.GEOFENCE_SUCCESS;
case GPS_GEOFENCE_ERROR_GENERIC:
@@ -1970,7 +2014,7 @@
* All geofence callbacks are called on the same thread
*/
private void reportGeofenceTransition(int geofenceId, Location location, int transition,
- long transitionTimestamp) {
+ long transitionTimestamp) {
if (mGeofenceHardwareImpl == null) {
mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
}
@@ -1992,7 +2036,7 @@
mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
}
int monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
- if(status == GPS_GEOFENCE_AVAILABLE) {
+ if (status == GPS_GEOFENCE_AVAILABLE) {
monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
}
mGeofenceHardwareImpl.reportGeofenceMonitorStatus(
@@ -2048,12 +2092,13 @@
private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
// Sends a response for an NI request to HAL.
@Override
- public boolean sendNiResponse(int notificationId, int userResponse)
- {
+ public boolean sendNiResponse(int notificationId, int userResponse) {
// TODO Add Permission check
- if (DEBUG) Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
- ", response: " + userResponse);
+ if (DEBUG) {
+ Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
+ ", response: " + userResponse);
+ }
native_send_ni_response(notificationId, userResponse);
return true;
}
@@ -2074,8 +2119,7 @@
String text,
int requestorIdEncoding,
int textEncoding
- )
- {
+ ) {
Log.i(TAG, "reportNiNotification: entered");
Log.i(TAG, "notificationId: " + notificationId +
", niType: " + niType +
@@ -2094,7 +2138,8 @@
notification.niType = niType;
notification.needNotify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_NOTIFY) != 0;
notification.needVerify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_VERIFY) != 0;
- notification.privacyOverride = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_PRIVACY_OVERRIDE) != 0;
+ notification.privacyOverride =
+ (notifyFlags & GpsNetInitiatedHandler.GPS_NI_PRIVACY_OVERRIDE) != 0;
notification.timeout = timeout;
notification.defaultResponse = defaultResponse;
notification.requestorId = requestorId;
@@ -2126,8 +2171,7 @@
data = data_temp;
type = AGPS_SETID_TYPE_IMSI;
}
- }
- else if ((flags & AGPS_RIL_REQUEST_SETID_MSISDN) == AGPS_RIL_REQUEST_SETID_MSISDN) {
+ } else if ((flags & AGPS_RIL_REQUEST_SETID_MSISDN) == AGPS_RIL_REQUEST_SETID_MSISDN) {
String data_temp = phone.getLine1Number();
if (data_temp == null) {
// This means the framework does not have the SIM card ready.
@@ -2161,14 +2205,14 @@
if ((gsm_cell != null) && (phone.getNetworkOperator() != null)
&& (phone.getNetworkOperator().length() > 3)) {
int type;
- int mcc = Integer.parseInt(phone.getNetworkOperator().substring(0,3));
+ int mcc = Integer.parseInt(phone.getNetworkOperator().substring(0, 3));
int mnc = Integer.parseInt(phone.getNetworkOperator().substring(3));
int networkType = phone.getNetworkType();
if (networkType == TelephonyManager.NETWORK_TYPE_UMTS
- || networkType == TelephonyManager.NETWORK_TYPE_HSDPA
- || networkType == TelephonyManager.NETWORK_TYPE_HSUPA
- || networkType == TelephonyManager.NETWORK_TYPE_HSPA
- || networkType == TelephonyManager.NETWORK_TYPE_HSPAP) {
+ || networkType == TelephonyManager.NETWORK_TYPE_HSDPA
+ || networkType == TelephonyManager.NETWORK_TYPE_HSUPA
+ || networkType == TelephonyManager.NETWORK_TYPE_HSPA
+ || networkType == TelephonyManager.NETWORK_TYPE_HSPAP) {
type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
} else {
type = AGPS_REF_LOCATION_TYPE_GSM_CELLID;
@@ -2176,7 +2220,7 @@
native_agps_set_ref_location_cellid(type, mcc, mnc,
gsm_cell.getLac(), gsm_cell.getCid());
} else {
- Log.e(TAG,"Error getting cell location info.");
+ Log.e(TAG, "Error getting cell location info.");
}
} else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
Log.e(TAG, "CDMA not supported.");
@@ -2269,7 +2313,7 @@
* restart.
*/
boolean isInitialized = native_init();
- if(!isInitialized) {
+ if (!isInitialized) {
Log.w(TAG, "Native initialization failed at bootup");
} else {
native_cleanup();
@@ -2280,7 +2324,8 @@
reloadGpsProperties(mContext, mProperties);
// TODO: When this object "finishes" we should unregister by invoking
- // SubscriptionManager.getInstance(mContext).unregister(mOnSubscriptionsChangedListener);
+ // SubscriptionManager.getInstance(mContext).unregister
+ // (mOnSubscriptionsChangedListener);
// This is not strictly necessary because it will be unregistered if the
// notification fails but it is good form.
@@ -2359,12 +2404,18 @@
handleUpdateLocation(location);
}
}
+
@Override
- public void onStatusChanged(String provider, int status, Bundle extras) { }
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+
@Override
- public void onProviderEnabled(String provider) { }
+ public void onProviderEnabled(String provider) {
+ }
+
@Override
- public void onProviderDisabled(String provider) { }
+ public void onProviderDisabled(String provider) {
+ }
}
private String getSelectedApn() {
@@ -2373,7 +2424,7 @@
try {
cursor = mContext.getContentResolver().query(
uri,
- new String[] { "apn" },
+ new String[]{"apn"},
null /* selection */,
null /* selectionArgs */,
Carriers.DEFAULT_SORT_ORDER);
@@ -2404,7 +2455,7 @@
try {
cursor = mContext.getContentResolver().query(
Carriers.CONTENT_URI,
- new String[] { Carriers.PROTOCOL },
+ new String[]{Carriers.PROTOCOL},
selection,
null,
Carriers.DEFAULT_SORT_ORDER);
@@ -2461,7 +2512,7 @@
/**
* @return {@code true} if there is a data network available for outgoing connections,
- * {@code false} otherwise.
+ * {@code false} otherwise.
*/
private boolean isDataNetworkConnected() {
NetworkInfo activeNetworkInfo = mConnMgr.getActiveNetworkInfo();
@@ -2482,7 +2533,7 @@
* @return A string representing the current state stored in {@link #mAGpsDataConnectionState}.
*/
private String agpsDataConnStateAsString() {
- switch(mAGpsDataConnectionState) {
+ switch (mAGpsDataConnectionState) {
case AGPS_DATA_CONNECTION_CLOSED:
return "CLOSED";
case AGPS_DATA_CONNECTION_OPEN:
@@ -2549,12 +2600,12 @@
}
-
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
StringBuilder s = new StringBuilder();
s.append(" mStarted=").append(mStarted).append('\n');
s.append(" mFixInterval=").append(mFixInterval).append('\n');
+ s.append(" mLowPowerMode=").append(mLowPowerMode).append('\n');
s.append(" mDisableGps (battery saver mode)=").append(mDisableGps).append('\n');
s.append(" mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities));
s.append(" ( ");
@@ -2618,29 +2669,45 @@
// preallocated to avoid memory allocation in reportNmea()
private byte[] mNmeaBuffer = new byte[120];
- static { class_init_native(); }
+ static {
+ class_init_native();
+ }
+
private static native void class_init_native();
+
private static native boolean native_is_supported();
+
private static native boolean native_is_agps_ril_supported();
+
private static native boolean native_is_gnss_configuration_supported();
private native boolean native_init();
+
private native void native_cleanup();
+
private native boolean native_set_position_mode(int mode, int recurrence, int min_interval,
- int preferred_accuracy, int preferred_time);
+ int preferred_accuracy, int preferred_time, boolean lowPowerMode);
+
private native boolean native_start();
+
private native boolean native_stop();
+
private native void native_delete_aiding_data(int flags);
+
// returns number of SVs
// mask[0] is ephemeris mask and mask[1] is almanac mask
private native int native_read_sv_status(int[] prnWithFlags, float[] cn0s, float[] elevations,
float[] azimuths, float[] carrierFrequencies);
+
private native int native_read_nmea(byte[] buffer, int bufferSize);
+
private native void native_inject_location(double latitude, double longitude, float accuracy);
// XTRA Support
private native void native_inject_time(long time, long timeReference, int uncertainty);
+
private native boolean native_supports_xtra();
+
private native void native_inject_xtra_data(byte[] data, int length);
// DEBUG Support
@@ -2648,9 +2715,13 @@
// AGPS Support
private native void native_agps_data_conn_open(String apn, int apnIpType);
+
private native void native_agps_data_conn_closed();
+
private native void native_agps_data_conn_failed();
- private native void native_agps_ni_message(byte [] msg, int length);
+
+ private native void native_agps_ni_message(byte[] msg, int length);
+
private native void native_set_agps_server(int type, String hostname, int port);
// Network-initiated (NI) Support
@@ -2659,6 +2730,7 @@
// AGPS ril suport
private native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
int lac, int cid);
+
private native void native_agps_set_id(int type, String setid);
private native void native_update_network_state(boolean connected, int type,
@@ -2666,38 +2738,57 @@
// Hardware Geofence support.
private static native boolean native_is_geofence_supported();
+
private static native boolean native_add_geofence(int geofenceId, double latitude,
- double longitude, double radius, int lastTransition,int monitorTransitions,
+ double longitude, double radius, int lastTransition, int monitorTransitions,
int notificationResponsivenes, int unknownTimer);
+
private static native boolean native_remove_geofence(int geofenceId);
+
private static native boolean native_resume_geofence(int geofenceId, int transitions);
+
private static native boolean native_pause_geofence(int geofenceId);
// Gps Hal measurements support.
private static native boolean native_is_measurement_supported();
- private native boolean native_start_measurement_collection();
+
+ private native boolean native_start_measurement_collection(boolean enableFullTracking);
+
private native boolean native_stop_measurement_collection();
// Gps Navigation message support.
private static native boolean native_is_navigation_message_supported();
+
private native boolean native_start_navigation_message_collection();
+
private native boolean native_stop_navigation_message_collection();
// GNSS Configuration
private static native boolean native_set_supl_version(int version);
+
private static native boolean native_set_supl_mode(int mode);
+
private static native boolean native_set_supl_es(int es);
+
private static native boolean native_set_lpp_profile(int lppProfile);
+
private static native boolean native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect);
+
private static native boolean native_set_gps_lock(int gpsLock);
+
private static native boolean native_set_emergency_supl_pdn(int emergencySuplPdn);
// GNSS Batching
private static native int native_get_batch_size();
+
private static native boolean native_start_batch(long periodNanos, boolean wakeOnFifoFull);
+
private static native void native_flush_batch();
+
private static native boolean native_stop_batch();
+
private static native boolean native_init_batching();
+
private static native void native_cleanup_batching();
}
diff --git a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
index 924520b..477dae6 100644
--- a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
@@ -40,11 +40,11 @@
public void onMeasurementsAvailable(final GnssMeasurementsEvent event) {
ListenerOperation<IGnssMeasurementsListener> operation =
new ListenerOperation<IGnssMeasurementsListener>() {
- @Override
- public void execute(IGnssMeasurementsListener listener) throws RemoteException {
- listener.onGnssMeasurementsReceived(event);
- }
- };
+ @Override
+ public void execute(IGnssMeasurementsListener listener) throws RemoteException {
+ listener.onGnssMeasurementsReceived(event);
+ }
+ };
foreach(operation);
}
@@ -70,6 +70,9 @@
case RESULT_INTERNAL_ERROR:
status = GnssMeasurementsEvent.Callback.STATUS_NOT_SUPPORTED;
break;
+ case RESULT_NOT_ALLOWED:
+ status = GnssMeasurementsEvent.Callback.STATUS_NOT_ALLOWED;
+ break;
case RESULT_GPS_LOCATION_DISABLED:
status = GnssMeasurementsEvent.Callback.STATUS_LOCATION_DISABLED;
break;
diff --git a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
index fe2bb38..124220f 100644
--- a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
@@ -30,8 +30,8 @@
}
@Override
- protected boolean registerWithService() {
- return true;
+ protected int registerWithService() {
+ return RemoteListenerHelper.RESULT_SUCCESS;
}
@Override
diff --git a/services/core/java/com/android/server/location/NanoAppStateManager.java b/services/core/java/com/android/server/location/NanoAppStateManager.java
new file mode 100644
index 0000000..9869626
--- /dev/null
+++ b/services/core/java/com/android/server/location/NanoAppStateManager.java
@@ -0,0 +1,192 @@
+/*
+ * 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 com.android.server.location;
+
+import android.annotation.Nullable;
+import android.hardware.contexthub.V1_0.HubAppInfo;
+import android.hardware.location.NanoAppInstanceInfo;
+import android.util.Log;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Manages the state of loaded nanoapps at the Context Hubs.
+ *
+ * This class maintains a list of nanoapps that have been informed as loaded at the hubs. The state
+ * should be updated based on the hub callbacks (defined in IContexthubCallback.hal), as a result
+ * of either loadNanoApp, unloadNanoApp, or queryApps.
+ *
+ * The state tracked by this manager is used by clients of ContextHubService that use the old APIs.
+ *
+ * TODO(b/69270990): Remove this class and its logic once the old API is deprecated.
+ *
+ * @hide
+ */
+/* package */ class NanoAppStateManager {
+ private static final String TAG = "NanoAppStateManager";
+
+ /*
+ * Enables verbose debug logs for this class.
+ */
+ private static final boolean ENABLE_LOG_DEBUG = true;
+
+ /*
+ * Service cache maintaining of handle to nanoapp infos.
+ */
+ private final HashMap<Integer, NanoAppInstanceInfo> mNanoAppHash = new HashMap<>();
+
+ /*
+ * The next nanoapp handle to use.
+ */
+ private int mNextHandle = 0;
+
+ /**
+ * @param nanoAppHandle the nanoapp handle
+ * @return the NanoAppInstanceInfo for the given nanoapp, or null if the nanoapp does not exist
+ * in the cache
+ */
+ @Nullable
+ /* package */
+ synchronized NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) {
+ return mNanoAppHash.get(nanoAppHandle);
+ }
+
+ /**
+ * @return a collection of NanoAppInstanceInfo objects in the cache
+ */
+ /* package */
+ synchronized Collection<NanoAppInstanceInfo> getNanoAppInstanceInfoCollection() {
+ return mNanoAppHash.values();
+ }
+
+ /**
+ * @param contextHubId the ID of the hub to search for the instance
+ * @param nanoAppId the unique 64-bit ID of the nanoapp
+ * @return the nanoapp handle, -1 if the nanoapp is not in the cache
+ */
+ /* package */
+ synchronized int getNanoAppHandle(int contextHubId, long nanoAppId) {
+ for (NanoAppInstanceInfo info : mNanoAppHash.values()) {
+ if (info.getContexthubId() == contextHubId && info.getAppId() == nanoAppId) {
+ return info.getHandle();
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Adds a nanoapp instance to the cache.
+ *
+ * If the cache already contained the nanoapp, the entry is removed and a new nanoapp handle is
+ * generated.
+ *
+ * @param contextHubId the ID of the hub the nanoapp is loaded in
+ * @param nanoAppId the unique 64-bit ID of the nanoapp
+ * @param nanoAppVersion the version of the nanoapp
+ */
+ /* package */
+ synchronized void addNanoAppInstance(int contextHubId, long nanoAppId, int nanoAppVersion) {
+ removeNanoAppInstance(contextHubId, nanoAppId);
+ if (mNanoAppHash.size() == Integer.MAX_VALUE) {
+ Log.e(TAG, "Error adding nanoapp instance: max limit exceeded");
+ return;
+ }
+
+ int nanoAppHandle = mNextHandle;
+ for (int i = 0; i <= Integer.MAX_VALUE; i++) {
+ if (!mNanoAppHash.containsKey(nanoAppHandle)) {
+ mNanoAppHash.put(nanoAppHandle, new NanoAppInstanceInfo(
+ nanoAppHandle, nanoAppId, nanoAppVersion, contextHubId));
+ mNextHandle = (nanoAppHandle == Integer.MAX_VALUE) ? 0 : nanoAppHandle + 1;
+ break;
+ }
+ nanoAppHandle = (nanoAppHandle == Integer.MAX_VALUE) ? 0 : nanoAppHandle + 1;
+ }
+
+ if (ENABLE_LOG_DEBUG) {
+ Log.v(TAG, "Added app instance with handle " + nanoAppHandle + " to hub "
+ + contextHubId + ": ID=0x" + Long.toHexString(nanoAppId)
+ + ", version=0x" + Integer.toHexString(nanoAppVersion));
+ }
+ }
+
+ /**
+ * Removes a nanoapp instance from the cache.
+ *
+ * @param nanoAppId the ID of the nanoapp to remove the instance of
+ */
+ /* package */
+ synchronized void removeNanoAppInstance(int contextHubId, long nanoAppId) {
+ int nanoAppHandle = getNanoAppHandle(contextHubId, nanoAppId);
+ mNanoAppHash.remove(nanoAppHandle);
+ }
+
+ /**
+ * Performs a batch update of the nanoapp cache given a nanoapp query response.
+ *
+ * @param contextHubId the ID of the hub the response came from
+ * @param nanoAppInfoList the list of loaded nanoapps
+ */
+ /* package */
+ synchronized void updateCache(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
+ HashSet<Long> nanoAppIdSet = new HashSet<>();
+ for (HubAppInfo appInfo : nanoAppInfoList) {
+ handleQueryAppEntry(contextHubId, appInfo.appId, appInfo.version);
+ nanoAppIdSet.add(appInfo.appId);
+ }
+
+ Iterator<NanoAppInstanceInfo> iterator = mNanoAppHash.values().iterator();
+ while (iterator.hasNext()) {
+ NanoAppInstanceInfo info = iterator.next();
+ if (info.getContexthubId() == contextHubId &&
+ !nanoAppIdSet.contains(info.getAppId())) {
+ iterator.remove();
+ }
+ }
+ }
+
+ /**
+ * If the nanoapp exists in the cache, then the entry is updated. Otherwise, inserts a new
+ * instance of the nanoapp in the cache. This method should only be invoked from updateCache.
+ *
+ * @param contextHubId the ID of the hub the nanoapp is loaded in
+ * @param nanoAppId the unique 64-bit ID of the nanoapp
+ * @param nanoAppVersion the version of the nanoapp
+ */
+ private void handleQueryAppEntry(int contextHubId, long nanoAppId, int nanoAppVersion) {
+ int nanoAppHandle = getNanoAppHandle(contextHubId, nanoAppId);
+ if (nanoAppHandle == -1) {
+ addNanoAppInstance(contextHubId, nanoAppId, nanoAppVersion);
+ } else {
+ NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppHandle);
+ if (info.getAppVersion() != nanoAppVersion) {
+ mNanoAppHash.put(nanoAppHandle, new NanoAppInstanceInfo(
+ nanoAppHandle, nanoAppId, nanoAppVersion, contextHubId));
+ if (ENABLE_LOG_DEBUG) {
+ Log.v(TAG, "Updated app instance with handle " + nanoAppHandle + " at hub "
+ + contextHubId + ": ID=0x" + Long.toHexString(nanoAppId)
+ + ", version=0x" + Integer.toHexString(nanoAppVersion));
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/OWNERS b/services/core/java/com/android/server/location/OWNERS
index d3c0ea8..5f0369b 100644
--- a/services/core/java/com/android/server/location/OWNERS
+++ b/services/core/java/com/android/server/location/OWNERS
@@ -1,4 +1,6 @@
ashutoshj@google.com
bduddie@google.com
+gomo@google.com
+sooniln@google.com
weiwa@google.com
wyattriley@google.com
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index f51bc87..58a9516 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -16,8 +16,6 @@
package com.android.server.location;
-import com.android.internal.util.Preconditions;
-
import android.annotation.NonNull;
import android.os.Handler;
import android.os.IBinder;
@@ -25,7 +23,8 @@
import android.os.RemoteException;
import android.util.Log;
-import java.lang.Runnable;
+import com.android.internal.util.Preconditions;
+
import java.util.HashMap;
import java.util.Map;
@@ -40,6 +39,7 @@
protected static final int RESULT_GPS_LOCATION_DISABLED = 3;
protected static final int RESULT_INTERNAL_ERROR = 4;
protected static final int RESULT_UNKNOWN = 5;
+ protected static final int RESULT_NOT_ALLOWED = 6;
private final Handler mHandler;
private final String mTag;
@@ -118,7 +118,8 @@
protected abstract boolean isAvailableInPlatform();
protected abstract boolean isGpsEnabled();
- protected abstract boolean registerWithService(); // must access only on handler thread
+ // must access only on handler thread
+ protected abstract int registerWithService();
protected abstract void unregisterFromService(); // must access only on handler thread
protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
@@ -177,10 +178,12 @@
private void tryRegister() {
mHandler.post(new Runnable() {
+ int registrationState = RESULT_INTERNAL_ERROR;
@Override
public void run() {
if (!mIsRegistered) {
- mIsRegistered = registerWithService();
+ registrationState = registerWithService();
+ mIsRegistered = registrationState == RESULT_SUCCESS;
}
if (!mIsRegistered) {
// post back a failure
@@ -188,7 +191,7 @@
@Override
public void run() {
synchronized (mListenerMap) {
- ListenerOperation<TListener> operation = getHandlerOperation(RESULT_INTERNAL_ERROR);
+ ListenerOperation<TListener> operation = getHandlerOperation(registrationState);
foreachUnsafe(operation);
}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 60f451a..aa55930 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -28,6 +28,8 @@
import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.IActivityManager;
@@ -60,6 +62,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.os.ShellCallback;
import android.os.StrictMode;
import android.os.SystemProperties;
@@ -75,6 +78,10 @@
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.security.keystore.UserNotAuthenticatedException;
+import android.security.recoverablekeystore.KeyEntryRecoveryData;
+import android.security.recoverablekeystore.KeyStoreRecoveryData;
+import android.security.recoverablekeystore.KeyStoreRecoveryMetadata;
+import android.security.recoverablekeystore.RecoverableKeyStoreLoader.RecoverableKeyStoreLoaderException;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.text.TextUtils;
@@ -95,9 +102,10 @@
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.SystemService;
import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
+import com.android.server.locksettings.LockSettingsStorage.PersistentData;
+import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
-import com.android.server.locksettings.LockSettingsStorage.PersistentData;
import libcore.util.HexEncoding;
@@ -169,6 +177,8 @@
private final KeyStore mKeyStore;
+ private final RecoverableKeyStoreManager mRecoverableKeyStoreManager;
+
private boolean mFirstCallToVold;
protected IGateKeeperService mGateKeeperService;
@@ -367,6 +377,10 @@
return KeyStore.getInstance();
}
+ public RecoverableKeyStoreManager getRecoverableKeyStoreManager() {
+ return RecoverableKeyStoreManager.getInstance(mContext);
+ }
+
public IStorageManager getStorageManager() {
final IBinder service = ServiceManager.getService("mount");
if (service != null) {
@@ -393,6 +407,7 @@
mInjector = injector;
mContext = injector.getContext();
mKeyStore = injector.getKeyStore();
+ mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
mHandler = injector.getHandler();
mStrongAuth = injector.getStrongAuth();
mActivityManager = injector.getActivityManager();
@@ -1914,6 +1929,72 @@
}
}
+ @Override
+ public void initRecoveryService(@NonNull String rootCertificateAlias,
+ @NonNull byte[] signedPublicKeyList, int userId)
+ throws RemoteException {
+ mRecoverableKeyStoreManager.initRecoveryService(rootCertificateAlias,
+ signedPublicKeyList, userId);
+ }
+
+ @Override
+ public KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account, int userId)
+ throws RemoteException {
+ return mRecoverableKeyStoreManager.getRecoveryData(account, userId);
+ }
+
+ @Override
+ public void setServerParameters(long serverParameters, int userId) throws RemoteException {
+ mRecoverableKeyStoreManager.setServerParameters(serverParameters, userId);
+ }
+
+ @Override
+ public void setRecoveryStatus(@NonNull String packageName, @Nullable String[] aliases,
+ int status, int userId) throws RemoteException {
+ mRecoverableKeyStoreManager.setRecoveryStatus(packageName, aliases, status, userId);
+ }
+
+ @Override
+ public void setRecoverySecretTypes(@NonNull @KeyStoreRecoveryMetadata.UserSecretType
+ int[] secretTypes, int userId) throws RemoteException {
+ mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes, userId);
+ }
+
+ @Override
+ public int[] getRecoverySecretTypes(int userId) throws RemoteException {
+ return mRecoverableKeyStoreManager.getRecoverySecretTypes(userId);
+
+ }
+
+ @Override
+ public int[] getPendingRecoverySecretTypes(int userId) throws RemoteException {
+ throw new SecurityException("Not implemented");
+ }
+
+ @Override
+ public void recoverySecretAvailable(@NonNull KeyStoreRecoveryMetadata recoverySecret,
+ int userId)
+ throws RemoteException {
+ mRecoverableKeyStoreManager.recoverySecretAvailable(recoverySecret, userId);
+ }
+
+ @Override
+ public byte[] startRecoverySession(@NonNull String sessionId,
+ @NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams,
+ @NonNull byte[] vaultChallenge, @NonNull List<KeyStoreRecoveryMetadata> secrets,
+ int userId) throws RemoteException {
+ return mRecoverableKeyStoreManager.startRecoverySession(sessionId, verifierPublicKey,
+ vaultParams, vaultChallenge, secrets, userId);
+ }
+
+ @Override
+ public void recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob,
+ @NonNull List<KeyEntryRecoveryData> applicationKeys, int userId)
+ throws RemoteException {
+ mRecoverableKeyStoreManager.recoverKeys(sessionId, recoveryKeyBlob, applicationKeys,
+ userId);
+ }
+
private static final String[] VALID_SETTINGS = new String[] {
LockPatternUtils.LOCKOUT_PERMANENT_KEY,
LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/AndroidKeyStoreFactory.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/AndroidKeyStoreFactory.java
new file mode 100644
index 0000000..9a4d051
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/AndroidKeyStoreFactory.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+import android.security.keystore.AndroidKeyStoreProvider;
+
+import java.security.KeyStoreException;
+import java.security.NoSuchProviderException;
+
+public interface AndroidKeyStoreFactory {
+ KeyStoreProxy getKeyStoreForUid(int uid) throws KeyStoreException, NoSuchProviderException;
+
+ class Impl implements AndroidKeyStoreFactory {
+ @Override
+ public KeyStoreProxy getKeyStoreForUid(int uid)
+ throws KeyStoreException, NoSuchProviderException {
+ return new KeyStoreProxyImpl(AndroidKeyStoreProvider.getKeyStoreForUid(uid));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/BadPlatformKeyException.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/BadPlatformKeyException.java
new file mode 100644
index 0000000..c7a98b6
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/BadPlatformKeyException.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+/**
+ * Error thrown when the {@link PlatformDecryptionKey} instance has a different generation ID from
+ * the {@link WrappedKey} instance.
+ *
+ * @hide
+ */
+public class BadPlatformKeyException extends Exception {
+
+ /**
+ * A new instance with {@code message}.
+ *
+ * @hide
+ */
+ public BadPlatformKeyException(String message) {
+ super(message);
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/InsecureUserException.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/InsecureUserException.java
new file mode 100644
index 0000000..5155a99
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/InsecureUserException.java
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+/**
+ * Error thrown initializing {@link PlatformKeyManager} if the user is not secure (i.e., has no
+ * lock screen set).
+ */
+public class InsecureUserException extends Exception {
+
+ /**
+ * A new instance with {@code message} error message.
+ */
+ public InsecureUserException(String message) {
+ super(message);
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxy.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxy.java
new file mode 100644
index 0000000..8103177
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxy.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+
+/**
+ * Proxies {@link java.security.KeyStore}. As all of its methods are final, it cannot otherwise be
+ * mocked for tests.
+ *
+ * @hide
+ */
+public interface KeyStoreProxy {
+
+ /** @see KeyStore#containsAlias(String) */
+ boolean containsAlias(String alias) throws KeyStoreException;
+
+ /** @see KeyStore#getKey(String, char[]) */
+ Key getKey(String alias, char[] password)
+ throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException;
+
+ /** @see KeyStore#setEntry(String, KeyStore.Entry, KeyStore.ProtectionParameter) */
+ void setEntry(String alias, KeyStore.Entry entry, KeyStore.ProtectionParameter protParam)
+ throws KeyStoreException;
+
+ /** @see KeyStore#deleteEntry(String) */
+ void deleteEntry(String alias) throws KeyStoreException;
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
new file mode 100644
index 0000000..59132da
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings.recoverablekeystore;
+
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+
+/**
+ * Implementation of {@link KeyStoreProxy} that delegates all method calls to the {@link KeyStore}.
+ */
+public class KeyStoreProxyImpl implements KeyStoreProxy {
+
+ private final KeyStore mKeyStore;
+
+ /**
+ * A new instance, delegating to {@code keyStore}.
+ */
+ public KeyStoreProxyImpl(KeyStore keyStore) {
+ mKeyStore = keyStore;
+ }
+
+ @Override
+ public boolean containsAlias(String alias) throws KeyStoreException {
+ return mKeyStore.containsAlias(alias);
+ }
+
+ @Override
+ public Key getKey(String alias, char[] password)
+ throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
+ return mKeyStore.getKey(alias, password);
+ }
+
+ @Override
+ public void setEntry(String alias, KeyStore.Entry entry, KeyStore.ProtectionParameter protParam)
+ throws KeyStoreException {
+ mKeyStore.setEntry(alias, entry, protParam);
+ }
+
+ @Override
+ public void deleteEntry(String alias) throws KeyStoreException {
+ mKeyStore.deleteEntry(alias);
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
new file mode 100644
index 0000000..37aeb3a
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
@@ -0,0 +1,261 @@
+/*
+ * 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;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.crypto.AEADBadTagException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+/**
+ * Utility functions for the flow where the RecoverableKeyStoreLoader syncs keys with remote
+ * storage.
+ *
+ * @hide
+ */
+public class KeySyncUtils {
+
+ private static final String RECOVERY_KEY_ALGORITHM = "AES";
+ private static final int RECOVERY_KEY_SIZE_BITS = 256;
+
+ private static final byte[] THM_ENCRYPTED_RECOVERY_KEY_HEADER =
+ "V1 THM_encrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
+ private static final byte[] LOCALLY_ENCRYPTED_RECOVERY_KEY_HEADER =
+ "V1 locally_encrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
+ private static final byte[] ENCRYPTED_APPLICATION_KEY_HEADER =
+ "V1 encrypted_application_key".getBytes(StandardCharsets.UTF_8);
+ private static final byte[] RECOVERY_CLAIM_HEADER =
+ "V1 KF_claim".getBytes(StandardCharsets.UTF_8);
+
+ private static final byte[] THM_KF_HASH_PREFIX = "THM_KF_hash".getBytes(StandardCharsets.UTF_8);
+
+ private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
+
+ /**
+ * Encrypts the recovery key using both the lock screen hash and the remote storage's public
+ * key.
+ *
+ * @param publicKey The public key of the remote storage.
+ * @param lockScreenHash The user's lock screen hash.
+ * @param vaultParams Additional parameters to send to the remote storage.
+ * @param recoveryKey The recovery key.
+ * @return The encrypted bytes.
+ * @throws NoSuchAlgorithmException if any SecureBox algorithm is unavailable.
+ * @throws InvalidKeyException if the public key or the lock screen could not be used to encrypt
+ * the data.
+ *
+ * @hide
+ */
+ public byte[] thmEncryptRecoveryKey(
+ PublicKey publicKey,
+ byte[] lockScreenHash,
+ byte[] vaultParams,
+ SecretKey recoveryKey
+ ) throws NoSuchAlgorithmException, InvalidKeyException {
+ byte[] encryptedRecoveryKey = locallyEncryptRecoveryKey(lockScreenHash, recoveryKey);
+ byte[] thmKfHash = calculateThmKfHash(lockScreenHash);
+ byte[] header = concat(THM_ENCRYPTED_RECOVERY_KEY_HEADER, vaultParams);
+ return SecureBox.encrypt(
+ /*theirPublicKey=*/ publicKey,
+ /*sharedSecret=*/ thmKfHash,
+ /*header=*/ header,
+ /*payload=*/ encryptedRecoveryKey);
+ }
+
+ /**
+ * Calculates the THM_KF hash of the lock screen hash.
+ *
+ * @param lockScreenHash The lock screen hash.
+ * @return The hash.
+ * @throws NoSuchAlgorithmException if SHA-256 is unavailable (should never happen).
+ *
+ * @hide
+ */
+ public static byte[] calculateThmKfHash(byte[] lockScreenHash)
+ throws NoSuchAlgorithmException {
+ MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
+ messageDigest.update(THM_KF_HASH_PREFIX);
+ messageDigest.update(lockScreenHash);
+ return messageDigest.digest();
+ }
+
+ /**
+ * Encrypts the recovery key using the lock screen hash.
+ *
+ * @param lockScreenHash The raw lock screen hash.
+ * @param recoveryKey The recovery key.
+ * @return The encrypted bytes.
+ * @throws NoSuchAlgorithmException if any SecureBox algorithm is unavailable.
+ * @throws InvalidKeyException if the hash cannot be used to encrypt for some reason.
+ */
+ private static byte[] locallyEncryptRecoveryKey(byte[] lockScreenHash, SecretKey recoveryKey)
+ throws NoSuchAlgorithmException, InvalidKeyException {
+ return SecureBox.encrypt(
+ /*theirPublicKey=*/ null,
+ /*sharedSecret=*/ lockScreenHash,
+ /*header=*/ LOCALLY_ENCRYPTED_RECOVERY_KEY_HEADER,
+ /*payload=*/ recoveryKey.getEncoded());
+ }
+
+ /**
+ * Returns a new random 256-bit AES recovery key.
+ *
+ * @hide
+ */
+ public static SecretKey generateRecoveryKey() throws NoSuchAlgorithmException {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(RECOVERY_KEY_ALGORITHM);
+ keyGenerator.init(RECOVERY_KEY_SIZE_BITS, new SecureRandom());
+ return keyGenerator.generateKey();
+ }
+
+ /**
+ * Encrypts all of the given keys with the recovery key, using SecureBox.
+ *
+ * @param recoveryKey The recovery key.
+ * @param keys The keys, indexed by their aliases.
+ * @return The encrypted key material, indexed by aliases.
+ * @throws NoSuchAlgorithmException if any of the SecureBox algorithms are unavailable.
+ * @throws InvalidKeyException if the recovery key is not appropriate for encrypting the keys.
+ *
+ * @hide
+ */
+ public static Map<String, byte[]> encryptKeysWithRecoveryKey(
+ SecretKey recoveryKey, Map<String, SecretKey> keys)
+ throws NoSuchAlgorithmException, InvalidKeyException {
+ HashMap<String, byte[]> encryptedKeys = new HashMap<>();
+ for (String alias : keys.keySet()) {
+ SecretKey key = keys.get(alias);
+ byte[] encryptedKey = SecureBox.encrypt(
+ /*theirPublicKey=*/ null,
+ /*sharedSecret=*/ recoveryKey.getEncoded(),
+ /*header=*/ ENCRYPTED_APPLICATION_KEY_HEADER,
+ /*payload=*/ key.getEncoded());
+ encryptedKeys.put(alias, encryptedKey);
+ }
+ return encryptedKeys;
+ }
+
+ /**
+ * Returns a random 16-byte key claimant.
+ *
+ * @hide
+ */
+ public static byte[] generateKeyClaimant() {
+ SecureRandom secureRandom = new SecureRandom();
+ byte[] key = new byte[KEY_CLAIMANT_LENGTH_BYTES];
+ secureRandom.nextBytes(key);
+ return key;
+ }
+
+ /**
+ * Encrypts a claim to recover a remote recovery key.
+ *
+ * @param publicKey The public key of the remote server.
+ * @param vaultParams Associated vault parameters.
+ * @param challenge The challenge issued by the server.
+ * @param thmKfHash The THM hash of the lock screen.
+ * @param keyClaimant The random key claimant.
+ * @return The encrypted recovery claim, to be sent to the remote server.
+ * @throws NoSuchAlgorithmException if any SecureBox algorithm is not present.
+ * @throws InvalidKeyException if the {@code publicKey} could not be used to encrypt.
+ *
+ * @hide
+ */
+ public static byte[] encryptRecoveryClaim(
+ PublicKey publicKey,
+ byte[] vaultParams,
+ byte[] challenge,
+ byte[] thmKfHash,
+ byte[] keyClaimant) throws NoSuchAlgorithmException, InvalidKeyException {
+ return SecureBox.encrypt(
+ publicKey,
+ /*sharedSecret=*/ null,
+ /*header=*/ concat(RECOVERY_CLAIM_HEADER, vaultParams, challenge),
+ /*payload=*/ concat(thmKfHash, keyClaimant));
+ }
+
+ /**
+ * Decrypts a recovery key, after having retrieved it from a remote server.
+ *
+ * @param lskfHash The lock screen hash associated with the key.
+ * @param encryptedRecoveryKey The encrypted key.
+ * @return The raw key material.
+ * @throws NoSuchAlgorithmException if any SecureBox algorithm is unavailable.
+ * @throws AEADBadTagException if the message has been tampered with or was encrypted with a
+ * different key.
+ */
+ public static byte[] decryptRecoveryKey(byte[] lskfHash, byte[] encryptedRecoveryKey)
+ throws NoSuchAlgorithmException, InvalidKeyException, AEADBadTagException {
+ return SecureBox.decrypt(
+ /*ourPrivateKey=*/ null,
+ /*sharedSecret=*/ lskfHash,
+ /*header=*/ LOCALLY_ENCRYPTED_RECOVERY_KEY_HEADER,
+ /*encryptedPayload=*/ encryptedRecoveryKey);
+ }
+
+ /**
+ * Decrypts an application key, using the recovery key.
+ *
+ * @param recoveryKey The recovery key - used to wrap all application keys.
+ * @param encryptedApplicationKey The application key to unwrap.
+ * @return The raw key material of the application key.
+ * @throws NoSuchAlgorithmException if any SecureBox algorithm is unavailable.
+ * @throws AEADBadTagException if the message has been tampered with or was encrypted with a
+ * different key.
+ */
+ public static byte[] decryptApplicationKey(byte[] recoveryKey, byte[] encryptedApplicationKey)
+ throws NoSuchAlgorithmException, InvalidKeyException, AEADBadTagException {
+ return SecureBox.decrypt(
+ /*ourPrivateKey=*/ null,
+ /*sharedSecret=*/ recoveryKey,
+ /*header=*/ ENCRYPTED_APPLICATION_KEY_HEADER,
+ /*encryptedPayload=*/ encryptedApplicationKey);
+ }
+
+ /**
+ * Returns the concatenation of all the given {@code arrays}.
+ */
+ @VisibleForTesting
+ static byte[] concat(byte[]... arrays) {
+ int length = 0;
+ for (byte[] array : arrays) {
+ length += array.length;
+ }
+
+ byte[] concatenated = new byte[length];
+ int pos = 0;
+ for (byte[] array : arrays) {
+ System.arraycopy(array, /*srcPos=*/ 0, concatenated, pos, array.length);
+ pos += array.length;
+ }
+
+ return concatenated;
+ }
+
+ // Statics only
+ private KeySyncUtils() {}
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformDecryptionKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformDecryptionKey.java
new file mode 100644
index 0000000..35571f1
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformDecryptionKey.java
@@ -0,0 +1,65 @@
+/*
+ * 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;
+
+import android.security.keystore.AndroidKeyStoreSecretKey;
+
+/**
+ * Used to unwrap recoverable keys before syncing them with remote storage.
+ *
+ * <p>This is a private key stored in AndroidKeyStore. Has an associated generation ID, which is
+ * stored with wrapped keys, allowing us to ensure the wrapped key has the same version as the
+ * platform key.
+ *
+ * @hide
+ */
+public class PlatformDecryptionKey {
+
+ private final int mGenerationId;
+ private final AndroidKeyStoreSecretKey mKey;
+
+ /**
+ * A new instance.
+ *
+ * @param generationId The generation ID of the platform key.
+ * @param key The key handle in AndroidKeyStore.
+ *
+ * @hide
+ */
+ public PlatformDecryptionKey(int generationId, AndroidKeyStoreSecretKey key) {
+ mGenerationId = generationId;
+ mKey = key;
+ }
+
+ /**
+ * Returns the generation ID.
+ *
+ * @hide
+ */
+ public int getGenerationId() {
+ return mGenerationId;
+ }
+
+ /**
+ * Returns the actual key, which can be used to decrypt.
+ *
+ * @hide
+ */
+ public AndroidKeyStoreSecretKey getKey() {
+ return mKey;
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java
new file mode 100644
index 0000000..38f5b45
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformEncryptionKey.java
@@ -0,0 +1,62 @@
+/*
+ * 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;
+
+import android.security.keystore.AndroidKeyStoreSecretKey;
+
+/**
+ * Private key stored in AndroidKeyStore. Used to wrap recoverable keys before writing them to disk.
+ *
+ * <p>Identified by a generation ID, which increments whenever a new platform key is generated. A
+ * new key must be generated whenever the user disables their lock screen, as the decryption key is
+ * tied to lock-screen authentication.
+ *
+ * <p>One current platform key exists per profile on the device. (As each must be tied to a
+ * different user's lock screen.)
+ *
+ * @hide
+ */
+public class PlatformEncryptionKey {
+
+ private final int mGenerationId;
+ private final AndroidKeyStoreSecretKey mKey;
+
+ /**
+ * A new instance.
+ *
+ * @param generationId The generation ID of the key.
+ * @param key The secret key handle. Can be used to encrypt WITHOUT requiring screen unlock.
+ */
+ public PlatformEncryptionKey(int generationId, AndroidKeyStoreSecretKey key) {
+ mGenerationId = generationId;
+ mKey = key;
+ }
+
+ /**
+ * Returns the generation ID of the key.
+ */
+ public int getGenerationId() {
+ return mGenerationId;
+ }
+
+ /**
+ * Returns the actual key, which can only be used to encrypt.
+ */
+ public AndroidKeyStoreSecretKey getKey() {
+ return mKey;
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
new file mode 100644
index 0000000..24f3f65
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -0,0 +1,335 @@
+/*
+ * 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;
+
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.security.keystore.AndroidKeyStoreSecretKey;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
+
+import java.io.IOException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.Locale;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.security.auth.DestroyFailedException;
+
+/**
+ * Manages creating and checking the validity of the platform key.
+ *
+ * <p>The platform key is used to wrap the material of recoverable keys before persisting them to
+ * disk. It is also used to decrypt the same keys on a screen unlock, before re-wrapping them with
+ * a recovery key and syncing them with remote storage.
+ *
+ * <p>Each platform key has two entries in AndroidKeyStore:
+ *
+ * <ul>
+ * <li>Encrypt entry - this entry enables the root user to at any time encrypt.
+ * <li>Decrypt entry - this entry enables the root user to decrypt only after recent user
+ * authentication, i.e., within 15 seconds after a screen unlock.
+ * </ul>
+ *
+ * <p>Both entries are enabled only for AES/GCM/NoPadding Cipher algorithm.
+ *
+ * @hide
+ */
+public class PlatformKeyManager {
+ private static final String TAG = "PlatformKeyManager";
+
+ private static final String KEY_ALGORITHM = "AES";
+ private static final int KEY_SIZE_BITS = 256;
+ private static final String KEY_ALIAS_PREFIX =
+ "com.android.server.locksettings.recoverablekeystore/platform/";
+ private static final String ENCRYPT_KEY_ALIAS_SUFFIX = "encrypt";
+ private static final String DECRYPT_KEY_ALIAS_SUFFIX = "decrypt";
+ private static final int USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS = 15;
+
+ private final Context mContext;
+ private final KeyStoreProxy mKeyStore;
+ private final RecoverableKeyStoreDb mDatabase;
+ private final int mUserId;
+
+ private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
+
+ /**
+ * A new instance operating on behalf of {@code userId}, storing its prefs in the location
+ * defined by {@code context}.
+ *
+ * @param context This should be the context of the RecoverableKeyStoreLoader service.
+ * @param userId The ID of the user to whose lock screen the platform key must be bound.
+ * @throws KeyStoreException if failed to initialize AndroidKeyStore.
+ * @throws NoSuchAlgorithmException if AES is unavailable - should never happen.
+ * @throws InsecureUserException if the user does not have a lock screen set.
+ * @throws SecurityException if the caller does not have permission to write to /data/system.
+ *
+ * @hide
+ */
+ public static PlatformKeyManager getInstance(Context context, RecoverableKeyStoreDb database, int userId)
+ throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException {
+ context = context.getApplicationContext();
+ PlatformKeyManager keyManager = new PlatformKeyManager(
+ userId,
+ context,
+ new KeyStoreProxyImpl(getAndLoadAndroidKeyStore()),
+ database);
+ keyManager.init();
+ return keyManager;
+ }
+
+ @VisibleForTesting
+ PlatformKeyManager(
+ int userId,
+ Context context,
+ KeyStoreProxy keyStore,
+ RecoverableKeyStoreDb database) {
+ mUserId = userId;
+ mKeyStore = keyStore;
+ mContext = context;
+ mDatabase = database;
+ }
+
+ /**
+ * Returns the current generation ID of the platform key. This increments whenever a platform
+ * key has to be replaced. (e.g., because the user has removed and then re-added their lock
+ * screen).
+ *
+ * @hide
+ */
+ public int getGenerationId() {
+ int generationId = mDatabase.getPlatformKeyGenerationId(mUserId);
+ if (generationId == -1) {
+ return 1;
+ }
+ return generationId;
+ }
+
+ /**
+ * Returns {@code true} if the platform key is available. A platform key won't be available if
+ * the user has not set up a lock screen.
+ *
+ * @hide
+ */
+ public boolean isAvailable() {
+ return mContext.getSystemService(KeyguardManager.class).isDeviceSecure(mUserId);
+ }
+
+ /**
+ * Generates a new key and increments the generation ID. Should be invoked if the platform key
+ * is corrupted and needs to be rotated.
+ *
+ * @throws NoSuchAlgorithmException if AES is unavailable - should never happen.
+ * @throws KeyStoreException if there is an error in AndroidKeyStore.
+ *
+ * @hide
+ */
+ public void regenerate() throws NoSuchAlgorithmException, KeyStoreException {
+ int nextId = getGenerationId() + 1;
+ generateAndLoadKey(nextId);
+ setGenerationId(nextId);
+ }
+
+ /**
+ * Returns the platform key used for encryption.
+ *
+ * @throws KeyStoreException if there was an AndroidKeyStore error.
+ * @throws UnrecoverableKeyException if the key could not be recovered.
+ * @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
+ *
+ * @hide
+ */
+ public PlatformEncryptionKey getEncryptKey()
+ throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException {
+ int generationId = getGenerationId();
+ AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
+ getEncryptAlias(generationId), /*password=*/ null);
+ return new PlatformEncryptionKey(generationId, key);
+ }
+
+ /**
+ * Returns the platform key used for decryption. Only works after a recent screen unlock.
+ *
+ * @throws KeyStoreException if there was an AndroidKeyStore error.
+ * @throws UnrecoverableKeyException if the key could not be recovered.
+ * @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
+ *
+ * @hide
+ */
+ public PlatformDecryptionKey getDecryptKey()
+ throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException {
+ int generationId = getGenerationId();
+ AndroidKeyStoreSecretKey key = (AndroidKeyStoreSecretKey) mKeyStore.getKey(
+ getDecryptAlias(generationId), /*password=*/ null);
+ return new PlatformDecryptionKey(generationId, key);
+ }
+
+ /**
+ * Initializes the class. If there is no current platform key, and the user has a lock screen
+ * set, will create the platform key and set the generation ID.
+ *
+ * @throws KeyStoreException if there was an error in AndroidKeyStore.
+ * @throws NoSuchAlgorithmException if AES is unavailable - should never happen.
+ *
+ * @hide
+ */
+ public void init() throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException {
+ if (!isAvailable()) {
+ throw new InsecureUserException(String.format(
+ Locale.US, "%d does not have a lock screen set.", mUserId));
+ }
+
+ int generationId = getGenerationId();
+ if (isKeyLoaded(generationId)) {
+ Log.i(TAG, String.format(
+ Locale.US, "Platform key generation %d exists already.", generationId));
+ return;
+ }
+ if (generationId == 1) {
+ Log.i(TAG, "Generating initial platform ID.");
+ } else {
+ Log.w(TAG, String.format(Locale.US, "Platform generation ID was %d but no "
+ + "entry was present in AndroidKeyStore. Generating fresh key.", generationId));
+ }
+
+ generateAndLoadKey(generationId);
+ }
+
+ /**
+ * Returns the alias of the encryption key with the specific {@code generationId} in the
+ * AndroidKeyStore.
+ *
+ * <p>These IDs look as follows:
+ * {@code com.security.recoverablekeystore/platform/<user id>/<generation id>/encrypt}
+ *
+ * @param generationId The generation ID.
+ * @return The alias.
+ */
+ private String getEncryptAlias(int generationId) {
+ return KEY_ALIAS_PREFIX + mUserId + "/" + generationId + "/" + ENCRYPT_KEY_ALIAS_SUFFIX;
+ }
+
+ /**
+ * Returns the alias of the decryption key with the specific {@code generationId} in the
+ * AndroidKeyStore.
+ *
+ * <p>These IDs look as follows:
+ * {@code com.security.recoverablekeystore/platform/<user id>/<generation id>/decrypt}
+ *
+ * @param generationId The generation ID.
+ * @return The alias.
+ */
+ private String getDecryptAlias(int generationId) {
+ return KEY_ALIAS_PREFIX + mUserId + "/" + generationId + "/" + DECRYPT_KEY_ALIAS_SUFFIX;
+ }
+
+ /**
+ * Sets the current generation ID to {@code generationId}.
+ */
+ private void setGenerationId(int generationId) {
+ mDatabase.setPlatformKeyGenerationId(mUserId, generationId);
+ }
+
+ /**
+ * Returns {@code true} if a key has been loaded with the given {@code generationId} into
+ * AndroidKeyStore.
+ *
+ * @throws KeyStoreException if there was an error checking AndroidKeyStore.
+ */
+ private boolean isKeyLoaded(int generationId) throws KeyStoreException {
+ return mKeyStore.containsAlias(getEncryptAlias(generationId))
+ && mKeyStore.containsAlias(getDecryptAlias(generationId));
+ }
+
+ /**
+ * Generates a new 256-bit AES key, and loads it into AndroidKeyStore with the given
+ * {@code generationId} determining its aliases.
+ *
+ * @throws NoSuchAlgorithmException if AES is unavailable. This should never happen, as it is
+ * available since API version 1.
+ * @throws KeyStoreException if there was an issue loading the keys into AndroidKeyStore.
+ */
+ private void generateAndLoadKey(int generationId)
+ throws NoSuchAlgorithmException, KeyStoreException {
+ String encryptAlias = getEncryptAlias(generationId);
+ String decryptAlias = getDecryptAlias(generationId);
+ SecretKey secretKey = generateAesKey();
+
+ mKeyStore.setEntry(
+ encryptAlias,
+ new KeyStore.SecretKeyEntry(secretKey),
+ new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+ mKeyStore.setEntry(
+ decryptAlias,
+ new KeyStore.SecretKeyEntry(secretKey),
+ new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
+ .setUserAuthenticationRequired(true)
+ .setUserAuthenticationValidityDurationSeconds(
+ USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setBoundToSpecificSecureUserId(mUserId)
+ .build());
+
+ try {
+ secretKey.destroy();
+ } catch (DestroyFailedException e) {
+ Log.w(TAG, "Failed to destroy in-memory platform key.", e);
+ }
+ }
+
+ /**
+ * Generates a new 256-bit AES key, in software.
+ *
+ * @return The software-generated AES key.
+ * @throws NoSuchAlgorithmException if AES key generation is not available. This should never
+ * happen, as AES has been supported since API level 1.
+ */
+ private static SecretKey generateAesKey() throws NoSuchAlgorithmException {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
+ keyGenerator.init(KEY_SIZE_BITS);
+ return keyGenerator.generateKey();
+ }
+
+ /**
+ * 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.
+ */
+ private 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/RecoverableKeyGenerator.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
new file mode 100644
index 0000000..d50a736
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java
@@ -0,0 +1,155 @@
+/*
+ * 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;
+
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.util.Log;
+
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
+
+import java.security.InvalidKeyException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.util.Locale;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.security.auth.DestroyFailedException;
+
+/**
+ * Generates keys and stores them both in AndroidKeyStore and on disk, in wrapped form.
+ *
+ * <p>Generates 256-bit AES keys, which can be used for encrypt / decrypt with AES/GCM/NoPadding.
+ * They are synced to disk wrapped by a platform key. This allows them to be exported to a remote
+ * service.
+ *
+ * @hide
+ */
+public class RecoverableKeyGenerator {
+ private static final String TAG = "RecoverableKeyGenerator";
+
+ private static final int RESULT_CANNOT_INSERT_ROW = -1;
+ private static final String KEY_GENERATOR_ALGORITHM = "AES";
+ private static final int KEY_SIZE_BITS = 256;
+
+ /**
+ * A new {@link RecoverableKeyGenerator} instance.
+ *
+ * @throws NoSuchAlgorithmException if "AES" key generation or "AES/GCM/NoPadding" cipher is
+ * unavailable. Should never happen.
+ *
+ * @hide
+ */
+ public static RecoverableKeyGenerator newInstance(RecoverableKeyStoreDb database)
+ throws NoSuchAlgorithmException {
+ // NB: This cannot use AndroidKeyStore as the provider, as we need access to the raw key
+ // material, so that it can be synced to disk in encrypted form.
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_GENERATOR_ALGORITHM);
+ return new RecoverableKeyGenerator(
+ keyGenerator, database, new AndroidKeyStoreFactory.Impl());
+ }
+
+ private final KeyGenerator mKeyGenerator;
+ private final RecoverableKeyStoreDb mDatabase;
+ private final AndroidKeyStoreFactory mAndroidKeyStoreFactory;
+
+ private RecoverableKeyGenerator(
+ KeyGenerator keyGenerator,
+ RecoverableKeyStoreDb recoverableKeyStoreDb,
+ AndroidKeyStoreFactory androidKeyStoreFactory) {
+ mKeyGenerator = keyGenerator;
+ mAndroidKeyStoreFactory = androidKeyStoreFactory;
+ mDatabase = recoverableKeyStoreDb;
+ }
+
+ /**
+ * Generates a 256-bit AES key with the given alias.
+ *
+ * <p>Stores in the AndroidKeyStore, as well as persisting in wrapped form to disk. It is
+ * persisted to disk so that it can be synced remotely, and then recovered on another device.
+ * The generated key allows encrypt/decrypt only using AES/GCM/NoPadding.
+ *
+ * @param platformKey The user's platform key, with which to wrap the generated key.
+ * @param uid The uid of the application that will own the key.
+ * @param alias The alias by which the key will be known in AndroidKeyStore.
+ * @throws RecoverableKeyStorageException if there is some error persisting the key either to
+ * the AndroidKeyStore or the database.
+ * @throws KeyStoreException if there is a KeyStore error wrapping the generated key.
+ * @throws InvalidKeyException if the platform key cannot be used to wrap keys.
+ *
+ * @hide
+ */
+ public void generateAndStoreKey(PlatformEncryptionKey platformKey, int uid, String alias)
+ throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException {
+ mKeyGenerator.init(KEY_SIZE_BITS);
+ SecretKey key = mKeyGenerator.generateKey();
+
+ KeyStoreProxy keyStore;
+
+ try {
+ keyStore = mAndroidKeyStoreFactory.getKeyStoreForUid(uid);
+ } catch (NoSuchProviderException e) {
+ throw new RecoverableKeyStorageException(
+ "Impossible: AndroidKeyStore provider did not exist", e);
+ } catch (KeyStoreException e) {
+ throw new RecoverableKeyStorageException(
+ "Could not load AndroidKeyStore for " + uid, e);
+ }
+
+ try {
+ keyStore.setEntry(
+ alias,
+ new KeyStore.SecretKeyEntry(key),
+ 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 RecoverableKeyStorageException(
+ "Failed to load (%d, %s) into AndroidKeyStore", e);
+ }
+
+ WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key);
+ try {
+ // Keep raw key material in memory for minimum possible time.
+ key.destroy();
+ } catch (DestroyFailedException e) {
+ Log.w(TAG, "Could not destroy SecretKey.");
+ }
+ long result = mDatabase.insertKey(uid, alias, wrappedKey);
+
+ if (result == RESULT_CANNOT_INSERT_ROW) {
+ // Attempt to clean up
+ try {
+ keyStore.deleteEntry(alias);
+ } catch (KeyStoreException e) {
+ Log.e(TAG, String.format(Locale.US,
+ "Could not delete recoverable key (%d, %s) from "
+ + "AndroidKeyStore after error writing to database.", uid, alias),
+ e);
+ }
+
+ throw new RecoverableKeyStorageException(
+ String.format(
+ Locale.US, "Failed writing (%d, %s) to database.", uid, alias));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageException.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageException.java
new file mode 100644
index 0000000..f9d28f1
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageException.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+/**
+ * Error thrown when there was a problem writing or reading recoverable key information to or from
+ * storage.
+ *
+ * <p>Storage is typically
+ * {@link com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb} or
+ * AndroidKeyStore.
+ */
+public class RecoverableKeyStorageException extends Exception {
+ public RecoverableKeyStorageException(String message) {
+ super(message);
+ }
+
+ public RecoverableKeyStorageException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
new file mode 100644
index 0000000..e459f28
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -0,0 +1,211 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.os.UserHandle;
+
+import android.security.recoverablekeystore.KeyEntryRecoveryData;
+import android.security.recoverablekeystore.KeyStoreRecoveryData;
+import android.security.recoverablekeystore.KeyStoreRecoveryMetadata;
+import android.security.recoverablekeystore.RecoverableKeyStoreLoader;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class with {@link RecoverableKeyStoreLoader} API implementation and internal methods to interact
+ * with {@code LockSettingsService}.
+ *
+ * @hide
+ */
+public class RecoverableKeyStoreManager {
+ private static final String TAG = "RecoverableKeyStoreManager";
+
+ private static RecoverableKeyStoreManager mInstance;
+ private Context mContext;
+
+ /**
+ * Returns a new or existing instance.
+ *
+ * @hide
+ */
+ public static synchronized RecoverableKeyStoreManager getInstance(Context mContext) {
+ if (mInstance == null) {
+ mInstance = new RecoverableKeyStoreManager(mContext);
+ }
+ return mInstance;
+ }
+
+ @VisibleForTesting
+ RecoverableKeyStoreManager(Context context) {
+ mContext = context;
+ }
+
+ public int initRecoveryService(
+ @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList, int userId)
+ throws RemoteException {
+ checkRecoverKeyStorePermission();
+ // TODO open /system/etc/security/... cert file
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Gets all data necessary to recover application keys on new device.
+ *
+ * @return recovery data
+ * @hide
+ */
+ public KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account, int userId)
+ throws RemoteException {
+ checkRecoverKeyStorePermission();
+ final int callingUid = Binder.getCallingUid(); // Recovery agent uid.
+ final int callingUserId = UserHandle.getCallingUserId();
+ final long callingIdentiy = Binder.clearCallingIdentity();
+ try {
+ // TODO: Return the latest snapshot for the calling recovery agent.
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentiy);
+ }
+
+ // KeyStoreRecoveryData without application keys and empty recovery blob.
+ KeyStoreRecoveryData recoveryData =
+ new KeyStoreRecoveryData(
+ /*snapshotVersion=*/ 1,
+ new ArrayList<KeyStoreRecoveryMetadata>(),
+ new ArrayList<KeyEntryRecoveryData>(),
+ /*encryptedRecoveryKeyBlob=*/ new byte[] {});
+ throw new ServiceSpecificException(
+ RecoverableKeyStoreLoader.UNINITIALIZED_RECOVERY_PUBLIC_KEY);
+ }
+
+ public void setServerParameters(long serverParameters, int userId) throws RemoteException {
+ checkRecoverKeyStorePermission();
+ throw new UnsupportedOperationException();
+ }
+
+ public void setRecoveryStatus(
+ @NonNull String packageName, @Nullable String[] aliases, int status, int userId)
+ throws RemoteException {
+ checkRecoverKeyStorePermission();
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Sets recovery secrets list used by all recovery agents for given {@code userId}
+ *
+ * @hide
+ */
+ public void setRecoverySecretTypes(
+ @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] secretTypes, int userId)
+ throws RemoteException {
+ checkRecoverKeyStorePermission();
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Gets secret types necessary to create Recovery Data.
+ *
+ * @return secret types
+ * @hide
+ */
+ public int[] getRecoverySecretTypes(int userId) throws RemoteException {
+ checkRecoverKeyStorePermission();
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Gets secret types RecoverableKeyStoreLoaders is waiting for to create new Recovery Data.
+ *
+ * @return secret types
+ * @hide
+ */
+ public int[] getPendingRecoverySecretTypes(int userId) throws RemoteException {
+ checkRecoverKeyStorePermission();
+ throw new UnsupportedOperationException();
+ }
+
+ public void recoverySecretAvailable(
+ @NonNull KeyStoreRecoveryMetadata recoverySecret, int userId) throws RemoteException {
+ final int callingUid = Binder.getCallingUid(); // Recovery agent uid.
+ if (recoverySecret.getLockScreenUiFormat() == KeyStoreRecoveryMetadata.TYPE_LOCKSCREEN) {
+ throw new SecurityException(
+ "Caller " + callingUid + "is not allowed to set lock screen secret");
+ }
+ checkRecoverKeyStorePermission();
+ // TODO: add hook from LockSettingsService to set lock screen secret.
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Initializes recovery session.
+ *
+ * @return recovery claim
+ * @hide
+ */
+ public byte[] startRecoverySession(
+ @NonNull String sessionId,
+ @NonNull byte[] verifierPublicKey,
+ @NonNull byte[] vaultParams,
+ @NonNull byte[] vaultChallenge,
+ @NonNull List<KeyStoreRecoveryMetadata> secrets,
+ int userId)
+ throws RemoteException {
+ checkRecoverKeyStorePermission();
+ throw new UnsupportedOperationException();
+ }
+
+ public void recoverKeys(
+ @NonNull String sessionId,
+ @NonNull byte[] recoveryKeyBlob,
+ @NonNull List<KeyEntryRecoveryData> applicationKeys,
+ int userId)
+ throws RemoteException {
+ checkRecoverKeyStorePermission();
+ throw new UnsupportedOperationException();
+ }
+
+ /** This function can only be used inside LockSettingsService. */
+ public void lockScreenSecretAvailable(
+ @KeyStoreRecoveryMetadata.LockScreenUiFormat int type,
+ String unencryptedPassword,
+ int userId) {
+ // TODO: compute SHA256 or Argon2id depending on secret type.
+ throw new UnsupportedOperationException();
+ }
+
+ /** This function can only be used inside LockSettingsService. */
+ public void lockScreenSecretChanged(
+ @KeyStoreRecoveryMetadata.LockScreenUiFormat int type,
+ @Nullable String unencryptedPassword,
+ int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+ private void checkRecoverKeyStorePermission() {
+ mContext.enforceCallingOrSelfPermission(
+ RecoverableKeyStoreLoader.PERMISSION_RECOVER_KEYSTORE,
+ "Caller " + Binder.getCallingUid() + " doesn't have RecoverKeyStore permission.");
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java
new file mode 100644
index 0000000..d8a2d31
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java
@@ -0,0 +1,462 @@
+/*
+ * 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;
+
+import android.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
+import java.math.BigInteger;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.EllipticCurve;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Arrays;
+import javax.crypto.AEADBadTagException;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyAgreement;
+import javax.crypto.Mac;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Implementation of the SecureBox v2 crypto functions.
+ *
+ * <p>Securebox v2 provides a simple interface to perform encryptions by using any of the following
+ * credential types:
+ *
+ * <ul>
+ * <li>A public key owned by the recipient,
+ * <li>A secret shared between the sender and the recipient, or
+ * <li>Both a recipient's public key and a shared secret.
+ * </ul>
+ *
+ * @hide
+ */
+public class SecureBox {
+
+ private static final byte[] VERSION = new byte[] {(byte) 0x02, 0}; // LITTLE_ENDIAN_TWO_BYTES(2)
+ private static final byte[] HKDF_SALT =
+ concat("SECUREBOX".getBytes(StandardCharsets.UTF_8), VERSION);
+ private static final byte[] HKDF_INFO_WITH_PUBLIC_KEY =
+ "P256 HKDF-SHA-256 AES-128-GCM".getBytes(StandardCharsets.UTF_8);
+ private static final byte[] HKDF_INFO_WITHOUT_PUBLIC_KEY =
+ "SHARED HKDF-SHA-256 AES-128-GCM".getBytes(StandardCharsets.UTF_8);
+ private static final byte[] CONSTANT_01 = {(byte) 0x01};
+ private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+ private static final byte EC_PUBLIC_KEY_PREFIX = (byte) 0x04;
+
+ private static final String CIPHER_ALG = "AES";
+ private static final String EC_ALG = "EC";
+ private static final String EC_P256_COMMON_NAME = "secp256r1";
+ private static final String EC_P256_OPENSSL_NAME = "prime256v1";
+ private static final String ENC_ALG = "AES/GCM/NoPadding";
+ private static final String KA_ALG = "ECDH";
+ private static final String MAC_ALG = "HmacSHA256";
+
+ private static final int EC_COORDINATE_LEN_BYTES = 32;
+ private static final int EC_PUBLIC_KEY_LEN_BYTES = 2 * EC_COORDINATE_LEN_BYTES + 1;
+ private static final int GCM_NONCE_LEN_BYTES = 12;
+ private static final int GCM_KEY_LEN_BYTES = 16;
+ private static final int GCM_TAG_LEN_BYTES = 16;
+
+ private static final BigInteger BIG_INT_02 = BigInteger.valueOf(2);
+
+ private enum AesGcmOperation {
+ ENCRYPT,
+ DECRYPT
+ }
+
+ // Parameters for the NIST P-256 curve y^2 = x^3 + ax + b (mod p)
+ private static final BigInteger EC_PARAM_P =
+ new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16);
+ private static final BigInteger EC_PARAM_A = EC_PARAM_P.subtract(new BigInteger("3"));
+ private static final BigInteger EC_PARAM_B =
+ new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16);
+
+ @VisibleForTesting static final ECParameterSpec EC_PARAM_SPEC;
+
+ static {
+ EllipticCurve curveSpec =
+ new EllipticCurve(new ECFieldFp(EC_PARAM_P), EC_PARAM_A, EC_PARAM_B);
+ ECPoint generator =
+ new ECPoint(
+ new BigInteger(
+ "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
+ 16),
+ new BigInteger(
+ "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5",
+ 16));
+ BigInteger generatorOrder =
+ new BigInteger(
+ "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16);
+ EC_PARAM_SPEC = new ECParameterSpec(curveSpec, generator, generatorOrder, /* cofactor */ 1);
+ }
+
+ private SecureBox() {}
+
+ /**
+ * Randomly generates a public-key pair that can be used for the functions {@link #encrypt} and
+ * {@link #decrypt}.
+ *
+ * @return the randomly generated public-key pair
+ * @throws NoSuchAlgorithmException if the underlying crypto algorithm is not supported
+ * @hide
+ */
+ public static KeyPair genKeyPair() throws NoSuchAlgorithmException {
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(EC_ALG);
+ try {
+ // Try using the OpenSSL provider first
+ keyPairGenerator.initialize(new ECGenParameterSpec(EC_P256_OPENSSL_NAME));
+ return keyPairGenerator.generateKeyPair();
+ } catch (InvalidAlgorithmParameterException ex) {
+ // Try another name for NIST P-256
+ }
+ try {
+ keyPairGenerator.initialize(new ECGenParameterSpec(EC_P256_COMMON_NAME));
+ return keyPairGenerator.generateKeyPair();
+ } catch (InvalidAlgorithmParameterException ex) {
+ throw new NoSuchAlgorithmException("Unable to find the NIST P-256 curve", ex);
+ }
+ }
+
+ /**
+ * Encrypts {@code payload} by using {@code theirPublicKey} and/or {@code sharedSecret}. At
+ * least one of {@code theirPublicKey} and {@code sharedSecret} must be non-null, and an empty
+ * {@code sharedSecret} is equivalent to null.
+ *
+ * <p>Note that {@code header} will be authenticated (but not encrypted) together with {@code
+ * payload}, and the same {@code header} has to be provided for {@link #decrypt}.
+ *
+ * @param theirPublicKey the recipient's public key, or null if the payload is to be encrypted
+ * only with the shared secret
+ * @param sharedSecret the secret shared between the sender and the recipient, or null if the
+ * payload is to be encrypted only with the recipient's public key
+ * @param header the data that will be authenticated with {@code payload} but not encrypted, or
+ * null if the data is empty
+ * @param payload the data to be encrypted, or null if the data is empty
+ * @return the encrypted payload
+ * @throws NoSuchAlgorithmException if any underlying crypto algorithm is not supported
+ * @throws InvalidKeyException if the provided key is invalid for underlying crypto algorithms
+ * @hide
+ */
+ public static byte[] encrypt(
+ @Nullable PublicKey theirPublicKey,
+ @Nullable byte[] sharedSecret,
+ @Nullable byte[] header,
+ @Nullable byte[] payload)
+ throws NoSuchAlgorithmException, InvalidKeyException {
+ sharedSecret = emptyByteArrayIfNull(sharedSecret);
+ if (theirPublicKey == null && sharedSecret.length == 0) {
+ throw new IllegalArgumentException("Both the public key and shared secret are empty");
+ }
+ header = emptyByteArrayIfNull(header);
+ payload = emptyByteArrayIfNull(payload);
+
+ KeyPair senderKeyPair;
+ byte[] dhSecret;
+ byte[] hkdfInfo;
+ if (theirPublicKey == null) {
+ senderKeyPair = null;
+ dhSecret = EMPTY_BYTE_ARRAY;
+ hkdfInfo = HKDF_INFO_WITHOUT_PUBLIC_KEY;
+ } else {
+ senderKeyPair = genKeyPair();
+ dhSecret = dhComputeSecret(senderKeyPair.getPrivate(), theirPublicKey);
+ hkdfInfo = HKDF_INFO_WITH_PUBLIC_KEY;
+ }
+
+ byte[] randNonce = genRandomNonce();
+ byte[] keyingMaterial = concat(dhSecret, sharedSecret);
+ SecretKey encryptionKey = hkdfDeriveKey(keyingMaterial, HKDF_SALT, hkdfInfo);
+ byte[] ciphertext = aesGcmEncrypt(encryptionKey, randNonce, payload, header);
+ if (senderKeyPair == null) {
+ return concat(VERSION, randNonce, ciphertext);
+ } else {
+ return concat(
+ VERSION, encodePublicKey(senderKeyPair.getPublic()), randNonce, ciphertext);
+ }
+ }
+
+ /**
+ * Decrypts {@code encryptedPayload} by using {@code ourPrivateKey} and/or {@code sharedSecret}.
+ * At least one of {@code ourPrivateKey} and {@code sharedSecret} must be non-null, and an empty
+ * {@code sharedSecret} is equivalent to null.
+ *
+ * <p>Note that {@code header} should be the same data used for {@link #encrypt}, which is
+ * authenticated (but not encrypted) together with {@code payload}; otherwise, an {@code
+ * AEADBadTagException} will be thrown.
+ *
+ * @param ourPrivateKey the recipient's private key, or null if the payload was encrypted only
+ * with the shared secret
+ * @param sharedSecret the secret shared between the sender and the recipient, or null if the
+ * payload was encrypted only with the recipient's public key
+ * @param header the data that was authenticated with the original payload but not encrypted, or
+ * null if the data is empty
+ * @param encryptedPayload the data to be decrypted
+ * @return the original payload that was encrypted
+ * @throws NoSuchAlgorithmException if any underlying crypto algorithm is not supported
+ * @throws InvalidKeyException if the provided key is invalid for underlying crypto algorithms
+ * @throws AEADBadTagException if the authentication tag contained in {@code encryptedPayload}
+ * cannot be validated
+ * @hide
+ */
+ public static byte[] decrypt(
+ @Nullable PrivateKey ourPrivateKey,
+ @Nullable byte[] sharedSecret,
+ @Nullable byte[] header,
+ byte[] encryptedPayload)
+ throws NoSuchAlgorithmException, InvalidKeyException, AEADBadTagException {
+ sharedSecret = emptyByteArrayIfNull(sharedSecret);
+ if (ourPrivateKey == null && sharedSecret.length == 0) {
+ throw new IllegalArgumentException("Both the private key and shared secret are empty");
+ }
+ header = emptyByteArrayIfNull(header);
+ encryptedPayload = emptyByteArrayIfNull(encryptedPayload);
+
+ ByteBuffer ciphertextBuffer = ByteBuffer.wrap(encryptedPayload);
+ byte[] version = readEncryptedPayload(ciphertextBuffer, VERSION.length);
+ if (!Arrays.equals(version, VERSION)) {
+ throw new IllegalArgumentException("The payload was not encrypted by SecureBox v2");
+ }
+
+ byte[] senderPublicKeyBytes;
+ byte[] dhSecret;
+ byte[] hkdfInfo;
+ if (ourPrivateKey == null) {
+ dhSecret = EMPTY_BYTE_ARRAY;
+ hkdfInfo = HKDF_INFO_WITHOUT_PUBLIC_KEY;
+ } else {
+ senderPublicKeyBytes = readEncryptedPayload(ciphertextBuffer, EC_PUBLIC_KEY_LEN_BYTES);
+ dhSecret = dhComputeSecret(ourPrivateKey, decodePublicKey(senderPublicKeyBytes));
+ hkdfInfo = HKDF_INFO_WITH_PUBLIC_KEY;
+ }
+
+ byte[] randNonce = readEncryptedPayload(ciphertextBuffer, GCM_NONCE_LEN_BYTES);
+ byte[] ciphertext = readEncryptedPayload(ciphertextBuffer, ciphertextBuffer.remaining());
+ byte[] keyingMaterial = concat(dhSecret, sharedSecret);
+ SecretKey decryptionKey = hkdfDeriveKey(keyingMaterial, HKDF_SALT, hkdfInfo);
+ return aesGcmDecrypt(decryptionKey, randNonce, ciphertext, header);
+ }
+
+ private static byte[] readEncryptedPayload(ByteBuffer buffer, int length) {
+ byte[] output = new byte[length];
+ try {
+ buffer.get(output);
+ } catch (BufferUnderflowException ex) {
+ throw new IllegalArgumentException("The encrypted payload is too short");
+ }
+ return output;
+ }
+
+ private static byte[] dhComputeSecret(PrivateKey ourPrivateKey, PublicKey theirPublicKey)
+ throws NoSuchAlgorithmException, InvalidKeyException {
+ KeyAgreement agreement = KeyAgreement.getInstance(KA_ALG);
+ try {
+ agreement.init(ourPrivateKey);
+ } catch (RuntimeException ex) {
+ // Rethrow the RuntimeException as InvalidKeyException
+ throw new InvalidKeyException(ex);
+ }
+ agreement.doPhase(theirPublicKey, /*lastPhase=*/ true);
+ return agreement.generateSecret();
+ }
+
+ /** Derives a 128-bit AES key. */
+ private static SecretKey hkdfDeriveKey(byte[] secret, byte[] salt, byte[] info)
+ throws NoSuchAlgorithmException {
+ Mac mac = Mac.getInstance(MAC_ALG);
+ try {
+ mac.init(new SecretKeySpec(salt, MAC_ALG));
+ } catch (InvalidKeyException ex) {
+ // This should never happen
+ throw new RuntimeException(ex);
+ }
+ byte[] pseudorandomKey = mac.doFinal(secret);
+
+ try {
+ mac.init(new SecretKeySpec(pseudorandomKey, MAC_ALG));
+ } catch (InvalidKeyException ex) {
+ // This should never happen
+ throw new RuntimeException(ex);
+ }
+ mac.update(info);
+ // Hashing just one block will yield 256 bits, which is enough to construct the AES key
+ byte[] hkdfOutput = mac.doFinal(CONSTANT_01);
+
+ return new SecretKeySpec(Arrays.copyOf(hkdfOutput, GCM_KEY_LEN_BYTES), CIPHER_ALG);
+ }
+
+ private static byte[] aesGcmEncrypt(SecretKey key, byte[] nonce, byte[] plaintext, byte[] aad)
+ throws NoSuchAlgorithmException, InvalidKeyException {
+ try {
+ return aesGcmInternal(AesGcmOperation.ENCRYPT, key, nonce, plaintext, aad);
+ } catch (AEADBadTagException ex) {
+ // This should never happen
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private static byte[] aesGcmDecrypt(SecretKey key, byte[] nonce, byte[] ciphertext, byte[] aad)
+ throws NoSuchAlgorithmException, InvalidKeyException, AEADBadTagException {
+ return aesGcmInternal(AesGcmOperation.DECRYPT, key, nonce, ciphertext, aad);
+ }
+
+ private static byte[] aesGcmInternal(
+ AesGcmOperation operation, SecretKey key, byte[] nonce, byte[] text, byte[] aad)
+ throws NoSuchAlgorithmException, InvalidKeyException, AEADBadTagException {
+ Cipher cipher;
+ try {
+ cipher = Cipher.getInstance(ENC_ALG);
+ } catch (NoSuchPaddingException ex) {
+ // This should never happen because AES-GCM doesn't use padding
+ throw new RuntimeException(ex);
+ }
+ GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LEN_BYTES * 8, nonce);
+ try {
+ if (operation == AesGcmOperation.DECRYPT) {
+ cipher.init(Cipher.DECRYPT_MODE, key, spec);
+ } else {
+ cipher.init(Cipher.ENCRYPT_MODE, key, spec);
+ }
+ } catch (InvalidAlgorithmParameterException ex) {
+ // This should never happen
+ throw new RuntimeException(ex);
+ }
+ try {
+ cipher.updateAAD(aad);
+ return cipher.doFinal(text);
+ } catch (AEADBadTagException ex) {
+ // Catch and rethrow AEADBadTagException first because it's a subclass of
+ // BadPaddingException
+ throw ex;
+ } catch (IllegalBlockSizeException | BadPaddingException ex) {
+ // This should never happen because AES-GCM can handle inputs of any length without
+ // padding
+ throw new RuntimeException(ex);
+ }
+ }
+
+ @VisibleForTesting
+ static byte[] encodePublicKey(PublicKey publicKey) {
+ ECPoint point = ((ECPublicKey) publicKey).getW();
+ byte[] x = point.getAffineX().toByteArray();
+ byte[] y = point.getAffineY().toByteArray();
+
+ byte[] output = new byte[EC_PUBLIC_KEY_LEN_BYTES];
+ // The order of arraycopy() is important, because the coordinates may have a one-byte
+ // leading 0 for the sign bit of two's complement form
+ System.arraycopy(y, 0, output, EC_PUBLIC_KEY_LEN_BYTES - y.length, y.length);
+ System.arraycopy(x, 0, output, 1 + EC_COORDINATE_LEN_BYTES - x.length, x.length);
+ output[0] = EC_PUBLIC_KEY_PREFIX;
+ return output;
+ }
+
+ @VisibleForTesting
+ static PublicKey decodePublicKey(byte[] keyBytes)
+ throws NoSuchAlgorithmException, InvalidKeyException {
+ BigInteger x =
+ new BigInteger(
+ /*signum=*/ 1,
+ Arrays.copyOfRange(keyBytes, 1, 1 + EC_COORDINATE_LEN_BYTES));
+ BigInteger y =
+ new BigInteger(
+ /*signum=*/ 1,
+ Arrays.copyOfRange(
+ keyBytes, 1 + EC_COORDINATE_LEN_BYTES, EC_PUBLIC_KEY_LEN_BYTES));
+
+ // Checks if the point is indeed on the P-256 curve for security considerations
+ validateEcPoint(x, y);
+
+ KeyFactory keyFactory = KeyFactory.getInstance(EC_ALG);
+ try {
+ return keyFactory.generatePublic(new ECPublicKeySpec(new ECPoint(x, y), EC_PARAM_SPEC));
+ } catch (InvalidKeySpecException ex) {
+ // This should never happen
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private static void validateEcPoint(BigInteger x, BigInteger y) throws InvalidKeyException {
+ if (x.compareTo(EC_PARAM_P) >= 0
+ || y.compareTo(EC_PARAM_P) >= 0
+ || x.signum() == -1
+ || y.signum() == -1) {
+ throw new InvalidKeyException("Point lies outside of the expected curve");
+ }
+
+ // Points on the curve satisfy y^2 = x^3 + ax + b (mod p)
+ BigInteger lhs = y.modPow(BIG_INT_02, EC_PARAM_P);
+ BigInteger rhs =
+ x.modPow(BIG_INT_02, EC_PARAM_P) // x^2
+ .add(EC_PARAM_A) // x^2 + a
+ .mod(EC_PARAM_P) // This will speed up the next multiplication
+ .multiply(x) // (x^2 + a) * x = x^3 + ax
+ .add(EC_PARAM_B) // x^3 + ax + b
+ .mod(EC_PARAM_P);
+ if (!lhs.equals(rhs)) {
+ throw new InvalidKeyException("Point lies outside of the expected curve");
+ }
+ }
+
+ private static byte[] genRandomNonce() throws NoSuchAlgorithmException {
+ byte[] nonce = new byte[GCM_NONCE_LEN_BYTES];
+ new SecureRandom().nextBytes(nonce);
+ return nonce;
+ }
+
+ @VisibleForTesting
+ static byte[] concat(byte[]... inputs) {
+ int length = 0;
+ for (int i = 0; i < inputs.length; i++) {
+ if (inputs[i] == null) {
+ inputs[i] = EMPTY_BYTE_ARRAY;
+ }
+ length += inputs[i].length;
+ }
+
+ byte[] output = new byte[length];
+ int outputPos = 0;
+ for (byte[] input : inputs) {
+ System.arraycopy(input, /*srcPos=*/ 0, output, outputPos, input.length);
+ outputPos += input.length;
+ }
+ return output;
+ }
+
+ private static byte[] emptyByteArrayIfNull(@Nullable byte[] input) {
+ return input == null ? EMPTY_BYTE_ARRAY : input;
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
new file mode 100644
index 0000000..dfa173c
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
@@ -0,0 +1,205 @@
+/*
+ * 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;
+
+import android.util.Log;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+
+/**
+ * A {@link javax.crypto.SecretKey} wrapped with AES/GCM/NoPadding.
+ *
+ * @hide
+ */
+public class WrappedKey {
+ private static final String TAG = "WrappedKey";
+
+ private static final String KEY_WRAP_CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+ private static final String APPLICATION_KEY_ALGORITHM = "AES";
+ private static final int GCM_TAG_LENGTH_BITS = 128;
+
+ private final int mPlatformKeyGenerationId;
+ private final byte[] mNonce;
+ private final byte[] mKeyMaterial;
+
+ /**
+ * Returns a wrapped form of {@code key}, using {@code wrappingKey} to encrypt the key material.
+ *
+ * @throws InvalidKeyException if {@code wrappingKey} cannot be used to encrypt {@code key}, or
+ * if {@code key} does not expose its key material. See
+ * {@link android.security.keystore.AndroidKeyStoreKey} for an example of a key that does
+ * not expose its key material.
+ */
+ public static WrappedKey fromSecretKey(PlatformEncryptionKey wrappingKey, SecretKey key)
+ throws InvalidKeyException, KeyStoreException {
+ if (key.getEncoded() == null) {
+ throw new InvalidKeyException(
+ "key does not expose encoded material. It cannot be wrapped.");
+ }
+
+ Cipher cipher;
+ try {
+ cipher = Cipher.getInstance(KEY_WRAP_CIPHER_ALGORITHM);
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+ throw new RuntimeException(
+ "Android does not support AES/GCM/NoPadding. This should never happen.");
+ }
+
+ cipher.init(Cipher.WRAP_MODE, wrappingKey.getKey());
+ byte[] encryptedKeyMaterial;
+ try {
+ encryptedKeyMaterial = cipher.wrap(key);
+ } catch (IllegalBlockSizeException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof KeyStoreException) {
+ // If AndroidKeyStore encounters any error here, it throws IllegalBlockSizeException
+ // with KeyStoreException as the cause. This is due to there being no better option
+ // here, as the Cipher#wrap only checked throws InvalidKeyException or
+ // IllegalBlockSizeException. If this is the case, we want to propagate it to the
+ // caller, so rethrow the cause.
+ throw (KeyStoreException) cause;
+ } else {
+ throw new RuntimeException(
+ "IllegalBlockSizeException should not be thrown by AES/GCM/NoPadding mode.",
+ e);
+ }
+ }
+
+ return new WrappedKey(
+ /*nonce=*/ cipher.getIV(),
+ /*keyMaterial=*/ encryptedKeyMaterial,
+ /*platformKeyGenerationId=*/ wrappingKey.getGenerationId());
+ }
+
+ /**
+ * A new instance.
+ *
+ * @param nonce The nonce with which the key material was encrypted.
+ * @param keyMaterial The encrypted bytes of the key material.
+ * @param platformKeyGenerationId The generation ID of the key used to wrap this key.
+ *
+ * @hide
+ */
+ public WrappedKey(byte[] nonce, byte[] keyMaterial, int platformKeyGenerationId) {
+ mNonce = nonce;
+ mKeyMaterial = keyMaterial;
+ mPlatformKeyGenerationId = platformKeyGenerationId;
+ }
+
+ /**
+ * Returns the nonce with which the key material was encrypted.
+ *
+ * @hide
+ */
+ public byte[] getNonce() {
+ return mNonce;
+ }
+
+ /**
+ * Returns the encrypted key material.
+ *
+ * @hide
+ */
+ public byte[] getKeyMaterial() {
+ return mKeyMaterial;
+ }
+
+
+ /**
+ * Returns the generation ID of the platform key, with which this key was wrapped.
+ *
+ * @hide
+ */
+ public int getPlatformKeyGenerationId() {
+ return mPlatformKeyGenerationId;
+ }
+
+ /**
+ * Unwraps the {@code wrappedKeys} with the {@code platformKey}.
+ *
+ * @return The unwrapped keys, indexed by alias.
+ * @throws NoSuchAlgorithmException if AES/GCM/NoPadding Cipher or AES key type is unavailable.
+ * @throws BadPlatformKeyException if the {@code platformKey} has a different generation ID to
+ * any of the {@code wrappedKeys}.
+ *
+ * @hide
+ */
+ public static Map<String, SecretKey> unwrapKeys(
+ PlatformDecryptionKey platformKey,
+ Map<String, WrappedKey> wrappedKeys)
+ throws NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException {
+ HashMap<String, SecretKey> unwrappedKeys = new HashMap<>();
+ Cipher cipher = Cipher.getInstance(KEY_WRAP_CIPHER_ALGORITHM);
+ int platformKeyGenerationId = platformKey.getGenerationId();
+
+ for (String alias : wrappedKeys.keySet()) {
+ WrappedKey wrappedKey = wrappedKeys.get(alias);
+ if (wrappedKey.getPlatformKeyGenerationId() != platformKeyGenerationId) {
+ throw new BadPlatformKeyException(String.format(
+ Locale.US,
+ "WrappedKey with alias '%s' was wrapped with platform key %d, not "
+ + "platform key %d",
+ alias,
+ wrappedKey.getPlatformKeyGenerationId(),
+ platformKey.getGenerationId()));
+ }
+
+ try {
+ cipher.init(
+ Cipher.UNWRAP_MODE,
+ platformKey.getKey(),
+ new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
+ } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+ Log.e(TAG,
+ String.format(
+ Locale.US,
+ "Could not init Cipher to unwrap recoverable key with alias '%s'",
+ alias),
+ e);
+ continue;
+ }
+ SecretKey key;
+ try {
+ key = (SecretKey) cipher.unwrap(
+ wrappedKey.getKeyMaterial(), APPLICATION_KEY_ALGORITHM, Cipher.SECRET_KEY);
+ } catch (InvalidKeyException | NoSuchAlgorithmException e) {
+ Log.e(TAG,
+ String.format(
+ Locale.US,
+ "Error unwrapping recoverable key with alias '%s'",
+ alias),
+ e);
+ continue;
+ }
+ unwrappedKeys.put(alias, key);
+ }
+
+ return unwrappedKeys;
+ }
+}
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
new file mode 100644
index 0000000..3644d36
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -0,0 +1,233 @@
+/*
+ * 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 android.annotation.Nullable;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.util.Log;
+
+import com.android.server.locksettings.recoverablekeystore.WrappedKey;
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry;
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Database of recoverable key information.
+ *
+ * @hide
+ */
+public class RecoverableKeyStoreDb {
+ private static final String TAG = "RecoverableKeyStoreDb";
+ private static final int IDLE_TIMEOUT_SECONDS = 30;
+
+ private final RecoverableKeyStoreDbHelper mKeyStoreDbHelper;
+
+ /**
+ * A new instance, storing the database in the user directory of {@code context}.
+ *
+ * @hide
+ */
+ public static RecoverableKeyStoreDb newInstance(Context context) {
+ RecoverableKeyStoreDbHelper helper = new RecoverableKeyStoreDbHelper(context);
+ helper.setWriteAheadLoggingEnabled(true);
+ helper.setIdleConnectionTimeout(IDLE_TIMEOUT_SECONDS);
+ return new RecoverableKeyStoreDb(helper);
+ }
+
+ private RecoverableKeyStoreDb(RecoverableKeyStoreDbHelper keyStoreDbHelper) {
+ this.mKeyStoreDbHelper = keyStoreDbHelper;
+ }
+
+ /**
+ * Inserts a key into the database.
+ *
+ * @param uid Uid of the application to whom the key belongs.
+ * @param alias The alias of the key in the AndroidKeyStore.
+ * @param wrappedKey The wrapped key.
+ * @return The primary key of the inserted row, or -1 if failed.
+ *
+ * @hide
+ */
+ public long insertKey(int uid, String alias, WrappedKey wrappedKey) {
+ SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(KeysEntry.COLUMN_NAME_UID, uid);
+ values.put(KeysEntry.COLUMN_NAME_ALIAS, alias);
+ values.put(KeysEntry.COLUMN_NAME_NONCE, wrappedKey.getNonce());
+ values.put(KeysEntry.COLUMN_NAME_WRAPPED_KEY, wrappedKey.getKeyMaterial());
+ values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, -1);
+ values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, wrappedKey.getPlatformKeyGenerationId());
+ return db.replace(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values);
+ }
+
+ /**
+ * Gets the key with {@code alias} for the app with {@code uid}.
+ *
+ * @hide
+ */
+ @Nullable public WrappedKey getKey(int uid, String alias) {
+ SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
+ String[] projection = {
+ KeysEntry._ID,
+ KeysEntry.COLUMN_NAME_NONCE,
+ KeysEntry.COLUMN_NAME_WRAPPED_KEY,
+ KeysEntry.COLUMN_NAME_GENERATION_ID};
+ String selection =
+ KeysEntry.COLUMN_NAME_UID + " = ? AND "
+ + KeysEntry.COLUMN_NAME_ALIAS + " = ?";
+ String[] selectionArguments = { Integer.toString(uid), alias };
+
+ try (
+ Cursor cursor = db.query(
+ KeysEntry.TABLE_NAME,
+ projection,
+ selection,
+ selectionArguments,
+ /*groupBy=*/ null,
+ /*having=*/ null,
+ /*orderBy=*/ null)
+ ) {
+ int count = cursor.getCount();
+ if (count == 0) {
+ return null;
+ }
+ if (count > 1) {
+ Log.wtf(TAG,
+ String.format(Locale.US,
+ "%d WrappedKey entries found for uid=%d alias='%s'. "
+ + "Should only ever be 0 or 1.", count, uid, alias));
+ return null;
+ }
+ cursor.moveToFirst();
+ byte[] nonce = cursor.getBlob(
+ cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_NONCE));
+ byte[] keyMaterial = cursor.getBlob(
+ cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_WRAPPED_KEY));
+ int generationId = cursor.getInt(
+ cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_GENERATION_ID));
+ return new WrappedKey(nonce, keyMaterial, generationId);
+ }
+ }
+
+ /**
+ * Returns all keys for the given {@code uid} and {@code platformKeyGenerationId}.
+ *
+ * @param uid User id of the profile to which all the keys are associated.
+ * @param platformKeyGenerationId The generation ID of the platform key that wrapped these keys.
+ * (i.e., this should be the most recent generation ID, as older platform keys are not
+ * usable.)
+ *
+ * @hide
+ */
+ public Map<String, WrappedKey> getAllKeys(int uid, int platformKeyGenerationId) {
+ SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
+ String[] projection = {
+ KeysEntry._ID,
+ KeysEntry.COLUMN_NAME_NONCE,
+ KeysEntry.COLUMN_NAME_WRAPPED_KEY,
+ KeysEntry.COLUMN_NAME_ALIAS};
+ String selection =
+ KeysEntry.COLUMN_NAME_UID + " = ? AND "
+ + KeysEntry.COLUMN_NAME_GENERATION_ID + " = ?";
+ String[] selectionArguments = {
+ Integer.toString(uid), Integer.toString(platformKeyGenerationId) };
+
+ try (
+ Cursor cursor = db.query(
+ KeysEntry.TABLE_NAME,
+ projection,
+ selection,
+ selectionArguments,
+ /*groupBy=*/ null,
+ /*having=*/ null,
+ /*orderBy=*/ null)
+ ) {
+ HashMap<String, WrappedKey> keys = new HashMap<>();
+ while (cursor.moveToNext()) {
+ byte[] nonce = cursor.getBlob(
+ cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_NONCE));
+ byte[] keyMaterial = cursor.getBlob(
+ cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_WRAPPED_KEY));
+ String alias = cursor.getString(
+ cursor.getColumnIndexOrThrow(KeysEntry.COLUMN_NAME_ALIAS));
+ keys.put(alias, new WrappedKey(nonce, keyMaterial, platformKeyGenerationId));
+ }
+ return keys;
+ }
+ }
+
+ /**
+ * Sets the {@code generationId} of the platform key for the account owned by {@code userId}.
+ *
+ * @return The primary key ID of the relation.
+ */
+ public long setPlatformKeyGenerationId(int userId, int generationId) {
+ SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, userId);
+ values.put(UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID, generationId);
+ return db.replace(
+ UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values);
+ }
+
+ /**
+ * Returns the generation ID associated with the platform key of the user with {@code userId}.
+ */
+ public int getPlatformKeyGenerationId(int userId) {
+ SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
+ String[] projection = {
+ UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID};
+ String selection =
+ UserMetadataEntry.COLUMN_NAME_USER_ID + " = ?";
+ String[] selectionArguments = {
+ Integer.toString(userId)};
+
+ try (
+ Cursor cursor = db.query(
+ UserMetadataEntry.TABLE_NAME,
+ projection,
+ selection,
+ selectionArguments,
+ /*groupBy=*/ null,
+ /*having=*/ null,
+ /*orderBy=*/ null)
+ ) {
+ if (cursor.getCount() == 0) {
+ return -1;
+ }
+ cursor.moveToFirst();
+ return cursor.getInt(
+ cursor.getColumnIndexOrThrow(
+ UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID));
+ }
+ }
+
+ /**
+ * Closes all open connections to the database.
+ */
+ public void close() {
+ mKeyStoreDbHelper.close();
+ }
+
+ // TODO: Add method for updating the 'last synced' time.
+}
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
new file mode 100644
index 0000000..b6c168f
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -0,0 +1,79 @@
+/*
+ * 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 android.provider.BaseColumns;
+
+/**
+ * Contract for recoverable key database. Describes the tables present.
+ */
+class RecoverableKeyStoreDbContract {
+ /**
+ * Table holding wrapped keys, and information about when they were last synced.
+ */
+ static class KeysEntry implements BaseColumns {
+ static final String TABLE_NAME = "keys";
+
+ /**
+ * The uid of the application that generated the key.
+ */
+ static final String COLUMN_NAME_UID = "uid";
+
+ /**
+ * The alias of the key, as set in AndroidKeyStore.
+ */
+ static final String COLUMN_NAME_ALIAS = "alias";
+
+ /**
+ * Nonce with which the key was encrypted.
+ */
+ static final String COLUMN_NAME_NONCE = "nonce";
+
+ /**
+ * Encrypted bytes of the key.
+ */
+ static final String COLUMN_NAME_WRAPPED_KEY = "wrapped_key";
+
+ /**
+ * Generation ID of the platform key that was used to encrypt this key.
+ */
+ static final String COLUMN_NAME_GENERATION_ID = "platform_key_generation_id";
+
+ /**
+ * Timestamp of when this key was last synced with remote storage, or -1 if never synced.
+ */
+ static final String COLUMN_NAME_LAST_SYNCED_AT = "last_synced_at";
+ }
+
+ /**
+ * Recoverable KeyStore metadata for a specific user profile.
+ */
+ static class UserMetadataEntry implements BaseColumns {
+ static final String TABLE_NAME = "user_metadata";
+
+ /**
+ * User ID of the profile.
+ */
+ static final String COLUMN_NAME_USER_ID = "user_id";
+
+ /**
+ * Every time a new platform key is generated for a user, this increments. The platform key
+ * is used to wrap recoverable keys on disk.
+ */
+ static final String COLUMN_NAME_PLATFORM_KEY_GENERATION_ID = "platform_key_generation_id";
+ }
+}
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
new file mode 100644
index 0000000..6868203
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
@@ -0,0 +1,57 @@
+package com.android.server.locksettings.recoverablekeystore.storage;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry;
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry;
+
+/**
+ * Helper for creating the recoverable key database.
+ */
+class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
+ private static final int DATABASE_VERSION = 1;
+ private static final String DATABASE_NAME = "recoverablekeystore.db";
+
+ private static final String SQL_CREATE_KEYS_ENTRY =
+ "CREATE TABLE " + KeysEntry.TABLE_NAME + "( "
+ + KeysEntry._ID + " INTEGER PRIMARY KEY,"
+ + 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,"
+ + "UNIQUE(" + KeysEntry.COLUMN_NAME_UID + ","
+ + KeysEntry.COLUMN_NAME_ALIAS + "))";
+
+ private static final String SQL_CREATE_USER_METADATA_ENTRY =
+ "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_DELETE_KEYS_ENTRY =
+ "DROP TABLE IF EXISTS " + KeysEntry.TABLE_NAME;
+
+ private static final String SQL_DELETE_USER_METADATA_ENTRY =
+ "DROP TABLE IF EXISTS " + UserMetadataEntry.TABLE_NAME;
+
+ RecoverableKeyStoreDbHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL(SQL_CREATE_KEYS_ENTRY);
+ db.execSQL(SQL_CREATE_USER_METADATA_ENTRY);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ db.execSQL(SQL_DELETE_KEYS_ENTRY);
+ db.execSQL(SQL_DELETE_USER_METADATA_ENTRY);
+ onCreate(db);
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 0b089fb..384efdd 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -21,6 +21,9 @@
import android.annotation.NonNull;
import android.app.ActivityManager;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -96,21 +99,23 @@
private final ArrayMap<IBinder, ClientRecord> mAllClientRecords =
new ArrayMap<IBinder, ClientRecord>();
private int mCurrentUserId = -1;
- private boolean mGlobalBluetoothA2dpOn = false;
private final IAudioService mAudioService;
private final AudioPlayerStateMonitor mAudioPlayerStateMonitor;
private final Handler mHandler = new Handler();
- private final AudioRoutesInfo mAudioRoutesInfo = new AudioRoutesInfo();
private final IntArray mActivePlayerMinPriorityQueue = new IntArray();
private final IntArray mActivePlayerUidMinPriorityQueue = new IntArray();
+ private final BroadcastReceiver mReceiver = new MediaRouterServiceBroadcastReceiver();
+ BluetoothDevice mBluetoothDevice;
+ int mAudioRouteMainType = AudioRoutesInfo.MAIN_SPEAKER;
+ boolean mGlobalBluetoothA2dpOn = false;
+
public MediaRouterService(Context context) {
mContext = context;
Watchdog.getInstance().addMonitor(this);
mAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
-
mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance();
mAudioPlayerStateMonitor.registerListener(
new AudioPlayerStateMonitor.OnAudioPlayerActiveStateChangedListener() {
@@ -170,44 +175,30 @@
@Override
public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
synchronized (mLock) {
- if (newRoutes.mainType != mAudioRoutesInfo.mainType) {
+ if (newRoutes.mainType != mAudioRouteMainType) {
if ((newRoutes.mainType & (AudioRoutesInfo.MAIN_HEADSET
| AudioRoutesInfo.MAIN_HEADPHONES
| AudioRoutesInfo.MAIN_USB)) == 0) {
// headset was plugged out.
- mGlobalBluetoothA2dpOn = newRoutes.bluetoothName != null;
+ mGlobalBluetoothA2dpOn = mBluetoothDevice != null;
} else {
// headset was plugged in.
mGlobalBluetoothA2dpOn = false;
}
- mAudioRoutesInfo.mainType = newRoutes.mainType;
+ mAudioRouteMainType = newRoutes.mainType;
}
- if (!TextUtils.equals(
- newRoutes.bluetoothName, mAudioRoutesInfo.bluetoothName)) {
- if (newRoutes.bluetoothName == null) {
- // BT was disconnected.
- mGlobalBluetoothA2dpOn = false;
- } else {
- // BT was connected or changed.
- mGlobalBluetoothA2dpOn = true;
- }
- mAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
- }
- // Although a Bluetooth device is connected before a new audio playback is
- // started, dispatchAudioRoutChanged() can be called after
- // onAudioPlayerActiveStateChanged(). That causes restoreBluetoothA2dp()
- // is called before mGlobalBluetoothA2dpOn is updated.
- // Calling restoreBluetoothA2dp() here could prevent that.
- restoreBluetoothA2dp();
+ // The new audio routes info could be delivered with several seconds delay.
+ // In order to avoid such delay, Bluetooth device info will be updated
+ // via MediaRouterServiceBroadcastReceiver.
}
}
});
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in the audio service.");
}
- synchronized (mLock) {
- mGlobalBluetoothA2dpOn = (audioRoutes != null && audioRoutes.bluetoothName != null);
- }
+
+ IntentFilter intentFilter = new IntentFilter(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+ context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
}
public void systemRunning() {
@@ -415,14 +406,12 @@
void restoreBluetoothA2dp() {
try {
- boolean btConnected = false;
boolean a2dpOn = false;
synchronized (mLock) {
- btConnected = mAudioRoutesInfo.bluetoothName != null;
a2dpOn = mGlobalBluetoothA2dpOn;
}
// We don't need to change a2dp status when bluetooth is not connected.
- if (btConnected) {
+ if (mBluetoothDevice != null) {
Slog.v(TAG, "restoreBluetoothA2dp(" + a2dpOn + ")");
mAudioService.setBluetoothA2dpOn(a2dpOn);
}
@@ -661,6 +650,25 @@
return false;
}
+ final class MediaRouterServiceBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
+ int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
+ BluetoothProfile.STATE_DISCONNECTED);
+ if (state == BluetoothProfile.STATE_DISCONNECTED) {
+ mGlobalBluetoothA2dpOn = false;
+ mBluetoothDevice = null;
+ } else if (state == BluetoothProfile.STATE_CONNECTED) {
+ mGlobalBluetoothA2dpOn = true;
+ mBluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ // To ensure that BT A2DP is on, call restoreBluetoothA2dp().
+ restoreBluetoothA2dp();
+ }
+ }
+ }
+ }
+
/**
* Information about a particular client of the media router.
* The contents of this object is guarded by mLock.
diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS
index 6b77d83..5d4287b 100644
--- a/services/core/java/com/android/server/net/OWNERS
+++ b/services/core/java/com/android/server/net/OWNERS
@@ -2,6 +2,7 @@
ek@google.com
hugobenichi@google.com
+jchalard@google.com
jsharkey@android.com
lorenzo@google.com
satk@google.com
diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
index 7ebef83..f4ee0ce 100644
--- a/services/core/java/com/android/server/pm/DumpState.java
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -40,6 +40,7 @@
public static final int DUMP_COMPILER_STATS = 1 << 21;
public static final int DUMP_CHANGES = 1 << 22;
public static final int DUMP_VOLUMES = 1 << 23;
+ public static final int DUMP_SERVICE_PERMISSIONS = 1 << 24;
public static final int OPTION_SHOW_FILTERS = 1 << 0;
@@ -92,4 +93,4 @@
public void setSharedUser(SharedUserSetting user) {
mSharedUser = user;
}
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 5dfb48a..696d895 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -47,6 +47,7 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
+import static android.content.pm.PackageManager.INSTALL_FAILED_NEWER_SDK;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
import static android.content.pm.PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
import static android.content.pm.PackageManager.INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE;
@@ -172,6 +173,7 @@
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageParser.ParseFlags;
+import android.content.pm.PackageParser.ServiceIntentInfo;
import android.content.pm.PackageStats;
import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
@@ -649,18 +651,15 @@
@GuardedBy("mInstallLock")
final Installer mInstaller;
- /** Directory where installed third-party apps stored */
- final File mAppInstallDir;
-
- /**
- * Directory to which applications installed internally have their
- * 32 bit native libraries copied.
- */
- private File mAppLib32InstallDir;
-
- // Directory containing the private parts (e.g. code and non-resource assets) of forward-locked
- // apps.
- final File mDrmAppPrivateInstallDir;
+ /** Directory where installed applications are stored */
+ private static final File sAppInstallDir =
+ new File(Environment.getDataDirectory(), "app");
+ /** Directory where installed application's 32-bit native libraries are copied. */
+ private static final File sAppLib32InstallDir =
+ new File(Environment.getDataDirectory(), "app-lib");
+ /** Directory where code and non-resource assets of forward-locked applications are stored */
+ private static final File sDrmAppPrivateInstallDir =
+ new File(Environment.getDataDirectory(), "app-private");
// ----------------------------------------------------------------
@@ -741,9 +740,6 @@
@GuardedBy("mAvailableFeatures")
final ArrayMap<String, FeatureInfo> mAvailableFeatures;
- // If mac_permissions.xml was found for seinfo labeling.
- boolean mFoundPolicyFile;
-
private final InstantAppRegistry mInstantAppRegistry;
@GuardedBy("mPackages")
@@ -2444,11 +2440,6 @@
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
mInstantAppRegistry = new InstantAppRegistry(this);
- File dataDir = Environment.getDataDirectory();
- mAppInstallDir = new File(dataDir, "app");
- mAppLib32InstallDir = new File(dataDir, "app-lib");
- mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
-
ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
final int builtInLibCount = libConfig.size();
for (int i = 0; i < builtInLibCount; i++) {
@@ -2458,7 +2449,7 @@
SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);
}
- mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
+ SELinuxMMAC.readInstallPolicy();
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "read user settings");
mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
@@ -2736,9 +2727,9 @@
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
- scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
+ scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
- scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags
+ scanDirTracedLI(sDrmAppPrivateInstallDir, mDefParseFlags
| PackageParser.PARSE_FORWARD_LOCK,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
@@ -2907,7 +2898,18 @@
// NOTE: We ignore potential failures here during a system scan (like
// the rest of the commands above) because there's precious little we
// can do about it. A settings error is reported, though.
- adjustCpuAbisForSharedUserLPw(setting.packages, null /*scannedPackage*/);
+ final List<String> changedAbiCodePath =
+ adjustCpuAbisForSharedUserLPw(setting.packages, null /*scannedPackage*/);
+ if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
+ for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
+ final String codePathString = changedAbiCodePath.get(i);
+ try {
+ mInstaller.rmdex(codePathString,
+ getDexCodeInstructionSet(getPreferredInstructionSet()));
+ } catch (InstallerException ignored) {
+ }
+ }
+ }
}
// Now that we know all the packages we are keeping,
@@ -8133,6 +8135,7 @@
int errorCode = PackageManager.INSTALL_SUCCEEDED;
if (throwable == null) {
+ // TODO(toddke): move lower in the scan chain
// Static shared libraries have synthetic package names
if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(parseResult.pkg);
@@ -8485,7 +8488,7 @@
// Set CPU Abis to application info.
if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0) {
final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, updatedPs);
- derivePackageAbi(pkg, cpuAbiOverride, false, mAppLib32InstallDir);
+ derivePackageAbi(pkg, cpuAbiOverride, false);
} else {
pkg.applicationInfo.primaryCpuAbi = updatedPs.primaryCpuAbiString;
pkg.applicationInfo.secondaryCpuAbi = updatedPs.secondaryCpuAbiString;
@@ -8593,7 +8596,7 @@
}
// Note that we invoke the following method only if we are about to unpack an application
- PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags
+ PackageParser.Package scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
/*
@@ -9614,12 +9617,12 @@
final PackageParser.Package scannedPkg;
try {
// Scan the parent
- scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags, currentTime, user);
+ scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags, currentTime, user);
// Scan the children
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPkg = pkg.childPackages.get(i);
- scanPackageLI(childPkg, parseFlags,
+ scanPackageNewLI(childPkg, parseFlags,
scanFlags, currentTime, user);
}
} finally {
@@ -9633,23 +9636,655 @@
return scannedPkg;
}
- private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
+ /** The result of a package scan. */
+ private static class ScanResult {
+ /** Whether or not the package scan was successful */
+ public final boolean success;
+ /**
+ * The final package settings. This may be the same object passed in
+ * the {@link ScanRequest}, but, with modified values.
+ */
+ @Nullable public final PackageSetting pkgSetting;
+ /** ABI code paths that have changed in the package scan */
+ @Nullable public final List<String> changedAbiCodePath;
+ public ScanResult(
+ boolean success,
+ @Nullable PackageSetting pkgSetting,
+ @Nullable List<String> changedAbiCodePath) {
+ this.success = success;
+ this.pkgSetting = pkgSetting;
+ this.changedAbiCodePath = changedAbiCodePath;
+ }
+ }
+
+ /** A package to be scanned */
+ private static class ScanRequest {
+ /** The parsed package */
+ @NonNull public final PackageParser.Package pkg;
+ /** Shared user settings, if the package has a shared user */
+ @Nullable public final SharedUserSetting sharedUserSetting;
+ /**
+ * Package settings of the currently installed version.
+ * <p><em>IMPORTANT:</em> The contents of this object may be modified
+ * during scan.
+ */
+ @Nullable public final PackageSetting pkgSetting;
+ /** A copy of the settings for the currently installed version */
+ @Nullable public final PackageSetting oldPkgSetting;
+ /** Package settings for the disabled version on the /system partition */
+ @Nullable public final PackageSetting disabledPkgSetting;
+ /** Package settings for the installed version under its original package name */
+ @Nullable public final PackageSetting originalPkgSetting;
+ /** The real package name of a renamed application */
+ @Nullable public final String realPkgName;
+ public final @ParseFlags int parseFlags;
+ public final @ScanFlags int scanFlags;
+ /** The user for which the package is being scanned */
+ @Nullable public final UserHandle user;
+ /** Whether or not the platform package is being scanned */
+ public final boolean isPlatformPackage;
+ public ScanRequest(
+ @NonNull PackageParser.Package pkg,
+ @Nullable SharedUserSetting sharedUserSetting,
+ @Nullable PackageSetting pkgSetting,
+ @Nullable PackageSetting disabledPkgSetting,
+ @Nullable PackageSetting originalPkgSetting,
+ @Nullable String realPkgName,
+ @ParseFlags int parseFlags,
+ @ScanFlags int scanFlags,
+ boolean isPlatformPackage,
+ @Nullable UserHandle user) {
+ this.pkg = pkg;
+ this.pkgSetting = pkgSetting;
+ this.sharedUserSetting = sharedUserSetting;
+ this.oldPkgSetting = pkgSetting == null ? null : new PackageSetting(pkgSetting);
+ this.disabledPkgSetting = disabledPkgSetting;
+ this.originalPkgSetting = originalPkgSetting;
+ this.realPkgName = realPkgName;
+ this.parseFlags = parseFlags;
+ this.scanFlags = scanFlags;
+ this.isPlatformPackage = isPlatformPackage;
+ this.user = user;
+ }
+ }
+
+ @GuardedBy("mInstallLock")
+ private PackageParser.Package scanPackageNewLI(@NonNull PackageParser.Package pkg,
final @ParseFlags int parseFlags, final @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
- boolean success = false;
- try {
- final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,
- currentTime, user);
- success = true;
- return res;
- } finally {
- if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
- // DELETE_DATA_ON_FAILURES is only used by frozen paths
- destroyAppDataLIF(pkg, UserHandle.USER_ALL,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
- destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
+
+ final String renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
+ final String realPkgName = getRealPackageName(pkg, renamedPkgName);
+ if (realPkgName != null) {
+ ensurePackageRenamed(pkg, renamedPkgName);
+ }
+ final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName);
+ final PackageSetting pkgSetting = mSettings.getPackageLPr(pkg.packageName);
+ final PackageSetting disabledPkgSetting =
+ mSettings.getDisabledSystemPkgLPr(pkg.packageName);
+
+ if (mTransferedPackages.contains(pkg.packageName)) {
+ Slog.w(TAG, "Package " + pkg.packageName
+ + " was transferred to another, but its .apk remains");
+ }
+
+ synchronized (mPackages) {
+ applyPolicy(pkg, parseFlags, scanFlags);
+ assertPackageIsValid(pkg, parseFlags, scanFlags);
+
+ SharedUserSetting sharedUserSetting = null;
+ if (pkg.mSharedUserId != null) {
+ // SIDE EFFECTS; may potentially allocate a new shared user
+ sharedUserSetting = mSettings.getSharedUserLPw(
+ pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+ Log.d(TAG, "Shared UserID " + pkg.mSharedUserId
+ + " (uid=" + sharedUserSetting.userId + "):"
+ + " packages=" + sharedUserSetting.packages);
+ }
+ }
+
+ boolean scanSucceeded = false;
+ try {
+ final ScanRequest request = new ScanRequest(pkg, sharedUserSetting, pkgSetting,
+ disabledPkgSetting, originalPkgSetting, realPkgName, parseFlags, scanFlags,
+ (pkg == mPlatformPackage), user);
+ final ScanResult result = scanPackageOnlyLI(request, mFactoryTest, currentTime);
+ if (result.success) {
+ commitScanResultsLocked(request, result);
+ }
+ scanSucceeded = true;
+ } finally {
+ if (!scanSucceeded && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
+ // DELETE_DATA_ON_FAILURES is only used by frozen paths
+ destroyAppDataLIF(pkg, UserHandle.USER_ALL,
+ StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+ destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
+ }
}
}
+ return pkg;
+ }
+
+ /**
+ * Commits the package scan and modifies system state.
+ * <p><em>WARNING:</em> The method may throw an excpetion in the middle
+ * of committing the package, leaving the system in an inconsistent state.
+ * This needs to be fixed so, once we get to this point, no errors are
+ * possible and the system is not left in an inconsistent state.
+ */
+ @GuardedBy("mPackages")
+ private void commitScanResultsLocked(@NonNull ScanRequest request, @NonNull ScanResult result)
+ throws PackageManagerException {
+ final PackageParser.Package pkg = request.pkg;
+ final @ParseFlags int parseFlags = request.parseFlags;
+ final @ScanFlags int scanFlags = request.scanFlags;
+ final PackageSetting oldPkgSetting = request.oldPkgSetting;
+ final PackageSetting originalPkgSetting = request.originalPkgSetting;
+ final UserHandle user = request.user;
+ final String realPkgName = request.realPkgName;
+ final PackageSetting pkgSetting = result.pkgSetting;
+ final List<String> changedAbiCodePath = result.changedAbiCodePath;
+ final boolean newPkgSettingCreated = (result.pkgSetting != request.pkgSetting);
+
+ if (newPkgSettingCreated) {
+ if (originalPkgSetting != null) {
+ mSettings.addRenamedPackageLPw(pkg.packageName, originalPkgSetting.name);
+ }
+ // THROWS: when we can't allocate a user id. add call to check if there's
+ // enough space to ensure we won't throw; otherwise, don't modify state
+ mSettings.addUserToSettingLPw(pkgSetting);
+
+ if (originalPkgSetting != null && (scanFlags & SCAN_CHECK_ONLY) == 0) {
+ mTransferedPackages.add(originalPkgSetting.name);
+ }
+ }
+ // TODO(toddke): Consider a method specifically for modifying the Package object
+ // post scan; or, moving this stuff out of the Package object since it has nothing
+ // to do with the package on disk.
+ // We need to have this here because addUserToSettingLPw() is sometimes responsible
+ // for creating the application ID. If we did this earlier, we would be saving the
+ // correct ID.
+ pkg.applicationInfo.uid = pkgSetting.appId;
+
+ mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);
+
+ if ((scanFlags & SCAN_CHECK_ONLY) == 0 && realPkgName != null) {
+ mTransferedPackages.add(pkg.packageName);
+ }
+
+ // THROWS: when requested libraries that can't be found. it only changes
+ // the state of the passed in pkg object, so, move to the top of the method
+ // and allow it to abort
+ if ((scanFlags & SCAN_BOOTING) == 0
+ && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+ // Check all shared libraries and map to their actual file path.
+ // We only do this here for apps not on a system dir, because those
+ // are the only ones that can fail an install due to this. We
+ // will take care of the system apps by updating all of their
+ // library paths after the scan is done. Also during the initial
+ // scan don't update any libs as we do this wholesale after all
+ // apps are scanned to avoid dependency based scanning.
+ updateSharedLibrariesLPr(pkg, null);
+ }
+
+ // All versions of a static shared library are referenced with the same
+ // package name. Internally, we use a synthetic package name to allow
+ // multiple versions of the same shared library to be installed. So,
+ // we need to generate the synthetic package name of the latest shared
+ // library in order to compare signatures.
+ PackageSetting signatureCheckPs = pkgSetting;
+ if (pkg.applicationInfo.isStaticSharedLibrary()) {
+ SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
+ if (libraryEntry != null) {
+ signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
+ }
+ }
+
+ final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+ if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
+ if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
+ // We just determined the app is signed correctly, so bring
+ // over the latest parsed certs.
+ pkgSetting.signatures.mSignatures = pkg.mSignatures;
+ } else {
+ if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+ throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "Package " + pkg.packageName + " upgrade keys do not match the "
+ + "previously installed version");
+ } else {
+ pkgSetting.signatures.mSignatures = pkg.mSignatures;
+ String msg = "System package " + pkg.packageName
+ + " signature changed; retaining data.";
+ reportSettingsProblem(Log.WARN, msg);
+ }
+ }
+ } else {
+ try {
+ final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg);
+ final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg);
+ final boolean compatMatch = verifySignatures(signatureCheckPs, pkg.mSignatures,
+ compareCompat, compareRecover);
+ // The new KeySets will be re-added later in the scanning process.
+ if (compatMatch) {
+ synchronized (mPackages) {
+ ksms.removeAppKeySetDataLPw(pkg.packageName);
+ }
+ }
+ // We just determined the app is signed correctly, so bring
+ // over the latest parsed certs.
+ pkgSetting.signatures.mSignatures = pkg.mSignatures;
+ } catch (PackageManagerException e) {
+ if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+ throw e;
+ }
+ // The signature has changed, but this package is in the system
+ // image... let's recover!
+ pkgSetting.signatures.mSignatures = pkg.mSignatures;
+ // However... if this package is part of a shared user, but it
+ // doesn't match the signature of the shared user, let's fail.
+ // What this means is that you can't change the signatures
+ // associated with an overall shared user, which doesn't seem all
+ // that unreasonable.
+ if (signatureCheckPs.sharedUser != null) {
+ if (compareSignatures(signatureCheckPs.sharedUser.signatures.mSignatures,
+ pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
+ throw new PackageManagerException(
+ INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+ "Signature mismatch for shared user: "
+ + pkgSetting.sharedUser);
+ }
+ }
+ // File a report about this.
+ String msg = "System package " + pkg.packageName
+ + " signature changed; retaining data.";
+ reportSettingsProblem(Log.WARN, msg);
+ }
+ }
+
+ if ((scanFlags & SCAN_CHECK_ONLY) == 0 && pkg.mAdoptPermissions != null) {
+ // This package wants to adopt ownership of permissions from
+ // another package.
+ for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {
+ final String origName = pkg.mAdoptPermissions.get(i);
+ final PackageSetting orig = mSettings.getPackageLPr(origName);
+ if (orig != null) {
+ if (verifyPackageUpdateLPr(orig, pkg)) {
+ Slog.i(TAG, "Adopting permissions from " + origName + " to "
+ + pkg.packageName);
+ mSettings.mPermissions.transferPermissions(origName, pkg.packageName);
+ }
+ }
+ }
+ }
+
+ if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
+ for (int i = changedAbiCodePath.size() - 1; i <= 0; --i) {
+ final String codePathString = changedAbiCodePath.get(i);
+ try {
+ mInstaller.rmdex(codePathString,
+ getDexCodeInstructionSet(getPreferredInstructionSet()));
+ } catch (InstallerException ignored) {
+ }
+ }
+ }
+
+ if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
+ if (oldPkgSetting != null) {
+ synchronized (mPackages) {
+ mSettings.mPackages.put(oldPkgSetting.name, oldPkgSetting);
+ }
+ }
+ } else {
+ final int userId = user == null ? 0 : user.getIdentifier();
+ // Modify state for the given package setting
+ commitPackageSettings(pkg, pkgSetting, user, scanFlags,
+ (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);
+ if (pkgSetting.getInstantApp(userId)) {
+ mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
+ }
+ }
+ }
+
+ /**
+ * Returns the "real" name of the package.
+ * <p>This may differ from the package's actual name if the application has already
+ * been installed under one of this package's original names.
+ */
+ private static @Nullable String getRealPackageName(@NonNull PackageParser.Package pkg,
+ @Nullable String renamedPkgName) {
+ if (pkg.mOriginalPackages == null || !pkg.mOriginalPackages.contains(renamedPkgName)) {
+ return null;
+ }
+ return pkg.mRealPackage;
+ }
+
+ /**
+ * Returns the original package setting.
+ * <p>A package can migrate its name during an update. In this scenario, a package
+ * designates a set of names that it considers as one of its original names.
+ * <p>An original package must be signed identically and it must have the same
+ * shared user [if any].
+ */
+ @GuardedBy("mPackages")
+ private @Nullable PackageSetting getOriginalPackageLocked(@NonNull PackageParser.Package pkg,
+ @Nullable String renamedPkgName) {
+ if (pkg.mOriginalPackages == null || pkg.mOriginalPackages.contains(renamedPkgName)) {
+ return null;
+ }
+ for (int i = pkg.mOriginalPackages.size() - 1; i >= 0; --i) {
+ final PackageSetting originalPs =
+ mSettings.getPackageLPr(pkg.mOriginalPackages.get(i));
+ if (originalPs != null) {
+ // the package is already installed under its original name...
+ // but, should we use it?
+ if (!verifyPackageUpdateLPr(originalPs, pkg)) {
+ // the new package is incompatible with the original
+ continue;
+ } else if (originalPs.sharedUser != null) {
+ if (!originalPs.sharedUser.name.equals(pkg.mSharedUserId)) {
+ // the shared user id is incompatible with the original
+ Slog.w(TAG, "Unable to migrate data from " + originalPs.name
+ + " to " + pkg.packageName + ": old uid "
+ + originalPs.sharedUser.name
+ + " differs from " + pkg.mSharedUserId);
+ continue;
+ }
+ // TODO: Add case when shared user id is added [b/28144775]
+ } else {
+ if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
+ + pkg.packageName + " to old name " + originalPs.name);
+ }
+ return originalPs;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Renames the package if it was installed under a different name.
+ * <p>When we've already installed the package under an original name, update
+ * the new package so we can continue to have the old name.
+ */
+ private static void ensurePackageRenamed(@NonNull PackageParser.Package pkg,
+ @NonNull String renamedPackageName) {
+ if (pkg.mOriginalPackages == null
+ || !pkg.mOriginalPackages.contains(renamedPackageName)
+ || pkg.packageName.equals(renamedPackageName)) {
+ return;
+ }
+ pkg.setPackageName(renamedPackageName);
+ }
+
+ /**
+ * Just scans the package without any side effects.
+ * <p>Not entirely true at the moment. There is still one side effect -- this
+ * method potentially modifies a live {@link PackageSetting} object representing
+ * the package being scanned. This will be resolved in the future.
+ *
+ * @param request Information about the package to be scanned
+ * @param isUnderFactoryTest Whether or not the device is under factory test
+ * @param currentTime The current time, in millis
+ * @return The results of the scan
+ */
+ @GuardedBy("mInstallLock")
+ private static @NonNull ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
+ boolean isUnderFactoryTest, long currentTime)
+ throws PackageManagerException {
+ final PackageParser.Package pkg = request.pkg;
+ PackageSetting pkgSetting = request.pkgSetting;
+ final PackageSetting disabledPkgSetting = request.disabledPkgSetting;
+ final PackageSetting originalPkgSetting = request.originalPkgSetting;
+ final @ParseFlags int parseFlags = request.parseFlags;
+ final @ScanFlags int scanFlags = request.scanFlags;
+ final String realPkgName = request.realPkgName;
+ final SharedUserSetting sharedUserSetting = request.sharedUserSetting;
+ final UserHandle user = request.user;
+ final boolean isPlatformPackage = request.isPlatformPackage;
+
+ List<String> changedAbiCodePath = null;
+
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+ Log.d(TAG, "Scanning package " + pkg.packageName);
+ }
+
+ if (Build.IS_DEBUGGABLE &&
+ pkg.isPrivileged() &&
+ !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
+ PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
+ }
+
+ // Initialize package source and resource directories
+ final File scanFile = new File(pkg.codePath);
+ final File destCodeFile = new File(pkg.applicationInfo.getCodePath());
+ final File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
+
+ // We keep references to the derived CPU Abis from settings in oder to reuse
+ // them in the case where we're not upgrading or booting for the first time.
+ String primaryCpuAbiFromSettings = null;
+ String secondaryCpuAbiFromSettings = null;
+ boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
+
+ if (!needToDeriveAbi) {
+ if (pkgSetting != null) {
+ primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString;
+ secondaryCpuAbiFromSettings = pkgSetting.secondaryCpuAbiString;
+ } else {
+ // Re-scanning a system package after uninstalling updates; need to derive ABI
+ needToDeriveAbi = true;
+ }
+ }
+
+ if (pkgSetting != null && pkgSetting.sharedUser != sharedUserSetting) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Package " + pkg.packageName + " shared user changed from "
+ + (pkgSetting.sharedUser != null
+ ? pkgSetting.sharedUser.name : "<nothing>")
+ + " to "
+ + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
+ + "; replacing with new");
+ pkgSetting = null;
+ }
+
+ String[] usesStaticLibraries = null;
+ if (pkg.usesStaticLibraries != null) {
+ usesStaticLibraries = new String[pkg.usesStaticLibraries.size()];
+ pkg.usesStaticLibraries.toArray(usesStaticLibraries);
+ }
+
+ final boolean createNewPackage = (pkgSetting == null);
+ if (createNewPackage) {
+ final String parentPackageName = (pkg.parentPackage != null)
+ ? pkg.parentPackage.packageName : null;
+ final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+ final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
+ // REMOVE SharedUserSetting from method; update in a separate call
+ pkgSetting = Settings.createNewSetting(pkg.packageName, originalPkgSetting,
+ disabledPkgSetting, realPkgName, sharedUserSetting, destCodeFile,
+ destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,
+ pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi,
+ pkg.mVersionCode, pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
+ user, true /*allowInstall*/, instantApp, virtualPreload,
+ parentPackageName, pkg.getChildPackageNames(),
+ UserManagerService.getInstance(), usesStaticLibraries,
+ pkg.usesStaticLibrariesVersions);
+ } else {
+ // REMOVE SharedUserSetting from method; update in a separate call.
+ //
+ // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
+ // secondaryCpuAbi are not known at this point so we always update them
+ // to null here, only to reset them at a later point.
+ Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
+ destCodeFile, pkg.applicationInfo.nativeLibraryDir,
+ pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi,
+ pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
+ pkg.getChildPackageNames(), UserManagerService.getInstance(),
+ usesStaticLibraries, pkg.usesStaticLibrariesVersions);
+ }
+ if (createNewPackage && originalPkgSetting != null) {
+ // If we are first transitioning from an original package,
+ // fix up the new package's name now. We need to do this after
+ // looking up the package under its new name, so getPackageLP
+ // can take care of fiddling things correctly.
+ pkg.setPackageName(originalPkgSetting.name);
+
+ // File a report about this.
+ String msg = "New package " + pkgSetting.realName
+ + " renamed to replace old package " + pkgSetting.name;
+ reportSettingsProblem(Log.WARN, msg);
+ }
+
+ if (disabledPkgSetting != null) {
+ pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+ }
+
+ SELinuxMMAC.assignSeInfoValue(pkg);
+
+ pkg.mExtras = pkgSetting;
+ pkg.applicationInfo.processName = fixProcessName(
+ pkg.applicationInfo.packageName,
+ pkg.applicationInfo.processName);
+
+ if (!isPlatformPackage) {
+ // Get all of our default paths setup
+ pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
+ }
+
+ final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
+
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
+ if (needToDeriveAbi) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
+ final boolean extractNativeLibs = !pkg.isLibrary();
+ derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+ // Some system apps still use directory structure for native libraries
+ // in which case we might end up not detecting abi solely based on apk
+ // structure. Try to detect abi based on directory structure.
+ if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
+ pkg.applicationInfo.primaryCpuAbi == null) {
+ setBundledAppAbisAndRoots(pkg, pkgSetting);
+ setNativeLibraryPaths(pkg, sAppLib32InstallDir);
+ }
+ } else {
+ // This is not a first boot or an upgrade, don't bother deriving the
+ // ABI during the scan. Instead, trust the value that was stored in the
+ // package setting.
+ pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings;
+ pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings;
+
+ setNativeLibraryPaths(pkg, sAppLib32InstallDir);
+
+ if (DEBUG_ABI_SELECTION) {
+ Slog.i(TAG, "Using ABIS and native lib paths from settings : " +
+ pkg.packageName + " " + pkg.applicationInfo.primaryCpuAbi + ", " +
+ pkg.applicationInfo.secondaryCpuAbi);
+ }
+ }
+ } else {
+ if ((scanFlags & SCAN_MOVE) != 0) {
+ // We haven't run dex-opt for this move (since we've moved the compiled output too)
+ // but we already have this packages package info in the PackageSetting. We just
+ // use that and derive the native library path based on the new codepath.
+ pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;
+ pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString;
+ }
+
+ // Set native library paths again. For moves, the path will be updated based on the
+ // ABIs we've determined above. For non-moves, the path will be updated based on the
+ // ABIs we determined during compilation, but the path will depend on the final
+ // package path (after the rename away from the stage path).
+ setNativeLibraryPaths(pkg, sAppLib32InstallDir);
+ }
+
+ // This is a special case for the "system" package, where the ABI is
+ // dictated by the zygote configuration (and init.rc). We should keep track
+ // of this ABI so that we can deal with "normal" applications that run under
+ // the same UID correctly.
+ if (isPlatformPackage) {
+ pkg.applicationInfo.primaryCpuAbi = VMRuntime.getRuntime().is64Bit() ?
+ Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
+ }
+
+ // If there's a mismatch between the abi-override in the package setting
+ // and the abiOverride specified for the install. Warn about this because we
+ // would've already compiled the app without taking the package setting into
+ // account.
+ if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
+ if (cpuAbiOverride == null && pkgSetting.cpuAbiOverrideString != null) {
+ Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
+ " for package " + pkg.packageName);
+ }
+ }
+
+ pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
+ pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
+ pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
+
+ // Copy the derived override back to the parsed package, so that we can
+ // update the package settings accordingly.
+ pkg.cpuAbiOverride = cpuAbiOverride;
+
+ if (DEBUG_ABI_SELECTION) {
+ Slog.d(TAG, "Resolved nativeLibraryRoot for " + pkg.applicationInfo.packageName
+ + " to root=" + pkg.applicationInfo.nativeLibraryRootDir + ", isa="
+ + pkg.applicationInfo.nativeLibraryRootRequiresIsa);
+ }
+
+ // Push the derived path down into PackageSettings so we know what to
+ // clean up at uninstall time.
+ pkgSetting.legacyNativeLibraryPathString = pkg.applicationInfo.nativeLibraryRootDir;
+
+ if (DEBUG_ABI_SELECTION) {
+ Log.d(TAG, "Abis for package[" + pkg.packageName + "] are" +
+ " primary=" + pkg.applicationInfo.primaryCpuAbi +
+ " secondary=" + pkg.applicationInfo.secondaryCpuAbi);
+ }
+
+ if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
+ // We don't do this here during boot because we can do it all
+ // at once after scanning all existing packages.
+ //
+ // We also do this *before* we perform dexopt on this package, so that
+ // we can avoid redundant dexopts, and also to make sure we've got the
+ // code and package path correct.
+ changedAbiCodePath =
+ adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, pkg);
+ }
+
+ if (isUnderFactoryTest && pkg.requestedPermissions.contains(
+ android.Manifest.permission.FACTORY_TEST)) {
+ pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
+ }
+
+ if (isSystemApp(pkg)) {
+ pkgSetting.isOrphaned = true;
+ }
+
+ // Take care of first install / last update times.
+ final long scanFileTime = getLastModifiedTime(pkg);
+ if (currentTime != 0) {
+ if (pkgSetting.firstInstallTime == 0) {
+ pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
+ } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
+ pkgSetting.lastUpdateTime = currentTime;
+ }
+ } else if (pkgSetting.firstInstallTime == 0) {
+ // We need *something*. Take time time stamp of the file.
+ pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
+ } else if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
+ if (scanFileTime != pkgSetting.timeStamp) {
+ // A package on the system image has changed; consider this
+ // to be an update.
+ pkgSetting.lastUpdateTime = scanFileTime;
+ }
+ }
+ pkgSetting.setTimeStamp(scanFileTime);
+
+ return new ScanResult(true, pkgSetting, changedAbiCodePath);
}
/**
@@ -9700,490 +10335,6 @@
}
}
- private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
- final @ParseFlags int parseFlags, final @ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user)
- throws PackageManagerException {
- if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
- Log.d(TAG, "Scanning package " + pkg.packageName);
- }
-
- applyPolicy(pkg, parseFlags, scanFlags);
-
- assertPackageIsValid(pkg, parseFlags, scanFlags);
-
- if (Build.IS_DEBUGGABLE &&
- pkg.isPrivileged() &&
- SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, false)) {
- PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
- }
-
- // Initialize package source and resource directories
- final File scanFile = new File(pkg.codePath);
- final File destCodeFile = new File(pkg.applicationInfo.getCodePath());
- final File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
-
- SharedUserSetting suid = null;
- PackageSetting pkgSetting = null;
-
- // Getting the package setting may have a side-effect, so if we
- // are only checking if scan would succeed, stash a copy of the
- // old setting to restore at the end.
- PackageSetting nonMutatedPs = null;
-
- // We keep references to the derived CPU Abis from settings in oder to reuse
- // them in the case where we're not upgrading or booting for the first time.
- String primaryCpuAbiFromSettings = null;
- String secondaryCpuAbiFromSettings = null;
- boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
-
- // writer
- synchronized (mPackages) {
- if (pkg.mSharedUserId != null) {
- // SIDE EFFECTS; may potentially allocate a new shared user
- suid = mSettings.getSharedUserLPw(
- pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
- if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
- Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + suid.userId
- + "): packages=" + suid.packages);
- }
- }
-
- // Check if we are renaming from an original package name.
- PackageSetting origPackage = null;
- String realName = null;
- if (pkg.mOriginalPackages != null) {
- // This package may need to be renamed to a previously
- // installed name. Let's check on that...
- final String renamed = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
- if (pkg.mOriginalPackages.contains(renamed)) {
- // This package had originally been installed as the
- // original name, and we have already taken care of
- // transitioning to the new one. Just update the new
- // one to continue using the old name.
- realName = pkg.mRealPackage;
- if (!pkg.packageName.equals(renamed)) {
- // Callers into this function may have already taken
- // care of renaming the package; only do it here if
- // it is not already done.
- pkg.setPackageName(renamed);
- }
- } else {
- for (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) {
- if ((origPackage = mSettings.getPackageLPr(
- pkg.mOriginalPackages.get(i))) != null) {
- // We do have the package already installed under its
- // original name... should we use it?
- if (!verifyPackageUpdateLPr(origPackage, pkg)) {
- // New package is not compatible with original.
- origPackage = null;
- continue;
- } else if (origPackage.sharedUser != null) {
- // Make sure uid is compatible between packages.
- if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) {
- Slog.w(TAG, "Unable to migrate data from " + origPackage.name
- + " to " + pkg.packageName + ": old uid "
- + origPackage.sharedUser.name
- + " differs from " + pkg.mSharedUserId);
- origPackage = null;
- continue;
- }
- // TODO: Add case when shared user id is added [b/28144775]
- } else {
- if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
- + pkg.packageName + " to old name " + origPackage.name);
- }
- break;
- }
- }
- }
- }
-
- if (mTransferedPackages.contains(pkg.packageName)) {
- Slog.w(TAG, "Package " + pkg.packageName
- + " was transferred to another, but its .apk remains");
- }
-
- // See comments in nonMutatedPs declaration
- if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
- PackageSetting foundPs = mSettings.getPackageLPr(pkg.packageName);
- if (foundPs != null) {
- nonMutatedPs = new PackageSetting(foundPs);
- }
- }
-
- if (!needToDeriveAbi) {
- PackageSetting foundPs = mSettings.getPackageLPr(pkg.packageName);
- if (foundPs != null) {
- primaryCpuAbiFromSettings = foundPs.primaryCpuAbiString;
- secondaryCpuAbiFromSettings = foundPs.secondaryCpuAbiString;
- } else {
- // when re-adding a system package failed after uninstalling updates.
- needToDeriveAbi = true;
- }
- }
-
- pkgSetting = mSettings.getPackageLPr(pkg.packageName);
- if (pkgSetting != null && pkgSetting.sharedUser != suid) {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Package " + pkg.packageName + " shared user changed from "
- + (pkgSetting.sharedUser != null
- ? pkgSetting.sharedUser.name : "<nothing>")
- + " to "
- + (suid != null ? suid.name : "<nothing>")
- + "; replacing with new");
- pkgSetting = null;
- }
- final PackageSetting oldPkgSetting =
- pkgSetting == null ? null : new PackageSetting(pkgSetting);
- final PackageSetting disabledPkgSetting =
- mSettings.getDisabledSystemPkgLPr(pkg.packageName);
-
- String[] usesStaticLibraries = null;
- if (pkg.usesStaticLibraries != null) {
- usesStaticLibraries = new String[pkg.usesStaticLibraries.size()];
- pkg.usesStaticLibraries.toArray(usesStaticLibraries);
- }
-
- if (pkgSetting == null) {
- final String parentPackageName = (pkg.parentPackage != null)
- ? pkg.parentPackage.packageName : null;
- final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
- final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
- // REMOVE SharedUserSetting from method; update in a separate call
- pkgSetting = Settings.createNewSetting(pkg.packageName, origPackage,
- disabledPkgSetting, realName, suid, destCodeFile, destResourceFile,
- pkg.applicationInfo.nativeLibraryRootDir, pkg.applicationInfo.primaryCpuAbi,
- pkg.applicationInfo.secondaryCpuAbi, pkg.getLongVersionCode(),
- pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags, user,
- true /*allowInstall*/, instantApp, virtualPreload,
- parentPackageName, pkg.getChildPackageNames(),
- UserManagerService.getInstance(), usesStaticLibraries,
- pkg.usesStaticLibrariesVersions);
- // SIDE EFFECTS; updates system state; move elsewhere
- if (origPackage != null) {
- mSettings.addRenamedPackageLPw(pkg.packageName, origPackage.name);
- }
- mSettings.addUserToSettingLPw(pkgSetting);
- } else {
- // REMOVE SharedUserSetting from method; update in a separate call.
- //
- // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
- // secondaryCpuAbi are not known at this point so we always update them
- // to null here, only to reset them at a later point.
- Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, suid, destCodeFile,
- pkg.applicationInfo.nativeLibraryDir, pkg.applicationInfo.primaryCpuAbi,
- pkg.applicationInfo.secondaryCpuAbi, pkg.applicationInfo.flags,
- pkg.applicationInfo.privateFlags, pkg.getChildPackageNames(),
- UserManagerService.getInstance(), usesStaticLibraries,
- pkg.usesStaticLibrariesVersions);
- }
- // SIDE EFFECTS; persists system state to files on disk; move elsewhere
- mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);
-
- // SIDE EFFECTS; modifies system state; move elsewhere
- if (pkgSetting.origPackage != null) {
- // If we are first transitioning from an original package,
- // fix up the new package's name now. We need to do this after
- // looking up the package under its new name, so getPackageLP
- // can take care of fiddling things correctly.
- pkg.setPackageName(origPackage.name);
-
- // File a report about this.
- String msg = "New package " + pkgSetting.realName
- + " renamed to replace old package " + pkgSetting.name;
- reportSettingsProblem(Log.WARN, msg);
-
- // Make a note of it.
- if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
- mTransferedPackages.add(origPackage.name);
- }
-
- // No longer need to retain this.
- pkgSetting.origPackage = null;
- }
-
- // SIDE EFFECTS; modifies system state; move elsewhere
- if ((scanFlags & SCAN_CHECK_ONLY) == 0 && realName != null) {
- // Make a note of it.
- mTransferedPackages.add(pkg.packageName);
- }
-
- if (mSettings.isDisabledSystemPackageLPr(pkg.packageName)) {
- pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
- }
-
- if ((scanFlags & SCAN_BOOTING) == 0
- && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
- // Check all shared libraries and map to their actual file path.
- // We only do this here for apps not on a system dir, because those
- // are the only ones that can fail an install due to this. We
- // will take care of the system apps by updating all of their
- // library paths after the scan is done. Also during the initial
- // scan don't update any libs as we do this wholesale after all
- // apps are scanned to avoid dependency based scanning.
- updateSharedLibrariesLPr(pkg, null);
- }
-
- if (mFoundPolicyFile) {
- SELinuxMMAC.assignSeInfoValue(pkg);
- }
- pkg.applicationInfo.uid = pkgSetting.appId;
- pkg.mExtras = pkgSetting;
-
-
- // Static shared libs have same package with different versions where
- // we internally use a synthetic package name to allow multiple versions
- // of the same package, therefore we need to compare signatures against
- // the package setting for the latest library version.
- PackageSetting signatureCheckPs = pkgSetting;
- if (pkg.applicationInfo.isStaticSharedLibrary()) {
- SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
- if (libraryEntry != null) {
- signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
- }
- }
-
- final KeySetManagerService ksms = mSettings.mKeySetManagerService;
- if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
- if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
- // We just determined the app is signed correctly, so bring
- // over the latest parsed certs.
- pkgSetting.signatures.mSignatures = pkg.mSignatures;
- } else {
- if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
- throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "Package " + pkg.packageName + " upgrade keys do not match the "
- + "previously installed version");
- } else {
- pkgSetting.signatures.mSignatures = pkg.mSignatures;
- String msg = "System package " + pkg.packageName
- + " signature changed; retaining data.";
- reportSettingsProblem(Log.WARN, msg);
- }
- }
- } else {
- try {
- final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg);
- final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg);
- final boolean compatMatch = verifySignatures(signatureCheckPs, pkg.mSignatures,
- compareCompat, compareRecover);
- // The new KeySets will be re-added later in the scanning process.
- if (compatMatch) {
- synchronized (mPackages) {
- ksms.removeAppKeySetDataLPw(pkg.packageName);
- }
- }
- // We just determined the app is signed correctly, so bring
- // over the latest parsed certs.
- pkgSetting.signatures.mSignatures = pkg.mSignatures;
- } catch (PackageManagerException e) {
- if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
- throw e;
- }
- // The signature has changed, but this package is in the system
- // image... let's recover!
- pkgSetting.signatures.mSignatures = pkg.mSignatures;
- // However... if this package is part of a shared user, but it
- // doesn't match the signature of the shared user, let's fail.
- // What this means is that you can't change the signatures
- // associated with an overall shared user, which doesn't seem all
- // that unreasonable.
- if (signatureCheckPs.sharedUser != null) {
- if (compareSignatures(signatureCheckPs.sharedUser.signatures.mSignatures,
- pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
- throw new PackageManagerException(
- INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
- "Signature mismatch for shared user: "
- + pkgSetting.sharedUser);
- }
- }
- // File a report about this.
- String msg = "System package " + pkg.packageName
- + " signature changed; retaining data.";
- reportSettingsProblem(Log.WARN, msg);
- }
- }
-
- if ((scanFlags & SCAN_CHECK_ONLY) == 0 && pkg.mAdoptPermissions != null) {
- // This package wants to adopt ownership of permissions from
- // another package.
- for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {
- final String origName = pkg.mAdoptPermissions.get(i);
- final PackageSetting orig = mSettings.getPackageLPr(origName);
- if (orig != null) {
- if (verifyPackageUpdateLPr(orig, pkg)) {
- Slog.i(TAG, "Adopting permissions from " + origName + " to "
- + pkg.packageName);
- // SIDE EFFECTS; updates permissions system state; move elsewhere
- mSettings.mPermissions.transferPermissions(origName, pkg.packageName);
- }
- }
- }
- }
- }
-
- pkg.applicationInfo.processName = fixProcessName(
- pkg.applicationInfo.packageName,
- pkg.applicationInfo.processName);
-
- if (pkg != mPlatformPackage) {
- // Get all of our default paths setup
- pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
- }
-
- final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
-
- if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
- if (needToDeriveAbi) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
- final boolean extractNativeLibs = !pkg.isLibrary();
- derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs, mAppLib32InstallDir);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-
- // Some system apps still use directory structure for native libraries
- // in which case we might end up not detecting abi solely based on apk
- // structure. Try to detect abi based on directory structure.
- if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
- pkg.applicationInfo.primaryCpuAbi == null) {
- setBundledAppAbisAndRoots(pkg, pkgSetting);
- setNativeLibraryPaths(pkg, mAppLib32InstallDir);
- }
- } else {
- // This is not a first boot or an upgrade, don't bother deriving the
- // ABI during the scan. Instead, trust the value that was stored in the
- // package setting.
- pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings;
- pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings;
-
- setNativeLibraryPaths(pkg, mAppLib32InstallDir);
-
- if (DEBUG_ABI_SELECTION) {
- Slog.i(TAG, "Using ABIS and native lib paths from settings : " +
- pkg.packageName + " " + pkg.applicationInfo.primaryCpuAbi + ", " +
- pkg.applicationInfo.secondaryCpuAbi);
- }
- }
- } else {
- if ((scanFlags & SCAN_MOVE) != 0) {
- // We haven't run dex-opt for this move (since we've moved the compiled output too)
- // but we already have this packages package info in the PackageSetting. We just
- // use that and derive the native library path based on the new codepath.
- pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;
- pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString;
- }
-
- // Set native library paths again. For moves, the path will be updated based on the
- // ABIs we've determined above. For non-moves, the path will be updated based on the
- // ABIs we determined during compilation, but the path will depend on the final
- // package path (after the rename away from the stage path).
- setNativeLibraryPaths(pkg, mAppLib32InstallDir);
- }
-
- // This is a special case for the "system" package, where the ABI is
- // dictated by the zygote configuration (and init.rc). We should keep track
- // of this ABI so that we can deal with "normal" applications that run under
- // the same UID correctly.
- if (mPlatformPackage == pkg) {
- pkg.applicationInfo.primaryCpuAbi = VMRuntime.getRuntime().is64Bit() ?
- Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
- }
-
- // If there's a mismatch between the abi-override in the package setting
- // and the abiOverride specified for the install. Warn about this because we
- // would've already compiled the app without taking the package setting into
- // account.
- if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
- if (cpuAbiOverride == null && pkgSetting.cpuAbiOverrideString != null) {
- Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
- " for package " + pkg.packageName);
- }
- }
-
- pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
- pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
- pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
-
- // Copy the derived override back to the parsed package, so that we can
- // update the package settings accordingly.
- pkg.cpuAbiOverride = cpuAbiOverride;
-
- if (DEBUG_ABI_SELECTION) {
- Slog.d(TAG, "Resolved nativeLibraryRoot for " + pkg.applicationInfo.packageName
- + " to root=" + pkg.applicationInfo.nativeLibraryRootDir + ", isa="
- + pkg.applicationInfo.nativeLibraryRootRequiresIsa);
- }
-
- // Push the derived path down into PackageSettings so we know what to
- // clean up at uninstall time.
- pkgSetting.legacyNativeLibraryPathString = pkg.applicationInfo.nativeLibraryRootDir;
-
- if (DEBUG_ABI_SELECTION) {
- Log.d(TAG, "Abis for package[" + pkg.packageName + "] are" +
- " primary=" + pkg.applicationInfo.primaryCpuAbi +
- " secondary=" + pkg.applicationInfo.secondaryCpuAbi);
- }
-
- // SIDE EFFECTS; removes DEX files from disk; move elsewhere
- if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
- // We don't do this here during boot because we can do it all
- // at once after scanning all existing packages.
- //
- // We also do this *before* we perform dexopt on this package, so that
- // we can avoid redundant dexopts, and also to make sure we've got the
- // code and package path correct.
- adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, pkg);
- }
-
- if (mFactoryTest && pkg.requestedPermissions.contains(
- android.Manifest.permission.FACTORY_TEST)) {
- pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
- }
-
- if (isSystemApp(pkg)) {
- pkgSetting.isOrphaned = true;
- }
-
- // Take care of first install / last update times.
- final long scanFileTime = getLastModifiedTime(pkg);
- if (currentTime != 0) {
- if (pkgSetting.firstInstallTime == 0) {
- pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
- } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
- pkgSetting.lastUpdateTime = currentTime;
- }
- } else if (pkgSetting.firstInstallTime == 0) {
- // We need *something*. Take time time stamp of the file.
- pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
- } else if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
- if (scanFileTime != pkgSetting.timeStamp) {
- // A package on the system image has changed; consider this
- // to be an update.
- pkgSetting.lastUpdateTime = scanFileTime;
- }
- }
- pkgSetting.setTimeStamp(scanFileTime);
-
- if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
- if (nonMutatedPs != null) {
- synchronized (mPackages) {
- mSettings.mPackages.put(nonMutatedPs.name, nonMutatedPs);
- }
- }
- } else {
- final int userId = user == null ? 0 : user.getIdentifier();
- // Modify state for the given package setting
- commitPackageSettings(pkg, pkgSetting, user, scanFlags,
- (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);
- if (pkgSetting.getInstantApp(userId)) {
- mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
- }
- }
- return pkg;
- }
-
/**
* Applies policy to the parsed package based upon the given policy flags.
* Ensures the package is in a good state.
@@ -10191,7 +10342,7 @@
* Implementation detail: This method must NOT have any side effect. It would
* ideally be static, but, it requires locks to read system state.
*/
- private void applyPolicy(PackageParser.Package pkg, final @ParseFlags int parseFlags,
+ private static void applyPolicy(PackageParser.Package pkg, final @ParseFlags int parseFlags,
final @ScanFlags int scanFlags) {
if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
@@ -10580,8 +10731,7 @@
* be available for query, resolution, etc...
*/
private void commitPackageSettings(PackageParser.Package pkg, PackageSetting pkgSetting,
- UserHandle user, final @ScanFlags int scanFlags, boolean chatty)
- throws PackageManagerException {
+ UserHandle user, final @ScanFlags int scanFlags, boolean chatty) {
final String pkgName = pkg.packageName;
if (mCustomResolverComponentName != null &&
mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
@@ -10934,11 +11084,11 @@
* If {@code extractLibs} is true, native libraries are extracted from the app if required.
*/
private static void derivePackageAbi(PackageParser.Package pkg, String cpuAbiOverride,
- boolean extractLibs, File appLib32InstallDir)
+ boolean extractLibs)
throws PackageManagerException {
// Give ourselves some initial paths; we'll come back for another
// pass once we've determined ABI below.
- setNativeLibraryPaths(pkg, appLib32InstallDir);
+ setNativeLibraryPaths(pkg, sAppLib32InstallDir);
// We would never need to extract libs for forward-locked and external packages,
// since the container service will do it for us. We shouldn't attempt to
@@ -11088,7 +11238,7 @@
// Now that we've calculated the ABIs and determined if it's an internal app,
// we will go ahead and populate the nativeLibraryPath.
- setNativeLibraryPaths(pkg, appLib32InstallDir);
+ setNativeLibraryPaths(pkg, sAppLib32InstallDir);
}
/**
@@ -11104,8 +11254,9 @@
* NOTE: We currently only match for the primary CPU abi string. Matching the secondary
* adds unnecessary complexity.
*/
- private void adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser,
- PackageParser.Package scannedPackage) {
+ private static @Nullable List<String> adjustCpuAbisForSharedUserLPw(
+ Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage) {
+ List<String> changedAbiCodePath = null;
String requiredInstructionSet = null;
if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) {
requiredInstructionSet = VMRuntime.getInstructionSet(
@@ -11175,15 +11326,15 @@
+ (scannedPackage != null ? scannedPackage : "null")
+ ")");
}
- try {
- mInstaller.rmdex(ps.codePathString,
- getDexCodeInstructionSet(getPreferredInstructionSet()));
- } catch (InstallerException ignored) {
+ if (changedAbiCodePath == null) {
+ changedAbiCodePath = new ArrayList<>();
}
+ changedAbiCodePath.add(ps.codePathString);
}
}
}
}
+ return changedAbiCodePath;
}
private void setUpCustomResolverActivity(PackageParser.Package pkg) {
@@ -12408,7 +12559,12 @@
out.print(' ');
filter.service.printComponentShortName(out);
out.print(" filter ");
- out.println(Integer.toHexString(System.identityHashCode(filter)));
+ out.print(Integer.toHexString(System.identityHashCode(filter)));
+ if (filter.service.info.permission != null) {
+ out.print(" permission "); out.println(filter.service.info.permission);
+ } else {
+ out.println();
+ }
}
@Override
@@ -16283,6 +16439,16 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
+ // App targetSdkVersion is below min supported version
+ if (!forceSdk && pkg.applicationInfo.isTargetingDeprecatedSdkVersion()) {
+ Slog.w(TAG, "App " + pkg.packageName + " targets deprecated sdk");
+
+ res.setError(INSTALL_FAILED_NEWER_SDK,
+ "App is targeting deprecated sdk (targetSdkVersion should be at least "
+ + Build.VERSION.MIN_SUPPORTED_TARGET_SDK_INT + ").");
+ return;
+ }
+
// Instant apps must have target SDK >= O and have targetSanboxVersion >= 2
if (instantApp && pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
Slog.w(TAG, "Instant app package " + pkg.packageName + " does not target O");
@@ -16420,6 +16586,13 @@
+ " target SDK " + oldTargetSdk + " does.");
return;
}
+ // Prevent persistent apps from being updated
+ if ((oldPackage.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0) {
+ res.setError(PackageManager.INSTALL_FAILED_INVALID_APK,
+ "Package " + oldPackage.packageName + " is a persistent app. "
+ + "Persistent apps are not updateable.");
+ return;
+ }
// Prevent apps from downgrading their targetSandbox.
final int oldTargetSandbox = oldPackage.applicationInfo.targetSandboxVersion;
final int newTargetSandbox = pkg.applicationInfo.targetSandboxVersion;
@@ -16603,7 +16776,7 @@
String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?
args.abiOverride : pkg.cpuAbiOverride);
final boolean extractNativeLibs = !pkg.isLibrary();
- derivePackageAbi(pkg, abiOverride, extractNativeLibs, mAppLib32InstallDir);
+ derivePackageAbi(pkg, abiOverride, extractNativeLibs);
} catch (PackageManagerException pme) {
Slog.e(TAG, "Error deriving application ABI", pme);
res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
@@ -16929,7 +17102,7 @@
return name.startsWith("vmdl") && name.endsWith(".tmp");
}
};
- for (File file : mDrmAppPrivateInstallDir.listFiles(filter)) {
+ for (File file : sDrmAppPrivateInstallDir.listFiles(filter)) {
file.delete();
}
}
@@ -20384,6 +20557,7 @@
pw.println(" dexopt: dump dexopt state");
pw.println(" compiler-stats: dump compiler statistics");
pw.println(" enabled-overlays: dump list of enabled overlay packages");
+ pw.println(" service-permissions: dump permissions required by services");
pw.println(" <package.name>: info about given package");
return;
} else if ("--checkin".equals(opt)) {
@@ -20519,6 +20693,8 @@
dumpState.setDump(DumpState.DUMP_COMPILER_STATS);
} else if ("changes".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_CHANGES);
+ } else if ("service-permissions".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS);
} else if ("write".equals(cmd)) {
synchronized (mPackages) {
mSettings.writeLPr();
@@ -20899,6 +21075,25 @@
ipw.decreaseIndent();
}
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
+ && packageName == null) {
+ if (dumpState.onTitlePrinted()) pw.println();
+ pw.println("Service permissions:");
+
+ final Iterator<ServiceIntentInfo> filterIterator = mServices.filterIterator();
+ while (filterIterator.hasNext()) {
+ final ServiceIntentInfo info = filterIterator.next();
+ final ServiceInfo serviceInfo = info.service.info;
+ final String permission = serviceInfo.permission;
+ if (permission != null) {
+ pw.print(" ");
+ pw.print(serviceInfo.getComponentName().flattenToShortString());
+ pw.print(": ");
+ pw.println(permission);
+ }
+ }
+ }
+
if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
if (dumpState.onTitlePrinted()) pw.println();
dumpDexoptStateLPr(pw, packageName);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index a7cced7..2d82c46 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1374,7 +1374,7 @@
}
ClearDataObserver obs = new ClearDataObserver();
- ActivityManager.getService().clearApplicationUserData(pkg, obs, userId);
+ ActivityManager.getService().clearApplicationUserData(pkg, false, obs, userId);
synchronized (obs) {
while (!obs.finished) {
try {
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 9733624..809e16c 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -131,7 +131,6 @@
* using the full set of code paths when the package's process is started.
*/
Set<String> oldCodePaths;
- PackageSettingBase origPackage;
/** Package name of the app that installed this package */
String installerPackageName;
@@ -263,7 +262,6 @@
lastUpdateTime = orig.lastUpdateTime;
legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString;
// Intentionally skip oldCodePaths; it's not relevant for copies
- origPackage = orig.origPackage;
parentPackageName = orig.parentPackageName;
primaryCpuAbiString = orig.primaryCpuAbiString;
resourcePath = orig.resourcePath;
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index f0ce3c9..fbf3d82 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -59,6 +59,8 @@
// All policy stanzas read from mac_permissions.xml. This is also the lock
// to synchronize access during policy load and access attempts.
private static List<Policy> sPolicies = new ArrayList<>();
+ /** Whether or not the policy files have been read */
+ private static boolean sPolicyRead;
/** Path to MAC permissions on system image */
private static final File[] MAC_PERMISSIONS =
@@ -88,6 +90,12 @@
* were loaded successfully; no partial loading is possible.
*/
public static boolean readInstallPolicy() {
+ synchronized (sPolicies) {
+ if (sPolicyRead) {
+ return true;
+ }
+ }
+
// Temp structure to hold the rules while we parse the xml file
List<Policy> policies = new ArrayList<>();
@@ -142,7 +150,9 @@
}
synchronized (sPolicies) {
- sPolicies = policies;
+ sPolicies.clear();
+ sPolicies.addAll(policies);
+ sPolicyRead = true;
if (DEBUG_POLICY_ORDER) {
for (Policy policy : sPolicies) {
@@ -280,6 +290,12 @@
*/
public static void assignSeInfoValue(PackageParser.Package pkg) {
synchronized (sPolicies) {
+ if (!sPolicyRead) {
+ if (DEBUG_POLICY) {
+ Slog.d(TAG, "Policy not read");
+ }
+ return;
+ }
for (Policy policy : sPolicies) {
String seInfo = policy.getMatchedSeInfo(pkg);
if (seInfo != null) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 4a5772f..648f847 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -687,7 +687,6 @@
(childPkgNames != null) ? new ArrayList<>(childPkgNames) : null;
pkgSetting.codePath = codePath;
pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
- pkgSetting.origPackage = originalPkg;
pkgSetting.parentPackageName = parentPkgName;
pkgSetting.pkgFlags = pkgFlags;
pkgSetting.pkgPrivateFlags = pkgPrivateFlags;
diff --git a/services/core/java/com/android/server/pm/ShortcutNonPersistentUser.java b/services/core/java/com/android/server/pm/ShortcutNonPersistentUser.java
new file mode 100644
index 0000000..7f6f684
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShortcutNonPersistentUser.java
@@ -0,0 +1,98 @@
+/*
+ * 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.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.server.pm.ShortcutService.DumpFilter;
+
+import java.io.PrintWriter;
+
+/**
+ * This class holds per-user information for {@link ShortcutService} that doesn't have to be
+ * persisted and is kept in-memory.
+ *
+ * The access to it must be guarded with the shortcut manager lock.
+ */
+public class ShortcutNonPersistentUser {
+ private final ShortcutService mService;
+
+ private final int mUserId;
+
+ /**
+ * Keep track of additional packages that other parts of the system have said are
+ * allowed to access shortcuts. The key is the part of the system it came from,
+ * the value is the package name that has access. We don't persist these because
+ * at boot all relevant system services will push this data back to us they do their
+ * normal evaluation of the state of the world.
+ */
+ private final ArrayMap<String, String> mHostPackages = new ArrayMap<>();
+
+ /**
+ * Set of package name values from above.
+ */
+ private final ArraySet<String> mHostPackageSet = new ArraySet<>();
+
+ public ShortcutNonPersistentUser(ShortcutService service, int userId) {
+ mService = service;
+ mUserId = userId;
+ }
+
+ public int getUserId() {
+ return mUserId;
+ }
+
+ public void setShortcutHostPackage(@NonNull String type, @Nullable String packageName) {
+ if (packageName != null) {
+ mHostPackages.put(type, packageName);
+ } else {
+ mHostPackages.remove(type);
+ }
+
+ mHostPackageSet.clear();
+ for (int i = 0; i < mHostPackages.size(); i++) {
+ mHostPackageSet.add(mHostPackages.valueAt(i));
+ }
+ }
+
+ public boolean hasHostPackage(@NonNull String packageName) {
+ return mHostPackageSet.contains(packageName);
+ }
+
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) {
+ if (filter.shouldDumpDetails()) {
+ if (mHostPackages.size() > 0) {
+ pw.print(prefix);
+ pw.print("Non-persistent: user ID:");
+ pw.println(mUserId);
+
+ pw.print(prefix);
+ pw.println(" Host packages:");
+ for (int i = 0; i < mHostPackages.size(); i++) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print(mHostPackages.keyAt(i));
+ pw.print(": ");
+ pw.println(mHostPackages.valueAt(i));
+ }
+ pw.println();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
index b14e9c9..3d37229 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -148,7 +148,7 @@
if (!anyVersionOkay && (currentPackage.getLongVersionCode() < mBackupSourceVersionCode)) {
Slog.w(TAG, String.format(
"Can't restore: package current version %d < backed up version %d",
- currentPackage.versionCode, mBackupSourceVersionCode));
+ currentPackage.getLongVersionCode(), mBackupSourceVersionCode));
return ShortcutInfo.DISABLED_REASON_VERSION_LOWER;
}
return ShortcutInfo.DISABLED_REASON_NOT_DISABLED;
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index ee2f374..065eafd 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -280,6 +280,13 @@
private final SparseArray<ShortcutUser> mUsers = new SparseArray<>();
/**
+ * User ID -> ShortcutNonPersistentUser
+ */
+ @GuardedBy("mLock")
+ private final SparseArray<ShortcutNonPersistentUser> mShortcutNonPersistentUsers =
+ new SparseArray<>();
+
+ /**
* Max number of dynamic + manifest shortcuts that each application can have at a time.
*/
private int mMaxShortcuts;
@@ -330,7 +337,10 @@
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_UNINSTALLED_PACKAGES;
- @GuardedBy("mLock")
+ /**
+ * Note we use a fine-grained lock for {@link #mUnlockedUsers} due to b/64303666.
+ */
+ @GuardedBy("mUnlockedUsers")
final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray();
// Stats
@@ -600,7 +610,7 @@
if (DEBUG) {
Slog.d(TAG, "handleUnlockUser: user=" + userId);
}
- synchronized (mLock) {
+ synchronized (mUnlockedUsers) {
mUnlockedUsers.put(userId, true);
}
@@ -628,7 +638,9 @@
synchronized (mLock) {
unloadUserLocked(userId);
- mUnlockedUsers.put(userId, false);
+ synchronized (mUnlockedUsers) {
+ mUnlockedUsers.put(userId, false);
+ }
}
}
@@ -1149,9 +1161,12 @@
// Requires mLock held, but "Locked" prefix would look weired so we just say "L".
protected boolean isUserUnlockedL(@UserIdInt int userId) {
// First, check the local copy.
- if (mUnlockedUsers.get(userId)) {
- return true;
+ synchronized (mUnlockedUsers) {
+ if (mUnlockedUsers.get(userId)) {
+ return true;
+ }
}
+
// If the local copy says the user is locked, check with AM for the actual state, since
// the user might just have been unlocked.
// Note we just don't use isUserUnlockingOrUnlocked() here, because it'll return false
@@ -1199,6 +1214,18 @@
return userPackages;
}
+ /** Return the non-persistent per-user state. */
+ @GuardedBy("mLock")
+ @NonNull
+ ShortcutNonPersistentUser getNonPersistentUserLocked(@UserIdInt int userId) {
+ ShortcutNonPersistentUser ret = mShortcutNonPersistentUsers.get(userId);
+ if (ret == null) {
+ ret = new ShortcutNonPersistentUser(this, userId);
+ mShortcutNonPersistentUsers.put(userId, ret);
+ }
+ return ret;
+ }
+
void forEachLoadedUserLocked(@NonNull Consumer<ShortcutUser> c) {
for (int i = mUsers.size() - 1; i >= 0; i--) {
c.accept(mUsers.valueAt(i));
@@ -2251,7 +2278,7 @@
return true;
}
synchronized (mLock) {
- return getUserShortcutsLocked(userId).hasHostPackage(callingPackage);
+ return getNonPersistentUserLocked(userId).hasHostPackage(callingPackage);
}
}
@@ -2375,10 +2402,7 @@
public void setShortcutHostPackage(@NonNull String type, @Nullable String packageName,
int userId) {
synchronized (mLock) {
- throwIfUserLockedL(userId);
-
- final ShortcutUser user = getUserShortcutsLocked(userId);
- user.setShortcutHostPackage(type, packageName);
+ getNonPersistentUserLocked(userId).setShortcutHostPackage(type, packageName);
}
}
@@ -3836,6 +3860,14 @@
pw.println();
}
}
+
+ for (int i = 0; i < mShortcutNonPersistentUsers.size(); i++) {
+ final ShortcutNonPersistentUser user = mShortcutNonPersistentUsers.valueAt(i);
+ if (filter.isUserMatch(user.getUserId())) {
+ user.dump(pw, " ", filter);
+ pw.println();
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 1efd765..b7247df 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -124,20 +124,6 @@
/** In-memory-cached default launcher. */
private ComponentName mCachedLauncher;
- /**
- * Keep track of additional packages that other parts of the system have said are
- * allowed to access shortcuts. The key is the part of the system it came from,
- * the value is the package name that has access. We don't persist these because
- * at boot all relevant system services will push this data back to us they do their
- * normal evaluation of the state of the world.
- */
- private final ArrayMap<String, String> mHostPackages = new ArrayMap<>();
-
- /**
- * Set of package name values from above.
- */
- private final ArraySet<String> mHostPackageSet = new ArraySet<>();
-
private String mKnownLocales;
private long mLastAppScanTime;
@@ -482,23 +468,6 @@
return mCachedLauncher;
}
- public void setShortcutHostPackage(@NonNull String type, @Nullable String packageName) {
- if (packageName != null) {
- mHostPackages.put(type, packageName);
- } else {
- mHostPackages.remove(type);
- }
-
- mHostPackageSet.clear();
- for (int i = 0; i < mHostPackages.size(); i++) {
- mHostPackageSet.add(mHostPackages.valueAt(i));
- }
- }
-
- public boolean hasHostPackage(@NonNull String packageName) {
- return mHostPackageSet.contains(packageName);
- }
-
public void resetThrottling() {
for (int i = mPackages.size() - 1; i >= 0; i--) {
mPackages.valueAt(i).resetThrottling();
@@ -587,18 +556,6 @@
pw.print("Last known launcher: ");
pw.print(mLastKnownLauncher);
pw.println();
-
- if (mHostPackages.size() > 0) {
- pw.print(prefix);
- pw.println("Host packages:");
- for (int i = 0; i < mHostPackages.size(); i++) {
- pw.print(prefix);
- pw.print(" ");
- pw.print(mHostPackages.keyAt(i));
- pw.print(": ");
- pw.println(mHostPackages.valueAt(i));
- }
- }
}
for (int i = 0; i < mLaunchers.size(); i++) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index dc481ca..03cd4f1 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3702,8 +3702,10 @@
}
@Override
- public UserInfo createUserEvenWhenDisallowed(String name, int flags) {
- UserInfo user = createUserInternalUnchecked(name, flags, UserHandle.USER_NULL, null);
+ public UserInfo createUserEvenWhenDisallowed(String name, int flags,
+ String[] disallowedPackages) {
+ UserInfo user = createUserInternalUnchecked(name, flags, UserHandle.USER_NULL,
+ disallowedPackages);
// Keep this in sync with UserManager.createUser
if (user != null && !user.isAdmin() && !user.isDemo()) {
setUserRestriction(UserManager.DISALLOW_SMS, true, user.id);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 4b13404..4564988 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -113,7 +113,8 @@
UserManager.DISALLOW_OEM_UNLOCK,
UserManager.DISALLOW_UNMUTE_DEVICE,
UserManager.DISALLOW_AUTOFILL,
- UserManager.DISALLOW_USER_SWITCH
+ UserManager.DISALLOW_USER_SWITCH,
+ UserManager.DISALLOW_UNIFIED_PASSWORD,
});
/**
@@ -192,7 +193,7 @@
UserManager.DISALLOW_BLUETOOTH_SHARING
);
- /*
+ /**
* Special user restrictions that are always applied to all users no matter who sets them.
*/
private static final Set<String> PROFILE_GLOBAL_RESTRICTIONS = Sets.newArraySet(
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 61591bb..9752a57 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -321,11 +321,6 @@
static final int LONG_PRESS_BACK_NOTHING = 0;
static final int LONG_PRESS_BACK_GO_TO_VOICE_ASSIST = 1;
- // Number of presses needed before we induce panic press behavior on the back button
- static final int PANIC_PRESS_BACK_COUNT = 4;
- static final int PANIC_PRESS_BACK_NOTHING = 0;
- static final int PANIC_PRESS_BACK_HOME = 1;
-
// These need to match the documentation/constant in
// core/res/res/values/config.xml
static final int LONG_PRESS_HOME_NOTHING = 0;
@@ -520,7 +515,6 @@
volatile boolean mBackKeyHandled;
volatile boolean mBeganFromNonInteractive;
volatile int mPowerKeyPressCounter;
- volatile int mBackKeyPressCounter;
volatile boolean mEndCallKeyHandled;
volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
volatile boolean mGoingToSleep;
@@ -582,7 +576,6 @@
int mDoublePressOnPowerBehavior;
int mTriplePressOnPowerBehavior;
int mLongPressOnBackBehavior;
- int mPanicPressOnBackBehavior;
int mShortPressOnSleepBehavior;
int mShortPressOnWindowBehavior;
volatile boolean mAwake;
@@ -800,16 +793,15 @@
private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 17;
private static final int MSG_BACK_LONG_PRESS = 18;
private static final int MSG_DISPOSE_INPUT_CONSUMER = 19;
- private static final int MSG_BACK_DELAYED_PRESS = 20;
- private static final int MSG_ACCESSIBILITY_SHORTCUT = 21;
- private static final int MSG_BUGREPORT_TV = 22;
- private static final int MSG_ACCESSIBILITY_TV = 23;
- private static final int MSG_DISPATCH_BACK_KEY_TO_AUTOFILL = 24;
- private static final int MSG_SYSTEM_KEY_PRESS = 25;
- private static final int MSG_HANDLE_ALL_APPS = 26;
- private static final int MSG_LAUNCH_ASSIST = 27;
- private static final int MSG_LAUNCH_ASSIST_LONG_PRESS = 28;
- private static final int MSG_POWER_VERY_LONG_PRESS = 29;
+ private static final int MSG_ACCESSIBILITY_SHORTCUT = 20;
+ private static final int MSG_BUGREPORT_TV = 21;
+ private static final int MSG_ACCESSIBILITY_TV = 22;
+ private static final int MSG_DISPATCH_BACK_KEY_TO_AUTOFILL = 23;
+ private static final int MSG_SYSTEM_KEY_PRESS = 24;
+ private static final int MSG_HANDLE_ALL_APPS = 25;
+ private static final int MSG_LAUNCH_ASSIST = 26;
+ private static final int MSG_LAUNCH_ASSIST_LONG_PRESS = 27;
+ private static final int MSG_POWER_VERY_LONG_PRESS = 28;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
@@ -887,15 +879,10 @@
break;
case MSG_BACK_LONG_PRESS:
backLongPress();
- finishBackKeyPress();
break;
case MSG_DISPOSE_INPUT_CONSUMER:
disposeInputConsumer((InputConsumer) msg.obj);
break;
- case MSG_BACK_DELAYED_PRESS:
- backMultiPressAction(msg.arg1);
- finishBackKeyPress();
- break;
case MSG_ACCESSIBILITY_SHORTCUT:
accessibilityShortcutActivated();
break;
@@ -1181,14 +1168,6 @@
// Reset back key state for long press
mBackKeyHandled = false;
- // Cancel multi-press detection timeout.
- if (hasPanicPressOnBackBehavior()) {
- if (mBackKeyPressCounter != 0
- && mBackKeyPressCounter < PANIC_PRESS_BACK_COUNT) {
- mHandler.removeMessages(MSG_BACK_DELAYED_PRESS);
- }
- }
-
if (hasLongPressOnBackBehavior()) {
Message msg = mHandler.obtainMessage(MSG_BACK_LONG_PRESS);
msg.setAsynchronous(true);
@@ -1202,21 +1181,6 @@
// Cache handled state
boolean handled = mBackKeyHandled;
- if (hasPanicPressOnBackBehavior()) {
- // Check for back key panic press
- ++mBackKeyPressCounter;
-
- final long eventTime = event.getDownTime();
-
- if (mBackKeyPressCounter <= PANIC_PRESS_BACK_COUNT) {
- // This could be a multi-press. Wait a little bit longer to confirm.
- Message msg = mHandler.obtainMessage(MSG_BACK_DELAYED_PRESS,
- mBackKeyPressCounter, 0, eventTime);
- msg.setAsynchronous(true);
- mHandler.sendMessageDelayed(msg, ViewConfiguration.getMultiPressTimeout());
- }
- }
-
// Reset back long press state
cancelPendingBackKeyAction();
@@ -1394,10 +1358,6 @@
}
}
- private void finishBackKeyPress() {
- mBackKeyPressCounter = 0;
- }
-
private void cancelPendingPowerKeyAction() {
if (!mPowerKeyHandled) {
mPowerKeyHandled = true;
@@ -1415,18 +1375,6 @@
}
}
- private void backMultiPressAction(int count) {
- if (count >= PANIC_PRESS_BACK_COUNT) {
- switch (mPanicPressOnBackBehavior) {
- case PANIC_PRESS_BACK_NOTHING:
- break;
- case PANIC_PRESS_BACK_HOME:
- launchHomeFromHotKey();
- break;
- }
- }
- }
-
private void powerPress(long eventTime, boolean interactive, int count) {
if (mScreenOnEarly && !mScreenOnFully) {
Slog.i(TAG, "Suppressed redundant power key press while "
@@ -1642,10 +1590,6 @@
return mLongPressOnBackBehavior != LONG_PRESS_BACK_NOTHING;
}
- private boolean hasPanicPressOnBackBehavior() {
- return mPanicPressOnBackBehavior != PANIC_PRESS_BACK_NOTHING;
- }
-
private void interceptScreenshotChord() {
if (mScreenshotChordEnabled
&& mScreenshotChordVolumeDownKeyTriggered && mScreenshotChordPowerKeyTriggered
@@ -2036,8 +1980,6 @@
mLongPressOnBackBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnBackBehavior);
- mPanicPressOnBackBehavior = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_backPanicBehavior);
mShortPressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shortPressOnPowerBehavior);
@@ -2557,7 +2499,7 @@
// check if user has enabled this operation. SecurityException will be thrown if this app
// has not been allowed by the user
- final int mode = mAppOpsManager.checkOpNoThrow(outAppOp[0], callingUid, attrs.packageName);
+ final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, attrs.packageName);
switch (mode) {
case AppOpsManager.MODE_ALLOWED:
case AppOpsManager.MODE_IGNORED:
@@ -3392,7 +3334,7 @@
}
boolean keyguardOn() {
- return isKeyguardShowingAndNotOccluded();
+ return isKeyguardShowingAndNotOccluded() || inKeyguardRestrictedKeyInputMode();
}
private static final int[] WINDOW_TYPES_WHERE_HOME_DOESNT_WORK = {
@@ -6934,6 +6876,13 @@
return mKeyguardOccluded;
}
+ /** {@inheritDoc} */
+ @Override
+ public boolean inKeyguardRestrictedKeyInputMode() {
+ if (mKeyguardDelegate == null) return false;
+ return mKeyguardDelegate.isInputRestricted();
+ }
+
@Override
public void dismissKeyguardLw(IKeyguardDismissCallback callback) {
if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
@@ -8296,9 +8245,6 @@
pw.print("mLongPressOnBackBehavior=");
pw.println(longPressOnBackBehaviorToString(mLongPressOnBackBehavior));
pw.print(prefix);
- pw.print("mPanicPressOnBackBehavior=");
- pw.println(panicPressOnBackBehaviorToString(mPanicPressOnBackBehavior));
- pw.print(prefix);
pw.print("mLongPressOnHomeBehavior=");
pw.println(longPressOnHomeBehaviorToString(mLongPressOnHomeBehavior));
pw.print(prefix);
@@ -8498,17 +8444,6 @@
}
}
- private static String panicPressOnBackBehaviorToString(int behavior) {
- switch (behavior) {
- case PANIC_PRESS_BACK_NOTHING:
- return "PANIC_PRESS_BACK_NOTHING";
- case PANIC_PRESS_BACK_HOME:
- return "PANIC_PRESS_BACK_HOME";
- default:
- return Integer.toString(behavior);
- }
- }
-
private static String longPressOnHomeBehaviorToString(int behavior) {
switch (behavior) {
case LONG_PRESS_HOME_NOTHING:
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index bcb7212..cfe4088 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1360,6 +1360,17 @@
public boolean isKeyguardTrustedLw();
/**
+ * inKeyguardRestrictedKeyInputMode
+ *
+ * If keyguard screen is showing or in restricted key input mode (i.e. in
+ * keyguard password emergency screen). When in such mode, certain keys,
+ * such as the Home key and the right soft keys, don't work.
+ *
+ * @return true if in keyguard restricted input mode.
+ */
+ public boolean inKeyguardRestrictedKeyInputMode();
+
+ /**
* Ask the policy to dismiss the keyguard, if it is currently shown.
*
* @param callback Callback to be informed about the result.
diff --git a/services/core/java/com/android/server/slice/PinnedSliceState.java b/services/core/java/com/android/server/slice/PinnedSliceState.java
new file mode 100644
index 0000000..cf930f5
--- /dev/null
+++ b/services/core/java/com/android/server/slice/PinnedSliceState.java
@@ -0,0 +1,215 @@
+/*
+ * 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.slice;
+
+import android.app.slice.ISliceListener;
+import android.app.slice.Slice;
+import android.app.slice.SliceProvider;
+import android.app.slice.SliceSpec;
+import android.content.ContentProviderClient;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Manages the state of a pinned slice.
+ */
+public class PinnedSliceState {
+
+ private static final long SLICE_TIMEOUT = 5000;
+ private static final String TAG = "PinnedSliceState";
+
+ private final Object mLock;
+
+ private final SliceManagerService mService;
+ private final Uri mUri;
+ @GuardedBy("mLock")
+ private final ArraySet<String> mPinnedPkgs = new ArraySet<>();
+ @GuardedBy("mLock")
+ private final ArraySet<ISliceListener> mListeners = new ArraySet<>();
+ @GuardedBy("mLock")
+ private SliceSpec[] mSupportedSpecs = null;
+
+ public PinnedSliceState(SliceManagerService service, Uri uri) {
+ mService = service;
+ mUri = uri;
+ mService.getHandler().post(this::handleSendPinned);
+ mLock = mService.getLock();
+ }
+
+ public SliceSpec[] getSpecs() {
+ return mSupportedSpecs;
+ }
+
+ public void mergeSpecs(SliceSpec[] supportedSpecs) {
+ synchronized (mLock) {
+ if (mSupportedSpecs == null) {
+ mSupportedSpecs = supportedSpecs;
+ } else {
+ List<SliceSpec> specs = Arrays.asList(mSupportedSpecs);
+ mSupportedSpecs = specs.stream().map(s -> {
+ SliceSpec other = findSpec(supportedSpecs, s.getType());
+ if (other == null) return null;
+ if (other.getRevision() < s.getRevision()) {
+ return other;
+ }
+ return s;
+ }).filter(s -> s != null).toArray(SliceSpec[]::new);
+ }
+ }
+ }
+
+ private SliceSpec findSpec(SliceSpec[] specs, String type) {
+ for (SliceSpec spec : specs) {
+ if (Objects.equals(spec.getType(), type)) {
+ return spec;
+ }
+ }
+ return null;
+ }
+
+ public Uri getUri() {
+ return mUri;
+ }
+
+ public void destroy() {
+ mService.getHandler().post(this::handleSendUnpinned);
+ }
+
+ public void onChange() {
+ mService.getHandler().post(this::handleBind);
+ }
+
+ public void addSliceListener(ISliceListener listener, SliceSpec[] specs) {
+ synchronized (mLock) {
+ if (mListeners.add(listener) && mListeners.size() == 1) {
+ mService.listen(mUri);
+ }
+ mergeSpecs(specs);
+ }
+ }
+
+ public boolean removeSliceListener(ISliceListener listener) {
+ synchronized (mLock) {
+ if (mListeners.remove(listener) && mListeners.size() == 0) {
+ mService.unlisten(mUri);
+ }
+ }
+ return !isPinned();
+ }
+
+ public void pin(String pkg, SliceSpec[] specs) {
+ synchronized (mLock) {
+ mPinnedPkgs.add(pkg);
+ mergeSpecs(specs);
+ }
+ }
+
+ public boolean unpin(String pkg) {
+ synchronized (mLock) {
+ mPinnedPkgs.remove(pkg);
+ }
+ return !isPinned();
+ }
+
+ public boolean isListening() {
+ synchronized (mLock) {
+ return !mListeners.isEmpty();
+ }
+ }
+
+ @VisibleForTesting
+ public boolean isPinned() {
+ synchronized (mLock) {
+ return !mPinnedPkgs.isEmpty() || !mListeners.isEmpty();
+ }
+ }
+
+ ContentProviderClient getClient() {
+ ContentProviderClient client =
+ mService.getContext().getContentResolver().acquireContentProviderClient(mUri);
+ client.setDetectNotResponding(SLICE_TIMEOUT);
+ return client;
+ }
+
+ private void handleBind() {
+ Slice s;
+ try (ContentProviderClient client = getClient()) {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(Arrays.asList(mSupportedSpecs)));
+ final Bundle res;
+ try {
+ res = client.call(SliceProvider.METHOD_SLICE, null, extras);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to bind slice " + mUri, e);
+ return;
+ }
+ if (res == null) return;
+ Bundle.setDefusable(res, true);
+ s = res.getParcelable(SliceProvider.EXTRA_SLICE);
+ }
+ synchronized (mLock) {
+ mListeners.removeIf(l -> {
+ try {
+ l.onSliceUpdated(s);
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to notify slice " + mUri, e);
+ return true;
+ }
+ });
+ if (!isPinned()) {
+ // All the listeners died, remove from pinned state.
+ mService.removePinnedSlice(mUri);
+ }
+ }
+ }
+
+ private void handleSendPinned() {
+ try (ContentProviderClient client = getClient()) {
+ Bundle b = new Bundle();
+ b.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri);
+ try {
+ client.call(SliceProvider.METHOD_PIN, null, b);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to contact " + mUri, e);
+ }
+ }
+ }
+
+ private void handleSendUnpinned() {
+ try (ContentProviderClient client = getClient()) {
+ Bundle b = new Bundle();
+ b.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri);
+ try {
+ client.call(SliceProvider.METHOD_UNPIN, null, b);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to contact " + mUri, e);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 047e270..2d9e772 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -16,21 +16,307 @@
package com.android.server.slice;
-import android.app.slice.ISliceManager;
-import android.content.Context;
+import static android.content.ContentProvider.getUserIdFromUri;
+import static android.content.ContentProvider.maybeAddUserId;
+import android.Manifest.permission;
+import android.app.AppOpsManager;
+import android.app.slice.ISliceListener;
+import android.app.slice.ISliceManager;
+import android.app.slice.SliceSpec;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.AssistUtils;
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
import com.android.server.SystemService;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
public class SliceManagerService extends ISliceManager.Stub {
- public SliceManagerService(Context context) {
+ private static final String TAG = "SliceManagerService";
+ private final Object mLock = new Object();
+ private final Context mContext;
+ private final PackageManagerInternal mPackageManagerInternal;
+ private final AppOpsManager mAppOps;
+ private final AssistUtils mAssistUtils;
+
+ @GuardedBy("mLock")
+ private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>();
+ private final Handler mHandler;
+ private final ContentObserver mObserver;
+
+ public SliceManagerService(Context context) {
+ this(context, createHandler().getLooper());
}
+ @VisibleForTesting
+ SliceManagerService(Context context, Looper looper) {
+ mContext = context;
+ mPackageManagerInternal = Preconditions.checkNotNull(
+ LocalServices.getService(PackageManagerInternal.class));
+ mAppOps = context.getSystemService(AppOpsManager.class);
+ mAssistUtils = new AssistUtils(context);
+ mHandler = new Handler(looper);
+
+ mObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ try {
+ getPinnedSlice(uri).onChange();
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Received change for unpinned slice " + uri, e);
+ }
+ }
+ };
+ }
+
+ /// ----- Lifecycle stuff -----
private void systemReady() {
}
- private void onUnlockUser(int userHandle) {
+ private void onUnlockUser(int userId) {
+ }
+
+ private void onStopUser(int userId) {
+ synchronized (mLock) {
+ mPinnedSlicesByUri.values().removeIf(s -> getUserIdFromUri(s.getUri()) == userId);
+ }
+ }
+
+ /// ----- ISliceManager stuff -----
+ @Override
+ public void addSliceListener(Uri uri, String pkg, ISliceListener listener, SliceSpec[] specs)
+ throws RemoteException {
+ verifyCaller(pkg);
+ uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
+ enforceAccess(pkg, uri);
+ getOrCreatePinnedSlice(uri).addSliceListener(listener, specs);
+ }
+
+ @Override
+ public void removeSliceListener(Uri uri, String pkg, ISliceListener listener)
+ throws RemoteException {
+ verifyCaller(pkg);
+ uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
+ enforceAccess(pkg, uri);
+ if (getPinnedSlice(uri).removeSliceListener(listener)) {
+ removePinnedSlice(uri);
+ }
+ }
+
+ @Override
+ public void pinSlice(String pkg, Uri uri, SliceSpec[] specs) throws RemoteException {
+ verifyCaller(pkg);
+ enforceFullAccess(pkg, "pinSlice", uri);
+ uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
+ getOrCreatePinnedSlice(uri).pin(pkg, specs);
+ }
+
+ @Override
+ public void unpinSlice(String pkg, Uri uri) throws RemoteException {
+ verifyCaller(pkg);
+ enforceFullAccess(pkg, "unpinSlice", uri);
+ uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
+ if (getPinnedSlice(uri).unpin(pkg)) {
+ removePinnedSlice(uri);
+ }
+ }
+
+ @Override
+ public boolean hasSliceAccess(String pkg) throws RemoteException {
+ verifyCaller(pkg);
+ return hasFullSliceAccess(pkg, Binder.getCallingUserHandle().getIdentifier());
+ }
+
+ @Override
+ public SliceSpec[] getPinnedSpecs(Uri uri, String pkg) throws RemoteException {
+ verifyCaller(pkg);
+ enforceAccess(pkg, uri);
+ return getPinnedSlice(uri).getSpecs();
+ }
+
+ /// ----- internal code -----
+ void removePinnedSlice(Uri uri) {
+ synchronized (mLock) {
+ mPinnedSlicesByUri.remove(uri).destroy();
+ }
+ }
+
+ private PinnedSliceState getPinnedSlice(Uri uri) {
+ synchronized (mLock) {
+ PinnedSliceState manager = mPinnedSlicesByUri.get(uri);
+ if (manager == null) {
+ throw new IllegalStateException(String.format("Slice %s not pinned",
+ uri.toString()));
+ }
+ return manager;
+ }
+ }
+
+ private PinnedSliceState getOrCreatePinnedSlice(Uri uri) {
+ synchronized (mLock) {
+ PinnedSliceState manager = mPinnedSlicesByUri.get(uri);
+ if (manager == null) {
+ manager = createPinnedSlice(uri);
+ mPinnedSlicesByUri.put(uri, manager);
+ }
+ return manager;
+ }
+ }
+
+ @VisibleForTesting
+ PinnedSliceState createPinnedSlice(Uri uri) {
+ return new PinnedSliceState(this, uri);
+ }
+
+ public Object getLock() {
+ return mLock;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ private void enforceAccess(String pkg, Uri uri) {
+ getContext().enforceUriPermission(uri, permission.BIND_SLICE,
+ permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ "Slice binding requires the permission BIND_SLICE");
+ int user = Binder.getCallingUserHandle().getIdentifier();
+ if (getUserIdFromUri(uri, user) != user) {
+ getContext().enforceCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL,
+ "Slice interaction across users requires INTERACT_ACROSS_USERS_FULL");
+ }
+ }
+
+ private void enforceFullAccess(String pkg, String name, Uri uri) {
+ int user = Binder.getCallingUserHandle().getIdentifier();
+ if (!hasFullSliceAccess(pkg, user)) {
+ throw new SecurityException(String.format("Call %s requires full slice access", name));
+ }
+ if (getUserIdFromUri(uri, user) != user) {
+ getContext().enforceCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL,
+ "Slice interaction across users requires INTERACT_ACROSS_USERS_FULL");
+ }
+ }
+
+ private void verifyCaller(String pkg) {
+ mAppOps.checkPackage(Binder.getCallingUid(), pkg);
+ }
+
+ private boolean hasFullSliceAccess(String pkg, int userId) {
+ return isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId)
+ || isGrantedFullAccess(pkg, userId);
+ }
+
+ private boolean isAssistant(String pkg, int userId) {
+ final ComponentName cn = mAssistUtils.getAssistComponentForUser(userId);
+ if (cn == null) {
+ return false;
+ }
+ return cn.getPackageName().equals(pkg);
+ }
+
+ public void listen(Uri uri) {
+ mContext.getContentResolver().registerContentObserver(uri, true, mObserver);
+ }
+
+ public void unlisten(Uri uri) {
+ mContext.getContentResolver().unregisterContentObserver(mObserver);
+ synchronized (mLock) {
+ mPinnedSlicesByUri.forEach((u, s) -> {
+ if (s.isListening()) {
+ listen(u);
+ }
+ });
+ }
+ }
+
+ private boolean isDefaultHomeApp(String pkg, int userId) {
+ String defaultHome = getDefaultHome(userId);
+ return Objects.equals(pkg, defaultHome);
+ }
+
+ // Based on getDefaultHome in ShortcutService.
+ // TODO: Unify if possible
+ @VisibleForTesting
+ String getDefaultHome(int userId) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
+
+ // Default launcher from package manager.
+ final ComponentName defaultLauncher = mPackageManagerInternal
+ .getHomeActivitiesAsUser(allHomeCandidates, userId);
+
+ ComponentName detected = null;
+ if (defaultLauncher != null) {
+ detected = defaultLauncher;
+ }
+
+ if (detected == null) {
+ // If we reach here, that means it's the first check since the user was created,
+ // and there's already multiple launchers and there's no default set.
+ // Find the system one with the highest priority.
+ // (We need to check the priority too because of FallbackHome in Settings.)
+ // If there's no system launcher yet, then no one can access slices, until
+ // the user explicitly sets one.
+ final int size = allHomeCandidates.size();
+
+ int lastPriority = Integer.MIN_VALUE;
+ for (int i = 0; i < size; i++) {
+ final ResolveInfo ri = allHomeCandidates.get(i);
+ if (!ri.activityInfo.applicationInfo.isSystemApp()) {
+ continue;
+ }
+ if (ri.priority < lastPriority) {
+ continue;
+ }
+ detected = ri.activityInfo.getComponentName();
+ lastPriority = ri.priority;
+ }
+ }
+ return detected.getPackageName();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private boolean isGrantedFullAccess(String pkg, int userId) {
+ // TODO: This will be user granted access, if we allow this through a prompt.
+ return false;
+ }
+
+ private static ServiceThread createHandler() {
+ ServiceThread handlerThread = new ServiceThread(TAG,
+ Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
+ handlerThread.start();
+ return handlerThread;
}
public static class Lifecycle extends SystemService {
@@ -57,5 +343,10 @@
public void onUnlockUser(int userHandle) {
mService.onUnlockUser(userHandle);
}
+
+ @Override
+ public void onStopUser(int userHandle) {
+ mService.onStopUser(userHandle);
+ }
}
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 1ce1400..b31f4b3 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -455,7 +455,7 @@
Slog.d(TAG, "Pulling " + tagId);
switch (tagId) {
- case StatsLog.WIFI_BYTES_TRANSFERRED: {
+ case StatsLog.WIFI_BYTES_TRANSFER: {
long token = Binder.clearCallingIdentity();
try {
// TODO: Consider caching the following call to get BatteryStatsInternal.
@@ -476,7 +476,7 @@
}
break;
}
- case StatsLog.MOBILE_BYTES_TRANSFERRED: {
+ case StatsLog.MOBILE_BYTES_TRANSFER: {
long token = Binder.clearCallingIdentity();
try {
BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
@@ -496,7 +496,7 @@
}
break;
}
- case StatsLog.WIFI_BYTES_TRANSFERRED_BY_FG_BG: {
+ case StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
long token = Binder.clearCallingIdentity();
try {
BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
@@ -516,7 +516,7 @@
}
break;
}
- case StatsLog.MOBILE_BYTES_TRANSFERRED_BY_FG_BG: {
+ case StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
long token = Binder.clearCallingIdentity();
try {
BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
@@ -536,7 +536,7 @@
}
break;
}
- case StatsLog.KERNEL_WAKELOCK_PULLED: {
+ case StatsLog.KERNEL_WAKELOCK: {
final KernelWakelockStats wakelockStats =
mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
List<StatsLogEventWrapper> ret = new ArrayList();
@@ -552,7 +552,7 @@
}
return ret.toArray(new StatsLogEventWrapper[ret.size()]);
}
- case StatsLog.CPU_TIME_PER_FREQ_PULLED: {
+ case StatsLog.CPU_TIME_PER_FREQ: {
List<StatsLogEventWrapper> ret = new ArrayList();
for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readDelta();
@@ -568,7 +568,7 @@
}
return ret.toArray(new StatsLogEventWrapper[ret.size()]);
}
- case StatsLog.WIFI_ACTIVITY_ENERGY_INFO_PULLED: {
+ case StatsLog.WIFI_ACTIVITY_ENERGY_INFO: {
List<StatsLogEventWrapper> ret = new ArrayList();
long token = Binder.clearCallingIdentity();
if (mWifiManager == null) {
@@ -596,7 +596,7 @@
}
break;
}
- case StatsLog.MODEM_ACTIVITY_INFO_PULLED: {
+ case StatsLog.MODEM_ACTIVITY_INFO: {
List<StatsLogEventWrapper> ret = new ArrayList();
long token = Binder.clearCallingIdentity();
if (mTelephony == null) {
diff --git a/services/core/java/com/android/server/timezone/PackageStatusStorage.java b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
index 5601c91..251a277 100644
--- a/services/core/java/com/android/server/timezone/PackageStatusStorage.java
+++ b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
@@ -82,12 +82,16 @@
PackageStatusStorage(File storageDir) {
mPackageStatusFile = new AtomicFile(new File(storageDir, "package-status.xml"));
+ }
+
+ /**
+ * Initialize any storage, as needed.
+ *
+ * @throws IOException if the storage could not be initialized
+ */
+ void initialize() throws IOException {
if (!mPackageStatusFile.getBaseFile().exists()) {
- try {
- insertInitialPackageStatus();
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
+ insertInitialPackageStatus();
}
}
@@ -342,13 +346,13 @@
}
/** Only used during tests to force a known table state. */
- public void forceCheckStateForTests(int checkStatus, PackageVersions packageVersions) {
+ public void forceCheckStateForTests(int checkStatus, PackageVersions packageVersions)
+ throws IOException {
synchronized (this) {
try {
- int optimisticLockId = getCurrentOptimisticLockId();
- writePackageStatusWithOptimisticLockCheck(optimisticLockId, optimisticLockId,
- checkStatus, packageVersions);
- } catch (IOException | ParseException e) {
+ final int initialOptimisticLockId = (int) System.currentTimeMillis();
+ writePackageStatusLocked(checkStatus, initialOptimisticLockId, packageVersions);
+ } catch (IOException e) {
throw new IllegalStateException(e);
}
}
diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java
index c362c80..0e8d8bc 100644
--- a/services/core/java/com/android/server/timezone/PackageTracker.java
+++ b/services/core/java/com/android/server/timezone/PackageTracker.java
@@ -28,6 +28,7 @@
import android.util.Slog;
import java.io.File;
+import java.io.IOException;
import java.io.PrintWriter;
import java.time.Clock;
@@ -97,10 +98,6 @@
Clock elapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
PackageTrackerHelperImpl helperImpl = new PackageTrackerHelperImpl(context);
File storageDir = FileUtils.createDir(Environment.getDataSystemDirectory(), "timezone");
- if (!storageDir.exists()) {
- storageDir.mkdir();
- }
-
return new PackageTracker(
elapsedRealtimeClock /* elapsedRealtimeClock */,
helperImpl /* configHelper */,
@@ -121,11 +118,11 @@
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- protected synchronized void start() {
+ protected synchronized boolean start() {
mTrackingEnabled = mConfigHelper.isTrackingEnabled();
if (!mTrackingEnabled) {
Slog.i(TAG, "Time zone updater / data package tracking explicitly disabled.");
- return;
+ return false;
}
mUpdateAppPackageName = mConfigHelper.getUpdateAppPackageName();
@@ -143,6 +140,14 @@
mCheckTriggered = false;
mCheckFailureCount = 0;
+ // Initialize the storage, as needed.
+ try {
+ mPackageStatusStorage.initialize();
+ } catch (IOException e) {
+ Slog.w(TAG, "PackageTracker storage could not be initialized.", e);
+ return false;
+ }
+
// Initialize the intent helper.
mIntentHelper.initialize(mUpdateAppPackageName, mDataAppPackageName, this);
@@ -152,6 +157,7 @@
mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
Slog.i(TAG, "Time zone updater / data package tracking enabled");
+ return true;
}
/**
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index 52b49ba..30fc63c 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -121,6 +121,7 @@
}
public void start() {
+ // Return value deliberately ignored: no action required on failure to start.
mPackageTracker.start();
}
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 7b1e12e..35b6ad3 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -59,13 +59,6 @@
int userId, int processId, @NonNull ComponentName calling);
/**
- * Set whether the system has acquired a sleep token.
- *
- * @param isAsleep is {@code true} if the device is asleep, or {@code false} otherwise.
- */
- public abstract void onSleepStateChanged(boolean isAsleep);
-
- /**
* Set whether the display used for VR output is on.
*
* @param isScreenOn is {@code true} if the display is on and can receive commands,
@@ -74,13 +67,6 @@
public abstract void onScreenStateChanged(boolean isScreenOn);
/**
- * Set whether the keyguard is currently active/showing.
- *
- * @param isShowing is {@code true} if the keyguard is active/showing.
- */
- public abstract void onKeyguardStateChanged(boolean isShowing);
-
- /**
* Return NO_ERROR if the given package is installed on the device and enabled as a
* VrListenerService for the given current user, or a negative error code indicating a failure.
*
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 7d55b68..de723c6 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerInternal.ScreenObserver;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.INotificationManager;
@@ -58,6 +59,8 @@
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
+
+import com.android.server.FgThread;
import com.android.server.wm.WindowManagerInternal;
import android.view.inputmethod.InputMethodManagerInternal;
@@ -105,7 +108,8 @@
*
* @hide
*/
-public class VrManagerService extends SystemService implements EnabledComponentChangeListener{
+public class VrManagerService extends SystemService
+ implements EnabledComponentChangeListener, ScreenObserver {
public static final String TAG = "VrManagerService";
static final boolean DBG = false;
@@ -237,15 +241,17 @@
}
}
- private void setSleepState(boolean isAsleep) {
- setSystemState(FLAG_AWAKE, !isAsleep);
- }
-
private void setScreenOn(boolean isScreenOn) {
setSystemState(FLAG_SCREEN_ON, isScreenOn);
}
- private void setKeyguardShowing(boolean isShowing) {
+ @Override
+ public void onAwakeStateChanged(boolean isAwake) {
+ setSystemState(FLAG_AWAKE, isAwake);
+ }
+
+ @Override
+ public void onKeyguardStateChanged(boolean isShowing) {
setSystemState(FLAG_KEYGUARD_UNLOCKED, !isShowing);
}
@@ -706,21 +712,11 @@
}
@Override
- public void onSleepStateChanged(boolean isAsleep) {
- VrManagerService.this.setSleepState(isAsleep);
- }
-
- @Override
public void onScreenStateChanged(boolean isScreenOn) {
VrManagerService.this.setScreenOn(isScreenOn);
}
@Override
- public void onKeyguardStateChanged(boolean isShowing) {
- VrManagerService.this.setKeyguardShowing(isShowing);
- }
-
- @Override
public boolean isCurrentVrListener(String packageName, int userId) {
return VrManagerService.this.isCurrentVrListener(packageName, userId);
}
@@ -773,6 +769,9 @@
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ LocalServices.getService(ActivityManagerInternal.class)
+ .registerScreenObserver(this);
+
mNotificationManager = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
synchronized (mLock) {
@@ -828,9 +827,11 @@
@Override
public void onSwitchUser(int userHandle) {
- synchronized (mLock) {
- mComponentObserver.onUsersChanged();
- }
+ FgThread.getHandler().post(() -> {
+ synchronized (mLock) {
+ mComponentObserver.onUsersChanged();
+ }
+ });
}
diff --git a/services/core/java/com/android/server/wallpaper/IWallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/IWallpaperManagerService.java
new file mode 100644
index 0000000..60b08dd
--- /dev/null
+++ b/services/core/java/com/android/server/wallpaper/IWallpaperManagerService.java
@@ -0,0 +1,35 @@
+/*
+ * 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.wallpaper;
+
+import android.app.IWallpaperManager;
+import android.os.IBinder;
+
+/**
+ * Extended IWallpaperManager which can receive SystemService's lifetime events.
+ */
+interface IWallpaperManagerService extends IWallpaperManager, IBinder {
+ /**
+ * @see com.android.server.SystemService#onBootPhase(int)
+ */
+ void onBootPhase(int phase);
+
+ /**
+ * @see com.android.server.SystemService#onUnlockUser(int)
+ */
+ void onUnlockUser(final int userId);
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index b888ec2..8e916ad 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -99,6 +99,7 @@
import com.android.server.FgThread;
import com.android.server.SystemService;
+import java.lang.reflect.InvocationTargetException;
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -119,14 +120,16 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import com.android.internal.R;
-public class WallpaperManagerService extends IWallpaperManager.Stub {
+public class WallpaperManagerService extends IWallpaperManager.Stub
+ implements IWallpaperManagerService {
static final String TAG = "WallpaperManagerService";
static final boolean DEBUG = false;
static final boolean DEBUG_LIVE = DEBUG || true;
public static class Lifecycle extends SystemService {
- private WallpaperManagerService mService;
+ private IWallpaperManagerService mService;
public Lifecycle(Context context) {
super(context);
@@ -134,22 +137,30 @@
@Override
public void onStart() {
- mService = new WallpaperManagerService(getContext());
- publishBinderService(Context.WALLPAPER_SERVICE, mService);
+ try {
+ final Class<? extends IWallpaperManagerService> klass =
+ (Class<? extends IWallpaperManagerService>)Class.forName(
+ getContext().getResources().getString(
+ R.string.config_wallpaperManagerServiceName));
+ mService = klass.getConstructor(Context.class).newInstance(getContext());
+ publishBinderService(Context.WALLPAPER_SERVICE, mService);
+ } catch (Exception exp) {
+ Slog.wtf(TAG, "Failed to instantiate WallpaperManagerService", exp);
+ }
}
@Override
public void onBootPhase(int phase) {
- if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
- mService.systemReady();
- } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
- mService.switchUser(UserHandle.USER_SYSTEM, null);
+ if (mService != null) {
+ mService.onBootPhase(phase);
}
}
@Override
public void onUnlockUser(int userHandle) {
- mService.onUnlockUser(userHandle);
+ if (mService != null) {
+ mService.onUnlockUser(userHandle);
+ }
}
}
@@ -664,6 +675,7 @@
final SparseArray<Boolean> mUserRestorecon = new SparseArray<Boolean>();
int mCurrentUserId;
+ boolean mInAmbientMode;
static class WallpaperData {
@@ -942,6 +954,13 @@
}
mPaddingChanged = false;
}
+ if (mInfo != null && mInfo.getSupportsAmbientMode()) {
+ try {
+ mEngine.setInAmbientMode(mInAmbientMode);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to set ambient mode state", e);
+ }
+ }
try {
// This will trigger onComputeColors in the wallpaper engine.
// It's fine to be locked in here since the binder is oneway.
@@ -1255,7 +1274,17 @@
mLockWallpaperMap.remove(userId);
}
- void onUnlockUser(final int userId) {
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ systemReady();
+ } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+ switchUser(UserHandle.USER_SYSTEM, null);
+ }
+ }
+
+ @Override
+ public void onUnlockUser(final int userId) {
synchronized (mLock) {
if (mCurrentUserId == userId) {
if (mWaitingForUnlock) {
@@ -1722,6 +1751,28 @@
}
}
+ public void setInAmbientMode(boolean inAmbienMode) {
+ final IWallpaperEngine engine;
+ synchronized (mLock) {
+ mInAmbientMode = inAmbienMode;
+ final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
+ if (data != null && data.connection != null && data.connection.mInfo != null
+ && data.connection.mInfo.getSupportsAmbientMode()) {
+ engine = data.connection.mEngine;
+ } else {
+ engine = null;
+ }
+ }
+
+ if (engine != null) {
+ try {
+ engine.setInAmbientMode(inAmbienMode);
+ } catch (RemoteException e) {
+ // Cannot talk to wallpaper engine.
+ }
+ }
+ }
+
@Override
public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {
checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 95b139a..163b160 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
@@ -234,16 +235,7 @@
private static void populateTransformationMatrixLocked(WindowState windowState,
Matrix outMatrix) {
- sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
- sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
- sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDtDy;
- sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDsDy;
- sTempFloats[Matrix.MTRANS_X] = windowState.mShownPosition.x;
- sTempFloats[Matrix.MTRANS_Y] = windowState.mShownPosition.y;
- sTempFloats[Matrix.MPERSP_0] = 0;
- sTempFloats[Matrix.MPERSP_1] = 0;
- sTempFloats[Matrix.MPERSP_2] = 1;
- outMatrix.setValues(sTempFloats);
+ windowState.getTransformationMatrix(sTempFloats, outMatrix);
}
/**
@@ -1077,8 +1069,11 @@
continue;
}
- // If the window is not touchable - ignore.
- if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
+ // Ignore non-touchable windows, except the split-screen divider, which is
+ // occasionally non-touchable but still useful for identifying split-screen
+ // mode.
+ if (((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
+ && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) {
continue;
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index c39ce98..94a0cb7 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -66,6 +66,7 @@
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import android.view.animation.Transformation;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.input.InputApplicationHandle;
@@ -75,6 +76,7 @@
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.LinkedList;
class AppTokenList extends ArrayList<AppWindowToken> {
}
@@ -231,7 +233,7 @@
// If this initial window is animating, stop it -- we will do an animation to reveal
// it from behind the starting window, so there is no need for it to also be doing its
// own stuff.
- winAnimator.clearAnimation();
+ win.cancelAnimation();
if (getController() != null) {
getController().removeStartingWindow();
}
@@ -389,7 +391,7 @@
}
for (int i = mChildren.size() - 1; i >= 0 && !delayed; i--) {
- if ((mChildren.get(i)).isWindowAnimationSet()) {
+ if ((mChildren.get(i)).isSelfOrChildAnimating()) {
delayed = true;
}
}
@@ -610,8 +612,12 @@
*/
private void destroySurfaces(boolean cleanupOnResume) {
boolean destroyedSomething = false;
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState win = mChildren.get(i);
+
+ // Copying to a different list as multiple children can be removed.
+ // TODO: Not sure why this is needed.
+ final LinkedList<WindowState> children = new LinkedList<>(mChildren);
+ for (int i = children.size() - 1; i >= 0; i--) {
+ final WindowState win = children.get(i);
destroyedSomething |= win.destroySurface(cleanupOnResume, mAppStopped);
}
if (destroyedSomething) {
@@ -1320,7 +1326,7 @@
+ " pv=" + w.mPolicyVisibility
+ " mDrawState=" + winAnimator.drawStateToString()
+ " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested
- + " a=" + winAnimator.mAnimating);
+ + " a=" + winAnimator.isAnimationSet());
}
}
@@ -1520,6 +1526,11 @@
}
@Override
+ boolean isSelfAnimating() {
+ return mAppAnimator.isAnimating();
+ }
+
+ @Override
void dump(PrintWriter pw, String prefix) {
super.dump(pw, prefix);
if (appToken != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index eda8fec..f458457 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -146,6 +146,7 @@
import android.view.MagnificationSpec;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
import com.android.internal.annotations.VisibleForTesting;
@@ -339,6 +340,7 @@
new ApplySurfaceChangesTransactionState();
private final ScreenshotApplicationState mScreenshotApplicationState =
new ScreenshotApplicationState();
+ private final Transaction mTmpTransaction = new Transaction();
// True if this display is in the process of being removed. Used to determine if the removal of
// the display's direct children should be allowed.
@@ -379,29 +381,11 @@
*/
private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
+ /** Temporary float array to retrieve 3x3 matrix values. */
+ private final float[] mTmpFloats = new float[9];
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
- if (winAnimator.hasSurface()) {
- final boolean wasAnimating = winAnimator.mWasAnimating;
- final boolean nowAnimating = winAnimator.stepAnimationLocked(
- mTmpWindowAnimator.mCurrentTime);
- winAnimator.mWasAnimating = nowAnimating;
- mTmpWindowAnimator.orAnimating(nowAnimating);
-
- if (DEBUG_WALLPAPER) Slog.v(TAG,
- w + ": wasAnimating=" + wasAnimating + ", nowAnimating=" + nowAnimating);
-
- if (wasAnimating && !winAnimator.mAnimating
- && mWallpaperController.isWallpaperTarget(w)) {
- mTmpWindowAnimator.mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
- pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- if (DEBUG_LAYOUT_REPEATS) {
- mService.mWindowPlacerLocked.debugLayoutRepeats(
- "updateWindowsAndWallpaperLocked 2", pendingLayoutChanges);
- }
- }
- }
-
final AppWindowToken atoken = w.mAppToken;
if (winAnimator.mDrawState == READY_TO_SHOW) {
if (atoken == null || atoken.allDrawn) {
@@ -434,13 +418,13 @@
// If this window is animating, make a note that we have an animating window and take
// care of a request to run a detached wallpaper animation.
- if (winAnimator.mAnimating) {
- if (winAnimator.mAnimation != null) {
- if ((flags & FLAG_SHOW_WALLPAPER) != 0
- && winAnimator.mAnimation.getDetachWallpaper()) {
+ if (winAnimator.isAnimationSet()) {
+ final AnimationAdapter anim = w.getAnimation();
+ if (anim != null) {
+ if ((flags & FLAG_SHOW_WALLPAPER) != 0 && anim.getDetachWallpaper()) {
mTmpWindow = w;
}
- final int color = winAnimator.mAnimation.getBackgroundColor();
+ final int color = anim.getBackgroundColor();
if (color != 0) {
final TaskStack stack = w.getStack();
if (stack != null) {
@@ -448,7 +432,6 @@
}
}
}
- mTmpWindowAnimator.setAnimating(true);
}
// If this window's app token is running a detached wallpaper animation, make a note so
@@ -684,7 +667,10 @@
mWallpaperController.updateWallpaperVisibility();
}
- w.handleWindowMovedIfNeeded();
+ // Use mTmpTransaction instead of mPendingTransaction because we don't want to commit
+ // other changes in mPendingTransaction at this point.
+ w.handleWindowMovedIfNeeded(mTmpTransaction);
+ SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
final WindowStateAnimator winAnimator = w.mWinAnimator;
@@ -720,7 +706,7 @@
}
}
final TaskStack stack = w.getStack();
- if ((!winAnimator.isAnimationStarting() && !winAnimator.isWaitingForOpening())
+ if ((!winAnimator.isWaitingForOpening())
|| (stack != null && stack.isAnimatingBounds())) {
// Updates the shown frame before we set up the surface. This is needed
// because the resizing could change the top-left position (in addition to
@@ -736,6 +722,12 @@
winAnimator.computeShownFrameLocked();
}
winAnimator.setSurfaceBoundariesLocked(mTmpRecoveringMemory /* recoveringMemory */);
+
+ // Since setSurfaceBoundariesLocked applies the clipping, we need to apply the position
+ // to the surface of the window container as well. Use mTmpTransaction instead of
+ // mPendingTransaction to avoid committing any existing changes in there.
+ w.updateSurfacePosition(mTmpTransaction);
+ SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
}
final AppWindowToken atoken = w.mAppToken;
@@ -2617,8 +2609,7 @@
forAllWindows(w -> {
if (w.mAppToken == null && policy.canBeHiddenByKeyguardLw(w)
&& w.wouldBeVisibleIfPolicyIgnored() && !w.isVisible()) {
- w.mWinAnimator.setAnimation(
- policy.createHiddenByKeyguardExit(onWallpaper, goingToShade));
+ w.startAnimation(policy.createHiddenByKeyguardExit(onWallpaper, goingToShade));
}
}, true /* traverseTopToBottom */);
}
@@ -3775,13 +3766,13 @@
}
@Override
- void destroyAfterPendingTransaction(SurfaceControl surface) {
+ public void destroyAfterPendingTransaction(SurfaceControl surface) {
mPendingDestroyingSurfaces.add(surface);
}
/**
* Destroys any surfaces that have been put into the pending list with
- * {@link #destroyAfterTransaction}.
+ * {@link #destroyAfterPendingTransaction}.
*/
void onPendingTransactionApplied() {
for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
@@ -3789,4 +3780,21 @@
}
mPendingDestroyingSurfaces.clear();
}
+
+ @Override
+ void prepareSurfaces() {
+ final ScreenRotationAnimation screenRotationAnimation =
+ mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+ if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
+ screenRotationAnimation.getEnterTransformation().getMatrix().getValues(mTmpFloats);
+ mPendingTransaction.setMatrix(mWindowingLayer,
+ mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
+ mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
+ mPendingTransaction.setPosition(mWindowingLayer,
+ mTmpFloats[Matrix.MTRANS_X], mTmpFloats[Matrix.MTRANS_Y]);
+ mPendingTransaction.setAlpha(mWindowingLayer,
+ screenRotationAnimation.getEnterTransformation().getAlpha());
+ }
+ super.prepareSurfaces();
+ }
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index a37598e..03c0768 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -751,10 +751,10 @@
// There might be an old window delaying the animation start - clear it.
if (mDelayedImeWin != null) {
- mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
+ mDelayedImeWin.endDelayingAnimationStart();
}
mDelayedImeWin = imeWin;
- imeWin.mWinAnimator.startDelayingAnimationStart();
+ imeWin.startDelayingAnimationStart();
}
// If we are already waiting for something to be drawn, clear out the old one so it
@@ -765,25 +765,27 @@
mService.mWaitingForDrawnCallback.run();
}
mService.mWaitingForDrawnCallback = () -> {
- mAnimationStartDelayed = false;
- if (mDelayedImeWin != null) {
- mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
+ synchronized (mService.mWindowMap) {
+ mAnimationStartDelayed = false;
+ if (mDelayedImeWin != null) {
+ mDelayedImeWin.endDelayingAnimationStart();
+ }
+ // If the adjust status changed since this was posted, only notify
+ // the new states and don't animate.
+ long duration = 0;
+ if (mAdjustedForIme == adjustedForIme
+ && mAdjustedForDivider == adjustedForDivider) {
+ duration = IME_ADJUST_ANIM_DURATION;
+ } else {
+ Slog.w(TAG, "IME adjust changed while waiting for drawn:"
+ + " adjustedForIme=" + adjustedForIme
+ + " adjustedForDivider=" + adjustedForDivider
+ + " mAdjustedForIme=" + mAdjustedForIme
+ + " mAdjustedForDivider=" + mAdjustedForDivider);
+ }
+ notifyAdjustedForImeChanged(
+ mAdjustedForIme || mAdjustedForDivider, duration);
}
- // If the adjust status changed since this was posted, only notify
- // the new states and don't animate.
- long duration = 0;
- if (mAdjustedForIme == adjustedForIme
- && mAdjustedForDivider == adjustedForDivider) {
- duration = IME_ADJUST_ANIM_DURATION;
- } else {
- Slog.w(TAG, "IME adjust changed while waiting for drawn:"
- + " adjustedForIme=" + adjustedForIme
- + " adjustedForDivider=" + adjustedForDivider
- + " mAdjustedForIme=" + mAdjustedForIme
- + " mAdjustedForDivider=" + mAdjustedForDivider);
- }
- notifyAdjustedForImeChanged(
- mAdjustedForIme || mAdjustedForDivider, duration);
};
} else {
notifyAdjustedForImeChanged(
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 65951dc..28b4c1d 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -40,6 +40,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.input.InputWindowHandle;
import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
+import java.util.concurrent.atomic.AtomicReference;
/**
* Managing drag and drop operations initiated by View#startDragAndDrop.
@@ -67,42 +68,10 @@
private final Handler mHandler;
/**
- * Lock to preserve the order of state updates.
- * The lock is used to process drag and drop state updates in order without having the window
- * manager lock.
- *
- * Suppose DragDropController invokes a callback method A, then processes the following update
- * A'. Same for a callback method B and the following update B'. The callback wants
- * DragDropController to processes the updates in the order of A' then B'.
- *
- * Without mWriteLock: the following race can happen.
- *
- * 1. Thread a calls A.
- * 2. Thread b calls B.
- * 3. Thread b acquires the window manager lock
- * 4. thread b processes the update B'
- * 5. Thread a acquires the window manager lock
- * 6. thread a processes the update A'
- *
- * With mWriteLock we can ensure the order of A' and B'
- *
- * 1. Thread a acquire mWriteLock
- * 2. Thread a calls A
- * 3. Thread a acquire the window manager lock
- * 4. Thread a processes A'
- * 5. Thread b acquire mWriteLock
- * 6. Thread b calls B
- * 7. Thread b acquire the window manager lock
- * 8. Thread b processes B'
- *
- * Don't acquire the lock while holding the window manager lock, otherwise it causes a deadlock.
- */
- private final Object mWriteLock = new Object();
-
- /**
* Callback which is used to sync drag state with the vendor-specific code.
*/
- @NonNull private IDragDropCallback mCallback = new IDragDropCallback() {};
+ @NonNull private AtomicReference<IDragDropCallback> mCallback = new AtomicReference<>(
+ new IDragDropCallback() {});
boolean dragDropActiveLocked() {
return mDragState != null;
@@ -114,9 +83,7 @@
void registerCallback(IDragDropCallback callback) {
Preconditions.checkNotNull(callback);
- synchronized (mWriteLock) {
- mCallback = callback;
- }
+ mCallback.set(callback);
}
DragDropController(WindowManagerService service, Looper looper) {
@@ -136,45 +103,48 @@
+ " asbinder=" + window.asBinder());
}
- synchronized (mWriteLock) {
- synchronized (mService.mWindowMap) {
- if (dragDropActiveLocked()) {
- Slog.w(TAG_WM, "Drag already in progress");
- return null;
- }
+ if (width <= 0 || height <= 0) {
+ Slog.w(TAG_WM, "width and height of drag shadow must be positive");
+ return null;
+ }
- // TODO(multi-display): support other displays
- final DisplayContent displayContent =
- mService.getDefaultDisplayContentLocked();
- final Display display = displayContent.getDisplay();
-
- final SurfaceControl surface = new SurfaceControl.Builder(session)
- .setName("drag surface")
- .setSize(width, height)
- .setFormat(PixelFormat.TRANSLUCENT)
- .build();
- surface.setLayerStack(display.getLayerStack());
- float alpha = 1;
- if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
- alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
- }
- surface.setAlpha(alpha);
-
- if (SHOW_TRANSACTIONS)
- Slog.i(TAG_WM, " DRAG " + surface + ": CREATE");
- outSurface.copyFrom(surface);
- final IBinder winBinder = window.asBinder();
- IBinder token = new Binder();
- mDragState = new DragState(mService, token, surface, flags, winBinder);
- mDragState.mPid = callerPid;
- mDragState.mUid = callerUid;
- mDragState.mOriginalAlpha = alpha;
- token = mDragState.mToken = new Binder();
-
- // 5 second timeout for this window to actually begin the drag
- sendTimeoutMessage(MSG_DRAG_START_TIMEOUT, winBinder);
- return token;
+ synchronized (mService.mWindowMap) {
+ if (dragDropActiveLocked()) {
+ Slog.w(TAG_WM, "Drag already in progress");
+ return null;
}
+
+ // TODO(multi-display): support other displays
+ final DisplayContent displayContent =
+ mService.getDefaultDisplayContentLocked();
+ final Display display = displayContent.getDisplay();
+
+ final SurfaceControl surface = new SurfaceControl.Builder(session)
+ .setName("drag surface")
+ .setSize(width, height)
+ .setFormat(PixelFormat.TRANSLUCENT)
+ .build();
+ surface.setLayerStack(display.getLayerStack());
+ float alpha = 1;
+ if ((flags & View.DRAG_FLAG_OPAQUE) == 0) {
+ alpha = DRAG_SHADOW_ALPHA_TRANSPARENT;
+ }
+ surface.setAlpha(alpha);
+
+ if (SHOW_TRANSACTIONS)
+ Slog.i(TAG_WM, " DRAG " + surface + ": CREATE");
+ outSurface.copyFrom(surface);
+ final IBinder winBinder = window.asBinder();
+ IBinder token = new Binder();
+ mDragState = new DragState(mService, token, surface, flags, winBinder);
+ mDragState.mPid = callerPid;
+ mDragState.mUid = callerUid;
+ mDragState.mOriginalAlpha = alpha;
+ token = mDragState.mToken = new Binder();
+
+ // 5 second timeout for this window to actually begin the drag
+ sendTimeoutMessage(MSG_DRAG_START_TIMEOUT, winBinder);
+ return token;
}
}
@@ -185,84 +155,89 @@
Slog.d(TAG_WM, "perform drag: win=" + window + " data=" + data);
}
- synchronized (mWriteLock) {
- if (!mCallback.performDrag(window, dragToken, touchSource, touchX, touchY, thumbCenterX,
- thumbCenterY, data)) {
- return false;
- }
+ final boolean callbackResult = mCallback.get().prePerformDrag(window, dragToken,
+ touchSource, touchX, touchY, thumbCenterX, thumbCenterY, data);
+ try {
synchronized (mService.mWindowMap) {
- if (mDragState == null) {
- Slog.w(TAG_WM, "No drag prepared");
- throw new IllegalStateException("performDrag() without prepareDrag()");
- }
-
- if (dragToken != mDragState.mToken) {
- Slog.w(TAG_WM, "Performing mismatched drag");
- throw new IllegalStateException("performDrag() does not match prepareDrag()");
- }
-
- final WindowState callingWin = mService.windowForClientLocked(null, window, false);
- if (callingWin == null) {
- Slog.w(TAG_WM, "Bad requesting window " + window);
- return false; // !!! TODO: throw here?
- }
-
- // !!! TODO: if input is not still focused on the initiating window, fail
- // the drag initiation (e.g. an alarm window popped up just as the application
- // called performDrag()
-
mHandler.removeMessages(MSG_DRAG_START_TIMEOUT, window.asBinder());
-
- // !!! TODO: extract the current touch (x, y) in screen coordinates. That
- // will let us eliminate the (touchX,touchY) parameters from the API.
-
- // !!! FIXME: put all this heavy stuff onto the mHandler looper, as well as
- // the actual drag event dispatch stuff in the dragstate
-
- final DisplayContent displayContent = callingWin.getDisplayContent();
- if (displayContent == null) {
- return false;
- }
- Display display = displayContent.getDisplay();
- mDragState.register(display);
- if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
- mDragState.getInputChannel())) {
- Slog.e(TAG_WM, "Unable to transfer touch focus");
- mDragState.closeLocked();
- return false;
- }
-
- mDragState.mDisplayContent = displayContent;
- mDragState.mData = data;
- mDragState.broadcastDragStartedLocked(touchX, touchY);
- mDragState.overridePointerIconLocked(touchSource);
-
- // remember the thumb offsets for later
- mDragState.mThumbOffsetX = thumbCenterX;
- mDragState.mThumbOffsetY = thumbCenterY;
-
- // Make the surface visible at the proper location
- final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
- mService.openSurfaceTransaction();
try {
- surfaceControl.setPosition(touchX - thumbCenterX,
- touchY - thumbCenterY);
- surfaceControl.setLayer(mDragState.getDragLayerLocked());
- surfaceControl.setLayerStack(display.getLayerStack());
- surfaceControl.show();
+ if (!callbackResult) {
+ return false;
+ }
+
+ Preconditions.checkState(
+ mDragState != null, "performDrag() without prepareDrag()");
+ Preconditions.checkState(
+ mDragState.mToken == dragToken,
+ "performDrag() does not match prepareDrag()");
+
+ final WindowState callingWin = mService.windowForClientLocked(
+ null, window, false);
+ if (callingWin == null) {
+ Slog.w(TAG_WM, "Bad requesting window " + window);
+ return false; // !!! TODO: throw here?
+ }
+
+ // !!! TODO: if input is not still focused on the initiating window, fail
+ // the drag initiation (e.g. an alarm window popped up just as the application
+ // called performDrag()
+
+ // !!! TODO: extract the current touch (x, y) in screen coordinates. That
+ // will let us eliminate the (touchX,touchY) parameters from the API.
+
+ // !!! FIXME: put all this heavy stuff onto the mHandler looper, as well as
+ // the actual drag event dispatch stuff in the dragstate
+
+ final DisplayContent displayContent = callingWin.getDisplayContent();
+ if (displayContent == null) {
+ Slog.w(TAG_WM, "display content is null");
+ return false;
+ }
+
+ final Display display = displayContent.getDisplay();
+ mDragState.register(display);
+ if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
+ mDragState.getInputChannel())) {
+ Slog.e(TAG_WM, "Unable to transfer touch focus");
+ return false;
+ }
+
+ mDragState.mDisplayContent = displayContent;
+ mDragState.mData = data;
+ mDragState.broadcastDragStartedLocked(touchX, touchY);
+ mDragState.overridePointerIconLocked(touchSource);
+ // remember the thumb offsets for later
+ mDragState.mThumbOffsetX = thumbCenterX;
+ mDragState.mThumbOffsetY = thumbCenterY;
+
+ // Make the surface visible at the proper location
+ final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
+ mService.openSurfaceTransaction();
+ try {
+ surfaceControl.setPosition(touchX - thumbCenterX,
+ touchY - thumbCenterY);
+ surfaceControl.setLayer(mDragState.getDragLayerLocked());
+ surfaceControl.setLayerStack(display.getLayerStack());
+ surfaceControl.show();
+ } finally {
+ mService.closeSurfaceTransaction("performDrag");
+ if (SHOW_LIGHT_TRANSACTIONS) {
+ Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
+ }
+ }
+
+ mDragState.notifyLocationLocked(touchX, touchY);
} finally {
- mService.closeSurfaceTransaction("performDrag");
- if (SHOW_LIGHT_TRANSACTIONS) {
- Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
+ if (mDragState != null && !mDragState.isInProgress()) {
+ mDragState.closeLocked();
}
}
-
- mDragState.notifyLocationLocked(touchX, touchY);
}
+ return true; // success!
+ } finally {
+ mCallback.get().postPerformDrag();
}
-
- return true; // success!
}
void reportDropResult(IWindow window, boolean consumed) {
@@ -271,8 +246,8 @@
Slog.d(TAG_WM, "Drop result=" + consumed + " reported by " + token);
}
- synchronized (mWriteLock) {
- mCallback.reportDropResult(window, consumed);
+ mCallback.get().preReportDropResult(window, consumed);
+ try {
synchronized (mService.mWindowMap) {
if (mDragState == null) {
// Most likely the drop recipient ANRed and we ended the drag
@@ -297,10 +272,11 @@
return; // !!! TODO: throw here?
}
-
mDragState.mDragResult = consumed;
mDragState.endDragLocked();
}
+ } finally {
+ mCallback.get().postReportDropResult();
}
}
@@ -309,8 +285,8 @@
Slog.d(TAG_WM, "cancelDragAndDrop");
}
- synchronized (mWriteLock) {
- mCallback.cancelDragAndDrop(dragToken);
+ mCallback.get().preCancelDragAndDrop(dragToken);
+ try {
synchronized (mService.mWindowMap) {
if (mDragState == null) {
Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
@@ -327,6 +303,8 @@
mDragState.mDragResult = false;
mDragState.cancelDragLocked();
}
+ } finally {
+ mCallback.get().postCancelDragAndDrop();
}
}
@@ -338,20 +316,18 @@
* @param newY Y coordinate value in dp in the screen coordinate
*/
void handleMotionEvent(boolean keepHandling, float newX, float newY) {
- synchronized (mWriteLock) {
- synchronized (mService.mWindowMap) {
- if (!dragDropActiveLocked()) {
- // The drag has ended but the clean-up message has not been processed by
- // window manager. Drop events that occur after this until window manager
- // has a chance to clean-up the input handle.
- return;
- }
+ synchronized (mService.mWindowMap) {
+ if (!dragDropActiveLocked()) {
+ // The drag has ended but the clean-up message has not been processed by
+ // window manager. Drop events that occur after this until window manager
+ // has a chance to clean-up the input handle.
+ return;
+ }
- if (keepHandling) {
- mDragState.notifyMoveLocked(newX, newY);
- } else {
- mDragState.notifyDropLocked(newX, newY);
- }
+ if (keepHandling) {
+ mDragState.notifyMoveLocked(newX, newY);
+ } else {
+ mDragState.notifyDropLocked(newX, newY);
}
}
}
@@ -414,12 +390,11 @@
if (DEBUG_DRAG) {
Slog.w(TAG_WM, "Timeout starting drag by win " + win);
}
- synchronized (mWriteLock) {
- synchronized (mService.mWindowMap) {
- // !!! TODO: ANR the app that has failed to start the drag in time
- if (mDragState != null) {
- mDragState.closeLocked();
- }
+
+ synchronized (mService.mWindowMap) {
+ // !!! TODO: ANR the app that has failed to start the drag in time
+ if (mDragState != null) {
+ mDragState.closeLocked();
}
}
break;
@@ -430,13 +405,12 @@
if (DEBUG_DRAG) {
Slog.w(TAG_WM, "Timeout ending drag to win " + win);
}
- synchronized (mWriteLock) {
- synchronized (mService.mWindowMap) {
- // !!! TODO: ANR the drag-receiving app
- if (mDragState != null) {
- mDragState.mDragResult = false;
- mDragState.endDragLocked();
- }
+
+ synchronized (mService.mWindowMap) {
+ // !!! TODO: ANR the drag-receiving app
+ if (mDragState != null) {
+ mDragState.mDragResult = false;
+ mDragState.endDragLocked();
}
}
break;
@@ -455,15 +429,13 @@
}
case MSG_ANIMATION_END: {
- synchronized (mWriteLock) {
- synchronized (mService.mWindowMap) {
- if (mDragState == null) {
- Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " +
- "plyaing animation");
- return;
- }
- mDragState.closeLocked();
+ synchronized (mService.mWindowMap) {
+ if (mDragState == null) {
+ Slog.wtf(TAG_WM, "mDragState unexpectedly became null while " +
+ "plyaing animation");
+ return;
}
+ mDragState.closeLocked();
}
break;
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 112e62f..b9f437a 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -568,6 +568,14 @@
mToken = token;
}
+ /**
+ * Returns true if it has sent DRAG_STARTED broadcast out but has not been sent DRAG_END
+ * broadcast.
+ */
+ boolean isInProgress() {
+ return mDragInProgress;
+ }
+
private static DragEvent obtainDragEvent(WindowState win, int action,
float x, float y, Object localState,
ClipDescription description, ClipData data,
diff --git a/services/core/java/com/android/server/wm/RemoteEventTrace.java b/services/core/java/com/android/server/wm/RemoteEventTrace.java
index 9f65ba3..b214d35 100644
--- a/services/core/java/com/android/server/wm/RemoteEventTrace.java
+++ b/services/core/java/com/android/server/wm/RemoteEventTrace.java
@@ -20,6 +20,7 @@
import java.io.FileOutputStream;
import java.io.DataOutputStream;
+import android.os.StrictMode;
import android.util.Slog;
import android.os.Debug;
@@ -40,22 +41,28 @@
}
void openSurfaceTransaction() {
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try {
mOut.writeUTF("OpenTransaction");
writeSigil();
} catch (Exception e) {
logException(e);
mService.disableSurfaceTrace();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
}
}
void closeSurfaceTransaction() {
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try {
mOut.writeUTF("CloseTransaction");
writeSigil();
} catch (Exception e) {
logException(e);
mService.disableSurfaceTrace();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
}
}
diff --git a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
index a12c2c4..d2cbf88 100644
--- a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
+++ b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
@@ -20,6 +20,7 @@
import android.graphics.Region;
import android.os.IBinder;
import android.os.Parcel;
+import android.os.StrictMode;
import android.util.Slog;
import android.view.SurfaceControl;
@@ -54,67 +55,122 @@
@Override
public void setAlpha(float alpha) {
- writeFloatEvent("Alpha", alpha);
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeFloatEvent("Alpha", alpha);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setAlpha(alpha);
}
@Override
public void setLayer(int zorder) {
- writeIntEvent("Layer", zorder);
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeIntEvent("Layer", zorder);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setLayer(zorder);
}
@Override
public void setPosition(float x, float y) {
- writeFloatEvent("Position", x, y);
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeFloatEvent("Position", x, y);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setPosition(x, y);
}
@Override
public void setGeometryAppliesWithResize() {
- writeEvent("GeometryAppliesWithResize");
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeEvent("GeometryAppliesWithResize");
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setGeometryAppliesWithResize();
}
@Override
public void setSize(int w, int h) {
- writeIntEvent("Size", w, h);
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeIntEvent("Size", w, h);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setSize(w, h);
}
@Override
public void setWindowCrop(Rect crop) {
- writeRectEvent("Crop", crop);
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeRectEvent("Crop", crop);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setWindowCrop(crop);
}
@Override
public void setFinalCrop(Rect crop) {
- writeRectEvent("FinalCrop", crop);
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeRectEvent("FinalCrop", crop);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setFinalCrop(crop);
}
@Override
public void setLayerStack(int layerStack) {
- writeIntEvent("LayerStack", layerStack);
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeIntEvent("LayerStack", layerStack);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setLayerStack(layerStack);
}
@Override
public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
- writeFloatEvent("Matrix", dsdx, dtdx, dsdy, dtdy);
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeFloatEvent("Matrix", dsdx, dtdx, dsdy, dtdy);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.setMatrix(dsdx, dtdx, dsdy, dtdy);
}
@Override
public void hide() {
- writeEvent("Hide");
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeEvent("Hide");
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.hide();
}
@Override
public void show() {
- writeEvent("Show");
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ writeEvent("Show");
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
super.show();
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index b08eb18..e653e7d 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -431,7 +431,7 @@
if (w.mAppOp == OP_NONE) {
return;
}
- final int mode = mService.mAppOps.checkOpNoThrow(w.mAppOp, w.getOwningUid(),
+ final int mode = mService.mAppOps.noteOpNoThrow(w.mAppOp, w.getOwningUid(),
w.getOwningPackage());
w.setAppOpVisibilityLw(mode == MODE_ALLOWED || mode == MODE_DEFAULT);
}, false /* traverseTopToBottom */);
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 5bc2722..3ef9b3f 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -29,7 +29,6 @@
import android.view.Choreographer;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
-import android.view.animation.Transformation;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -44,12 +43,19 @@
private final Object mLock = new Object();
+ /**
+ * Lock for cancelling animations. Must be acquired on it's own, or after acquiring
+ * {@link #mLock}
+ */
+ private final Object mCancelLock = new Object();
+
@VisibleForTesting
Choreographer mChoreographer;
private final Runnable mApplyTransactionRunnable = this::applyTransaction;
private final AnimationHandler mAnimationHandler;
private final Transaction mFrameTransaction;
+ private final AnimatorFactory mAnimatorFactory;
private boolean mApplyScheduled;
@GuardedBy("mLock")
@@ -58,15 +64,15 @@
@GuardedBy("mLock")
@VisibleForTesting
- final ArrayMap<SurfaceControl, ValueAnimator> mRunningAnimations = new ArrayMap<>();
+ final ArrayMap<SurfaceControl, RunningAnimation> mRunningAnimations = new ArrayMap<>();
SurfaceAnimationRunner() {
- this(null /* callbackProvider */, new Transaction());
+ this(null /* callbackProvider */, null /* animatorFactory */, new Transaction());
}
@VisibleForTesting
SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
- Transaction frameTransaction) {
+ AnimatorFactory animatorFactory, Transaction frameTransaction) {
SurfaceAnimationThread.getHandler().runWithScissors(() -> mChoreographer = getSfInstance(),
0 /* timeout */);
mFrameTransaction = frameTransaction;
@@ -74,6 +80,9 @@
mAnimationHandler.setProvider(callbackProvider != null
? callbackProvider
: new SfVsyncFrameCallbackProvider(mChoreographer));
+ mAnimatorFactory = animatorFactory != null
+ ? animatorFactory
+ : SfValueAnimator::new;
}
void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
@@ -94,18 +103,17 @@
synchronized (mLock) {
if (mPendingAnimations.containsKey(leash)) {
mPendingAnimations.remove(leash);
- // TODO: Releasing the leash is problematic if reparenting hasn't happened yet.
- // Fix with transaction
- //leash.release();
return;
}
- final ValueAnimator anim = mRunningAnimations.get(leash);
+ final RunningAnimation anim = mRunningAnimations.get(leash);
if (anim != null) {
mRunningAnimations.remove(leash);
+ synchronized (mCancelLock) {
+ anim.mCancelled = true;
+ }
SurfaceAnimationThread.getHandler().post(() -> {
- anim.cancel();
+ anim.mAnim.cancel();
applyTransaction();
- //leash.release();
});
}
}
@@ -119,54 +127,51 @@
}
private void startAnimationLocked(RunningAnimation a) {
- final ValueAnimator result = new SfValueAnimator();
+ final ValueAnimator anim = mAnimatorFactory.makeAnimator();
// Animation length is already expected to be scaled.
- result.overrideDurationScale(1.0f);
- result.setDuration(a.animSpec.getDuration());
- result.addUpdateListener(animation -> {
- applyTransformation(a, mFrameTransaction, result.getCurrentPlayTime());
+ anim.overrideDurationScale(1.0f);
+ anim.setDuration(a.mAnimSpec.getDuration());
+ anim.addUpdateListener(animation -> {
+ synchronized (mCancelLock) {
+ if (!a.mCancelled) {
+ applyTransformation(a, mFrameTransaction, anim.getCurrentPlayTime());
+ }
+ }
// Transaction will be applied in the commit phase.
scheduleApplyTransaction();
});
- result.addListener(new AnimatorListenerAdapter() {
-
- private boolean mCancelled;
-
+ anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- mFrameTransaction.show(a.leash);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
+ synchronized (mCancelLock) {
+ if (!a.mCancelled) {
+ mFrameTransaction.show(a.mLeash);
+ }
+ }
}
@Override
public void onAnimationEnd(Animator animation) {
synchronized (mLock) {
- mRunningAnimations.remove(a.leash);
- }
- if (!mCancelled) {
- // Post on other thread that we can push final state without jank.
- AnimationThread.getHandler().post(() -> {
- a.finishCallback.run();
-
- // Make sure to release the leash after finishCallback has been invoked such
- // that reparenting is done already when releasing the leash.
- a.leash.release();
- });
+ mRunningAnimations.remove(a.mLeash);
+ synchronized (mCancelLock) {
+ if (!a.mCancelled) {
+ // Post on other thread that we can push final state without jank.
+ AnimationThread.getHandler().post(a.mFinishCallback);
+ }
+ }
}
}
});
- result.start();
- mRunningAnimations.put(a.leash, result);
+ anim.start();
+ a.mAnim = anim;
+ mRunningAnimations.put(a.mLeash, a);
}
private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
- a.animSpec.apply(t, a.leash, currentPlayTime);
+ a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
}
private void stepAnimation(long frameTimeNanos) {
@@ -189,17 +194,26 @@
}
private static final class RunningAnimation {
- final AnimationSpec animSpec;
- final SurfaceControl leash;
- final Runnable finishCallback;
+ final AnimationSpec mAnimSpec;
+ final SurfaceControl mLeash;
+ final Runnable mFinishCallback;
+ ValueAnimator mAnim;
+
+ @GuardedBy("mCancelLock")
+ private boolean mCancelled;
RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) {
- this.animSpec = animSpec;
- this.leash = leash;
- this.finishCallback = finishCallback;
+ mAnimSpec = animSpec;
+ mLeash = leash;
+ mFinishCallback = finishCallback;
}
}
+ @VisibleForTesting
+ interface AnimatorFactory {
+ ValueAnimator makeAnimator();
+ }
+
/**
* Value animator that uses sf-vsync signal to tick.
*/
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 713d58b..e165211 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -97,7 +97,7 @@
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
cancelAnimation(t, true /* restarting */);
mAnimation = anim;
- final SurfaceControl surface = mAnimatable.getSurface();
+ final SurfaceControl surface = mAnimatable.getSurfaceControl();
if (surface == null) {
Slog.w(TAG, "Unable to start animation, surface is null or no children.");
cancelAnimation();
@@ -105,7 +105,7 @@
}
mLeash = createAnimationLeash(surface, t,
mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden);
- mAnimatable.onLeashCreated(t, mLeash);
+ mAnimatable.onAnimationLeashCreated(t, mLeash);
if (mAnimationStartDelayed) {
if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
return;
@@ -169,7 +169,16 @@
* surface is reparented to the leash. This method takes care of that.
*/
void setLayer(Transaction t, int layer) {
- t.setLayer(mLeash != null ? mLeash : mAnimatable.getSurface(), layer);
+ t.setLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), layer);
+ }
+
+ /**
+ * Sets the surface to be relatively layered.
+ *
+ * @see #setLayer
+ */
+ void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
+ t.setRelativeLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), relativeTo, layer);
}
/**
@@ -178,7 +187,7 @@
* @see #setLayer
*/
void reparent(Transaction t, SurfaceControl newParent) {
- t.reparent(mLeash != null ? mLeash : mAnimatable.getSurface(), newParent.getHandle());
+ t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent.getHandle());
}
/**
@@ -207,8 +216,8 @@
}
private void reset(Transaction t) {
- final SurfaceControl surface = mAnimatable.getSurface();
- final SurfaceControl parent = mAnimatable.getParentSurface();
+ final SurfaceControl surface = mAnimatable.getSurfaceControl();
+ final SurfaceControl parent = mAnimatable.getParentSurfaceControl();
// If the surface was destroyed, we don't care to reparent it back.
final boolean destroy = mLeash != null && surface != null && parent != null;
@@ -216,19 +225,22 @@
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent");
t.reparent(surface, parent.getHandle());
}
+ if (mLeash != null) {
+ mAnimatable.destroyAfterPendingTransaction(mLeash);
+ }
mLeash = null;
mAnimation = null;
// Make sure to inform the animatable after the leash was destroyed.
if (destroy) {
- mAnimatable.onLeashDestroyed(t);
+ mAnimatable.onAnimationLeashDestroyed(t);
}
}
private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width,
int height, boolean hidden) {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
- final SurfaceControl.Builder builder = mAnimatable.makeLeash()
+ final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
.setName(surface + " - animation-leash")
.setSize(width, height);
final SurfaceControl leash = builder.build();
@@ -273,7 +285,7 @@
* @param t The transaction to use to apply any necessary changes.
* @param leash The leash that was created.
*/
- void onLeashCreated(Transaction t, SurfaceControl leash);
+ void onAnimationLeashCreated(Transaction t, SurfaceControl leash);
/**
* Called when the leash is being destroyed, and the surface was reparented back to the
@@ -281,22 +293,30 @@
*
* @param t The transaction to use to apply any necessary changes.
*/
- void onLeashDestroyed(Transaction t);
+ void onAnimationLeashDestroyed(Transaction t);
/**
- * @return A new child surface.
+ * Destroy a given surface after executing {@link #getPendingTransaction}.
+ *
+ * @see WindowContainer#destroyAfterPendingTransaction
*/
- SurfaceControl.Builder makeLeash();
+ void destroyAfterPendingTransaction(SurfaceControl surface);
+
+ /**
+ * @return A new surface to be used for the animation leash, inserted at the correct
+ * position in the hierarchy.
+ */
+ SurfaceControl.Builder makeAnimationLeash();
/**
* @return The surface of the object to be animated.
*/
- @Nullable SurfaceControl getSurface();
+ @Nullable SurfaceControl getSurfaceControl();
/**
* @return The parent of the surface object to be animated.
*/
- @Nullable SurfaceControl getParentSurface();
+ @Nullable SurfaceControl getParentSurfaceControl();
/**
* @return The width of the surface to be animated.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6ea8a47..244eb66 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -22,7 +22,6 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.res.Configuration.EMPTY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static com.android.server.EventLogTags.WM_TASK_REMOVED;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
@@ -43,7 +42,6 @@
import android.util.EventLog;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import android.view.DisplayInfo;
import android.view.Surface;
import com.android.internal.annotations.VisibleForTesting;
@@ -162,7 +160,7 @@
boolean shouldDeferRemoval() {
// TODO: This should probably return false if mChildren.isEmpty() regardless if the stack
// is animating...
- return hasWindowsAlive() && mStack.isAnimating();
+ return hasWindowsAlive() && mStack.isSelfOrChildAnimating();
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index ca9f3a9..fa7ea2f 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -203,7 +203,7 @@
// Post back to WM to handle clean-ups. We still need the input
// event handler for the last finishInputEvent()!
- mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING);
+ mService.mTaskPositioningController.finishTaskPositioning();
}
handled = true;
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index bb5706c..4dfe290 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -22,6 +22,8 @@
import android.annotation.Nullable;
import android.app.IActivityManager;
import android.os.RemoteException;
+import android.os.Handler;
+import android.os.Looper;
import android.util.Slog;
import android.view.Display;
import android.view.IWindow;
@@ -37,6 +39,7 @@
private final InputManagerService mInputManager;
private final InputMonitor mInputMonitor;
private final IActivityManager mActivityManager;
+ private final Handler mHandler;
@GuardedBy("WindowManagerSerivce.mWindowMap")
private @Nullable TaskPositioner mTaskPositioner;
@@ -50,11 +53,12 @@
}
TaskPositioningController(WindowManagerService service, InputManagerService inputManager,
- InputMonitor inputMonitor, IActivityManager activityManager) {
+ InputMonitor inputMonitor, IActivityManager activityManager, Looper looper) {
mService = service;
mInputMonitor = inputMonitor;
mInputManager = inputManager;
mActivityManager = activityManager;
+ mHandler = new Handler(looper);
}
boolean startMovingTask(IWindow window, float startX, float startY) {
@@ -75,24 +79,27 @@
}
void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
- int taskId = -1;
- synchronized (mService.mWindowMap) {
- final Task task = displayContent.findTaskForResizePoint(x, y);
- if (task != null) {
- if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
- task.preserveOrientationOnResize(), x, y)) {
- return;
+ mHandler.post(() -> {
+ int taskId = -1;
+ synchronized (mService.mWindowMap) {
+ final Task task = displayContent.findTaskForResizePoint(x, y);
+ if (task != null) {
+ if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
+ task.preserveOrientationOnResize(), x, y)) {
+ return;
+ }
+ taskId = task.mTaskId;
+ } else {
+ taskId = displayContent.taskIdFromPoint(x, y);
}
- taskId = task.mTaskId;
- } else {
- taskId = displayContent.taskIdFromPoint(x, y);
}
- }
- if (taskId >= 0) {
- try {
- mActivityManager.setFocusedTask(taskId);
- } catch(RemoteException e) {}
- }
+ if (taskId >= 0) {
+ try {
+ mActivityManager.setFocusedTask(taskId);
+ } catch (RemoteException e) {
+ }
+ }
+ });
}
private boolean startPositioningLocked(WindowState win, boolean resize,
@@ -145,15 +152,17 @@
return true;
}
- void finishPositioning() {
- if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning");
+ void finishTaskPositioning() {
+ mHandler.post(() -> {
+ if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning");
- synchronized (mService.mWindowMap) {
- if (mTaskPositioner != null) {
- mTaskPositioner.unregister();
- mTaskPositioner = null;
- mInputMonitor.updateInputWindowsLw(true /*force*/);
+ synchronized (mService.mWindowMap) {
+ if (mTaskPositioner != null) {
+ mTaskPositioner.unregister();
+ mTaskPositioner = null;
+ mInputMonitor.updateInputWindowsLw(true /*force*/);
+ }
}
- }
+ });
}
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 28b1390..9946c6a 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -935,7 +935,7 @@
@Override
void removeIfPossible() {
- if (isAnimating()) {
+ if (isSelfOrChildAnimating()) {
mDeferRemoval = true;
return;
}
@@ -1643,7 +1643,7 @@
/** Returns true if a removal action is still being deferred. */
boolean checkCompleteDeferredRemoval() {
- if (isAnimating()) {
+ if (isSelfOrChildAnimating()) {
return true;
}
if (mDeferRemoval) {
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index 84ad576..5abda27 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -62,8 +62,8 @@
synchronized (this) {
if (!mTouchExcludeRegion.contains(x, y)) {
- mService.mH.obtainMessage(H.TAP_OUTSIDE_TASK,
- x, y, mDisplayContent).sendToTarget();
+ mService.mTaskPositioningController.handleTapOutsideTask(
+ mDisplayContent, x, y);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index a12c0e5..3389f71 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -115,7 +115,7 @@
void startAnimation(Animation anim) {
for (int ndx = mChildren.size() - 1; ndx >= 0; ndx--) {
final WindowState windowState = mChildren.get(ndx);
- windowState.mWinAnimator.setAnimation(anim);
+ windowState.startAnimation(anim);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 56175c7..bb25297 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -31,7 +31,7 @@
private Animation mAnimation;
private final Point mPosition = new Point();
- private final ThreadLocal<Tmp> mThreadLocalTmps = ThreadLocal.withInitial(Tmp::new);
+ private final ThreadLocal<TmpValues> mThreadLocalTmps = ThreadLocal.withInitial(TmpValues::new);
public WindowAnimationSpec(Animation animation, Point position) {
mAnimation = animation;
@@ -55,7 +55,7 @@
@Override
public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
- final Tmp tmp = mThreadLocalTmps.get();
+ final TmpValues tmp = mThreadLocalTmps.get();
tmp.transformation.clear();
mAnimation.getTransformation(currentPlayTime, tmp.transformation);
tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
@@ -63,7 +63,7 @@
t.setAlpha(leash, tmp.transformation.getAlpha());
}
- private static class Tmp {
+ private static class TmpValues {
final Transformation transformation = new Transformation();
final float[] floats = new float[9];
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index d6329bf..b2b6119 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -19,6 +19,9 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER;
import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION;
import static com.android.server.wm.proto.WindowContainerProto.VISIBLE;
@@ -26,14 +29,20 @@
import android.annotation.CallSuper;
import android.content.res.Configuration;
+import android.graphics.PixelFormat.Opacity;
+import android.util.Slog;
import android.view.MagnificationSpec;
import android.view.SurfaceControl;
+import android.view.SurfaceControl.Builder;
import android.view.SurfaceSession;
import android.util.Pools;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.util.ToBooleanFunction;
+import com.android.internal.util.ToBooleanFunction;
+import com.android.server.wm.SurfaceAnimator.Animatable;
+
+import java.io.PrintWriter;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.function.Consumer;
@@ -46,7 +55,9 @@
* changes are made to this class.
*/
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
- implements Comparable<WindowContainer> {
+ implements Comparable<WindowContainer>, Animatable {
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
static final int POSITION_TOP = Integer.MAX_VALUE;
static final int POSITION_BOTTOM = Integer.MIN_VALUE;
@@ -56,7 +67,7 @@
* For removing or setting new parent {@link #setParent} should be used, because it also
* performs configuration updates based on new parent's settings.
*/
- private WindowContainer mParent = null;
+ private WindowContainer<WindowContainer> mParent = null;
// List of children for this window container. List is in z-order as the children appear on
// screen with the top-most window container at the tail of the list.
@@ -69,7 +80,7 @@
new Pools.SynchronizedPool<>(3);
// The owner/creator for this container. No controller if null.
- WindowContainerController mController;
+ WindowContainerController mController;
protected SurfaceControl mSurfaceControl;
private int mLastLayer = 0;
@@ -78,12 +89,14 @@
/**
* Applied as part of the animation pass in "prepareSurfaces".
*/
- private final Transaction mPendingTransaction;
+ protected final Transaction mPendingTransaction;
+ protected final SurfaceAnimator mSurfaceAnimator;
protected final WindowManagerService mService;
WindowContainer(WindowManagerService service) {
mService = service;
mPendingTransaction = service.mTransactionFactory.make();
+ mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, service);
}
@Override
@@ -101,7 +114,7 @@
return mChildren.get(index);
}
- final protected void setParent(WindowContainer parent) {
+ final protected void setParent(WindowContainer<WindowContainer> parent) {
mParent = parent;
// Removing parent usually means that we've detached this entity to destroy it or to attach
// to another parent. In both cases we don't need to update the configuration now.
@@ -123,14 +136,18 @@
if (mParent == null) {
return;
}
+
if (mSurfaceControl == null) {
// If we don't yet have a surface, but we now have a parent, we should
// build a surface.
mSurfaceControl = makeSurface().build();
getPendingTransaction().show(mSurfaceControl);
} else {
- // If we have a surface but a new parent, we just need to perform a reparent.
- getPendingTransaction().reparent(mSurfaceControl, mParent.mSurfaceControl.getHandle());
+ // If we have a surface but a new parent, we just need to perform a reparent. Go through
+ // surface animator such that hierarchy is preserved when animating, i.e.
+ // mSurfaceControl stays attached to the leash and we just reparent the leash to the
+ // new parent.
+ mSurfaceAnimator.reparent(getPendingTransaction(), mParent.mSurfaceControl);
}
// Either way we need to ask the parent to assign us a Z-order.
@@ -139,8 +156,8 @@
}
// Temp. holders for a chain of containers we are currently processing.
- private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList();
- private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList();
+ private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList<>();
+ private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList<>();
/**
* Adds the input window container has a child of this container in order based on the input
@@ -214,7 +231,7 @@
@CallSuper
void removeImmediately() {
while (!mChildren.isEmpty()) {
- final WindowContainer child = mChildren.peekLast();
+ final E child = mChildren.peekLast();
child.removeImmediately();
// Need to do this after calling remove on the child because the child might try to
// remove/detach itself from its parent which will cause an exception if we remove
@@ -401,16 +418,40 @@
}
}
- boolean isAnimating() {
+ /**
+ * @return Whether our own container is running an animation or any child, no matter how deep in
+ * the hierarchy, is animating.
+ */
+ boolean isSelfOrChildAnimating() {
+ if (isSelfAnimating()) {
+ return true;
+ }
for (int j = mChildren.size() - 1; j >= 0; j--) {
final WindowContainer wc = mChildren.get(j);
- if (wc.isAnimating()) {
+ if (wc.isSelfOrChildAnimating()) {
return true;
}
}
return false;
}
+ /**
+ * @return Whether our own container is running an animation or our parent is animating. This
+ * doesn't consider whether children are animating.
+ */
+ boolean isAnimating() {
+
+ // We are animating if we ourselves are animating or if our parent is animating.
+ return isSelfAnimating() || mParent != null && mParent.isAnimating();
+ }
+
+ /**
+ * @return Whether our own container running an animation at the moment.
+ */
+ boolean isSelfAnimating() {
+ return mSurfaceAnimator.isAnimating();
+ }
+
void sendAppVisibilityToClients() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer wc = mChildren.get(i);
@@ -743,6 +784,15 @@
.setParent(mSurfaceControl);
}
+ @Override
+ public SurfaceControl getParentSurfaceControl() {
+ final WindowContainer parent = getParent();
+ if (parent == null) {
+ return null;
+ }
+ return parent.getSurfaceControl();
+ }
+
/**
* @return Whether this WindowContainer should be magnified by the accessibility magnifier.
*/
@@ -765,7 +815,10 @@
void assignLayer(Transaction t, int layer) {
final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null;
if (mSurfaceControl != null && changed) {
- t.setLayer(mSurfaceControl, layer);
+
+ // Route through surface animator to accommodate that our surface control might be
+ // attached to the leash, and leash is attached to parent container.
+ mSurfaceAnimator.setLayer(t, layer);
mLastLayer = layer;
mLastRelativeToLayer = null;
}
@@ -774,7 +827,10 @@
void assignRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
final boolean changed = layer != mLastLayer || mLastRelativeToLayer != relativeTo;
if (mSurfaceControl != null && changed) {
- t.setRelativeLayer(mSurfaceControl, relativeTo, layer);
+
+ // Route through surface animator to accommodate that our surface control might be
+ // attached to the leash, and leash is attached to parent container.
+ mSurfaceAnimator.setRelativeLayer(t, relativeTo, layer);
mLastLayer = layer;
mLastRelativeToLayer = relativeTo;
}
@@ -897,7 +953,8 @@
}
}
- SurfaceControl getSurfaceControl() {
+ @Override
+ public SurfaceControl getSurfaceControl() {
return mSurfaceControl;
}
@@ -907,13 +964,105 @@
* rather than an intentional design, so please take care when
* expanding use.
*/
- void destroyAfterPendingTransaction(SurfaceControl surface) {
+ @Override
+ public void destroyAfterPendingTransaction(SurfaceControl surface) {
if (mParent != null) {
mParent.destroyAfterPendingTransaction(surface);
}
}
-
- Transaction getPendingTransaction() {
+
+ @Override
+ public Transaction getPendingTransaction() {
return mPendingTransaction;
}
+
+ /**
+ * Starts an animation on the container.
+ *
+ * @param anim The animation to run.
+ * @param hidden Whether our container is currently hidden. TODO This should use isVisible at
+ * some point but the meaning is too weird to work for all containers.
+ */
+ void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
+ if (DEBUG_ANIM) Slog.v(TAG, "Starting animation on " + this + ": " + anim);
+
+ // TODO: This should use isVisible() but because isVisible has a really weird meaning at
+ // the moment this doesn't work for all animatable window containers.
+ mSurfaceAnimator.startAnimation(t, anim, hidden);
+ }
+
+ void cancelAnimation() {
+ mSurfaceAnimator.cancelAnimation();
+ }
+
+ @Override
+ public Builder makeAnimationLeash() {
+ return makeSurface();
+ }
+
+ @Override
+ public void commitPendingTransaction() {
+ scheduleAnimation();
+ }
+
+ private void reassignLayer(Transaction t) {
+ final WindowContainer parent = getParent();
+ if (parent != null) {
+ parent.assignChildLayers(t);
+ }
+ }
+
+ @Override
+ public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
+ reassignLayer(t);
+ }
+
+ @Override
+ public void onAnimationLeashDestroyed(Transaction t) {
+ reassignLayer(t);
+ }
+
+ /**
+ * Called when an animation has finished running.
+ */
+ protected void onAnimationFinished() {
+ }
+
+ /**
+ * @return The currently running animation, if any, or {@code null} otherwise.
+ */
+ AnimationAdapter getAnimation() {
+ return mSurfaceAnimator.getAnimation();
+ }
+
+ /**
+ * @see SurfaceAnimator#startDelayingAnimationStart
+ */
+ void startDelayingAnimationStart() {
+ mSurfaceAnimator.startDelayingAnimationStart();
+ }
+
+ /**
+ * @see SurfaceAnimator#endDelayingAnimationStart
+ */
+ void endDelayingAnimationStart() {
+ mSurfaceAnimator.endDelayingAnimationStart();
+ }
+
+ @Override
+ public int getSurfaceWidth() {
+ return mSurfaceControl.getWidth();
+ }
+
+ @Override
+ public int getSurfaceHeight() {
+ return mSurfaceControl.getHeight();
+ }
+
+ void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+ if (mSurfaceAnimator.isAnimating()) {
+ pw.print(prefix); pw.println("ContainerAnimator:");
+ mSurfaceAnimator.dump(pw, prefix + " ");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 036f7b0..62d2e7d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -151,23 +151,38 @@
*/
public interface IDragDropCallback {
/**
- * Called when drag operation is started.
+ * Called when drag operation is starting.
*/
- default boolean performDrag(IWindow window, IBinder dragToken,
+ default boolean prePerformDrag(IWindow window, IBinder dragToken,
int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
ClipData data) {
return true;
}
/**
- * Called when drop result is reported.
+ * Called when drag operation is started.
*/
- default void reportDropResult(IWindow window, boolean consumed) {}
+ default void postPerformDrag() {}
/**
- * Called when drag operation is cancelled.
+ * Called when drop result is being reported.
*/
- default void cancelDragAndDrop(IBinder dragToken) {}
+ default void preReportDropResult(IWindow window, boolean consumed) {}
+
+ /**
+ * Called when drop result was reported.
+ */
+ default void postReportDropResult() {}
+
+ /**
+ * Called when drag operation is being cancelled.
+ */
+ default void preCancelDragAndDrop(IBinder dragToken) {}
+
+ /**
+ * Called when drag operation was cancelled.
+ */
+ default void postCancelDragAndDrop() {}
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8c9948e..9fc9f3c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -725,8 +725,6 @@
}
}
- boolean mAnimateWallpaperWithTarget;
-
// TODO: Move to RootWindowContainer
AppWindowToken mFocusedApp = null;
@@ -1088,8 +1086,8 @@
mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
- mTaskPositioningController =
- new TaskPositioningController(this, mInputManager, mInputMonitor, mActivityManager);
+ mTaskPositioningController = new TaskPositioningController(
+ this, mInputManager, mInputMonitor, mActivityManager, mH.getLooper());
mDragDropController = new DragDropController(this, mH.getLooper());
LocalServices.addService(WindowManagerInternal.class, new LocalService());
@@ -2182,18 +2180,15 @@
if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
focusMayChange = isDefaultDisplay;
win.mAnimatingExit = true;
- win.mWinAnimator.mAnimating = true;
} else if (win.mWinAnimator.isAnimationSet()) {
// Currently in a hide animation... turn this into
// an exit.
win.mAnimatingExit = true;
- win.mWinAnimator.mAnimating = true;
} else if (win.getDisplayContent().mWallpaperController.isWallpaperTarget(win)) {
// If the wallpaper is currently behind this
// window, we need to change both of them inside
// of a transaction to avoid artifacts.
win.mAnimatingExit = true;
- win.mWinAnimator.mAnimating = true;
} else {
if (mInputMethodWindow == win) {
setInputMethodWindowLocked(null);
@@ -4566,6 +4561,7 @@
if (displayContent != null) {
mAnimator.addDisplayLocked(displayId);
displayContent.initializeDisplayBaseInfo();
+ reconfigureDisplayLocked(displayContent);
}
}
}
@@ -4613,7 +4609,6 @@
public static final int DO_ANIMATION_CALLBACK = 26;
public static final int CLIENT_FREEZE_TIMEOUT = 30;
- public static final int TAP_OUTSIDE_TASK = 31;
public static final int NOTIFY_ACTIVITY_DRAWN = 32;
public static final int ALL_WINDOWS_DRAWN = 33;
@@ -4627,8 +4622,6 @@
public static final int RESET_ANR_MESSAGE = 38;
public static final int WALLPAPER_DRAW_PENDING_TIMEOUT = 39;
- public static final int FINISH_TASK_POSITIONING = 40;
-
public static final int UPDATE_DOCKED_STACK_DIVIDER = 41;
public static final int WINDOW_REPLACEMENT_TIMEOUT = 46;
@@ -4905,17 +4898,6 @@
break;
}
- case TAP_OUTSIDE_TASK: {
- mTaskPositioningController.handleTapOutsideTask(
- (DisplayContent)msg.obj, msg.arg1, msg.arg2);
- }
- break;
-
- case FINISH_TASK_POSITIONING: {
- mTaskPositioningController.finishPositioning();
- }
- break;
-
case NOTIFY_ACTIVITY_DRAWN:
try {
mActivityManager.notifyActivityDrawn((IBinder) msg.obj);
@@ -6705,7 +6687,6 @@
synchronized (mWindowMap) {
final Display display = mDisplayManager.getDisplay(displayId);
if (display != null) {
- createDisplayContentLocked(display);
displayReady(displayId);
}
mWindowPlacerLocked.requestTraversal();
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 2358695..b9dc9db 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -18,9 +18,23 @@
import static android.os.Build.IS_USER;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.os.ShellCommand;
+import android.os.UserHandle;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.IWindowManager;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.PrintWriter;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* ShellCommands for WindowManagerService.
@@ -29,33 +43,279 @@
*/
public class WindowManagerShellCommand extends ShellCommand {
- private final WindowManagerService mService;
+ // IPC interface to activity manager -- don't need to do additional security checks.
+ private final IWindowManager mInterface;
+
+ // Internal service impl -- must perform security checks before touching.
+ private final WindowManagerService mInternal;
public WindowManagerShellCommand(WindowManagerService service) {
- mService = service;
+ mInterface = service;
+ mInternal = service;
}
@Override
public int onCommand(String cmd) {
- switch (cmd) {
- case "tracing":
- return mService.mWindowTracing.onShellCommand(this, getNextArgRequired());
- default:
- return handleDefaultCommands(cmd);
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
}
+ final PrintWriter pw = getOutPrintWriter();
+ try {
+ switch (cmd) {
+ case "size":
+ return runDisplaySize(pw);
+ case "density":
+ return runDisplayDensity(pw);
+ case "overscan":
+ return runDisplayOverscan(pw);
+ case "scaling":
+ return runDisplayScaling(pw);
+ case "screen-capture":
+ return runSetScreenCapture(pw);
+ case "dismiss-keyguard":
+ return runDismissKeyguard(pw);
+ case "surface-trace":
+ return runSurfaceTrace(pw);
+ case "tracing":
+ // XXX this should probably be changed to use openFileForSystem() to create
+ // the output trace file, so the shell gets the correct semantics for where
+ // trace files can be written.
+ return mInternal.mWindowTracing.onShellCommand(this,
+ getNextArgRequired());
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (RemoteException e) {
+ pw.println("Remote exception: " + e);
+ }
+ return -1;
+ }
+
+ private int runDisplaySize(PrintWriter pw) throws RemoteException {
+ String size = getNextArg();
+ int w, h;
+ if (size == null) {
+ Point initialSize = new Point();
+ Point baseSize = new Point();
+ try {
+ mInterface.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize);
+ mInterface.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize);
+ pw.println("Physical size: " + initialSize.x + "x" + initialSize.y);
+ if (!initialSize.equals(baseSize)) {
+ pw.println("Override size: " + baseSize.x + "x" + baseSize.y);
+ }
+ } catch (RemoteException e) {
+ }
+ return 0;
+ } else if ("reset".equals(size)) {
+ w = h = -1;
+ } else {
+ int div = size.indexOf('x');
+ if (div <= 0 || div >= (size.length()-1)) {
+ getErrPrintWriter().println("Error: bad size " + size);
+ return -1;
+ }
+ String wstr = size.substring(0, div);
+ String hstr = size.substring(div+1);
+ try {
+ w = parseDimension(wstr);
+ h = parseDimension(hstr);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad number " + e);
+ return -1;
+ }
+ }
+
+ if (w >= 0 && h >= 0) {
+ // TODO(multidisplay): For now Configuration only applies to main screen.
+ mInterface.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h);
+ } else {
+ mInterface.clearForcedDisplaySize(Display.DEFAULT_DISPLAY);
+ }
+ return 0;
+ }
+
+ private int runDisplayDensity(PrintWriter pw) throws RemoteException {
+ String densityStr = getNextArg();
+ int density;
+ if (densityStr == null) {
+ try {
+ int initialDensity = mInterface.getInitialDisplayDensity(Display.DEFAULT_DISPLAY);
+ int baseDensity = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
+ pw.println("Physical density: " + initialDensity);
+ if (initialDensity != baseDensity) {
+ pw.println("Override density: " + baseDensity);
+ }
+ } catch (RemoteException e) {
+ }
+ return 0;
+ } else if ("reset".equals(densityStr)) {
+ density = -1;
+ } else {
+ try {
+ density = Integer.parseInt(densityStr);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad number " + e);
+ return -1;
+ }
+ if (density < 72) {
+ getErrPrintWriter().println("Error: density must be >= 72");
+ return -1;
+ }
+ }
+
+ if (density > 0) {
+ // TODO(multidisplay): For now Configuration only applies to main screen.
+ mInterface.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density,
+ UserHandle.USER_CURRENT);
+ } else {
+ mInterface.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY,
+ UserHandle.USER_CURRENT);
+ }
+ return 0;
+ }
+
+ private int runDisplayOverscan(PrintWriter pw) throws RemoteException {
+ String overscanStr = getNextArgRequired();
+ Rect rect = new Rect();
+ if ("reset".equals(overscanStr)) {
+ rect.set(0, 0, 0, 0);
+ } else {
+ final Pattern FLATTENED_PATTERN = Pattern.compile(
+ "(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)");
+ Matcher matcher = FLATTENED_PATTERN.matcher(overscanStr);
+ if (!matcher.matches()) {
+ getErrPrintWriter().println("Error: bad rectangle arg: " + overscanStr);
+ return -1;
+ }
+ rect.left = Integer.parseInt(matcher.group(1));
+ rect.top = Integer.parseInt(matcher.group(2));
+ rect.right = Integer.parseInt(matcher.group(3));
+ rect.bottom = Integer.parseInt(matcher.group(4));
+ }
+
+ mInterface.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right,
+ rect.bottom);
+ return 0;
+ }
+
+ private int runDisplayScaling(PrintWriter pw) throws RemoteException {
+ String scalingStr = getNextArgRequired();
+ if ("auto".equals(scalingStr)) {
+ mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 0);
+ } else if ("off".equals(scalingStr)) {
+ mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1);
+ } else {
+ getErrPrintWriter().println("Error: scaling must be 'auto' or 'off'");
+ return -1;
+ }
+ return 0;
+ }
+
+ private int runSetScreenCapture(PrintWriter pw) throws RemoteException {
+ String userIdStr = getNextArg();
+ String enableStr = getNextArg();
+ int userId;
+ boolean disable;
+
+ try {
+ userId = Integer.parseInt(userIdStr);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad number " + e);
+ return -1;
+ }
+
+ disable = !Boolean.parseBoolean(enableStr);
+ mInternal.setScreenCaptureDisabled(userId, disable);
+ return 0;
+ }
+
+ private int runDismissKeyguard(PrintWriter pw) throws RemoteException {
+ mInterface.dismissKeyguard(null /* callback */);
+ return 0;
+ }
+
+ private int runSurfaceTrace(PrintWriter pw) throws RemoteException {
+ final ParcelFileDescriptor pfd;
+ try {
+ pfd = ParcelFileDescriptor.dup(getOutFileDescriptor());
+ } catch (IOException e) {
+ getErrPrintWriter().println("Unable to dup output stream: " + e.getMessage());
+ return -1;
+ }
+ mInternal.enableSurfaceTrace(pfd);
+
+ // Read input until an explicit quit command is sent or the stream is closed (meaning
+ // the user killed the command).
+ try {
+ InputStream input = getRawInputStream();
+ InputStreamReader converter = new InputStreamReader(input);
+ BufferedReader in = new BufferedReader(converter);
+ String line;
+
+ while ((line = in.readLine()) != null) {
+ if (line.length() <= 0) {
+ // no-op
+ } else if ("q".equals(line) || "quit".equals(line)) {
+ break;
+ } else {
+ pw.println("Invalid command: " + line);
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace(pw);
+ } finally {
+ mInternal.disableSurfaceTrace();
+ try {
+ pfd.close();
+ } catch (IOException e) {
+ }
+ }
+
+ return 0;
+ }
+
+ private int parseDimension(String s) throws NumberFormatException {
+ if (s.endsWith("px")) {
+ return Integer.parseInt(s.substring(0, s.length() - 2));
+ }
+ if (s.endsWith("dp")) {
+ int density;
+ try {
+ density = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
+ } catch (RemoteException e) {
+ density = DisplayMetrics.DENSITY_DEFAULT;
+ }
+ return Integer.parseInt(s.substring(0, s.length() - 2)) * density /
+ DisplayMetrics.DENSITY_DEFAULT;
+ }
+ return Integer.parseInt(s);
}
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
- pw.println("Window Manager (window) commands:");
+ pw.println("Window manager (window) commands:");
pw.println(" help");
- pw.println(" Print this help text.");
- pw.println();
- if (!IS_USER){
+ pw.println(" Print this help text.");
+ pw.println(" size [reset|WxH|WdpxHdp]");
+ pw.println(" Return or override display size.");
+ pw.println(" width and height in pixels unless suffixed with 'dp'.");
+ pw.println(" density [reset|DENSITY]");
+ pw.println(" Return or override display density.");
+ pw.println(" overscan [reset|LEFT,TOP,RIGHT,BOTTOM]");
+ pw.println(" Set overscan area for display.");
+ pw.println(" scaling [off|auto]");
+ pw.println(" Set display scaling mode.");
+ pw.println(" screen-capture [userId] [true|false]");
+ pw.println(" Enable or disable screen capture.");
+ pw.println(" dismiss-keyguard");
+ pw.println(" Dismiss the keyguard, prompting user for auth if necessary.");
+ pw.println(" surface-trace");
+ pw.println(" Log surface commands to stdout in a binary format.");
+ if (!IS_USER) {
pw.println(" tracing (start | stop)");
- pw.println(" start or stop window tracing");
- pw.println();
+ pw.println(" Start or stop window tracing.");
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e38605d..ddc1eac 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -19,11 +19,11 @@
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.SurfaceControl.Transaction;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
-import static android.view.SurfaceControl.Transaction;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
@@ -91,6 +91,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
+import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
@@ -117,7 +118,9 @@
import static com.android.server.wm.proto.WindowStateProto.IDENTIFIER;
import static com.android.server.wm.proto.WindowStateProto.PARENT_FRAME;
import static com.android.server.wm.proto.WindowStateProto.STACK_ID;
+import static com.android.server.wm.proto.WindowStateProto.SHOWN_POSITION;
import static com.android.server.wm.proto.WindowStateProto.SURFACE_INSETS;
+import static com.android.server.wm.proto.WindowStateProto.SURFACE_POSITION;
import static com.android.server.wm.proto.WindowStateProto.WINDOW_CONTAINER;
import android.annotation.CallSuper;
@@ -160,10 +163,14 @@
import android.view.ViewTreeObserver;
import android.view.WindowInfo;
import android.view.WindowManager;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.input.InputWindowHandle;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -594,6 +601,8 @@
*/
private boolean mDrawnStateEvaluated;
+ private final Point mSurfacePosition = new Point();
+
/**
* Compares two window sub-layers and returns -1 if the first is lesser than the second in terms
* of z-order and 1 otherwise.
@@ -1470,9 +1479,9 @@
final AppWindowToken atoken = mAppToken;
if (atoken != null) {
return ((!isParentWindowHidden() && !atoken.hiddenRequested)
- || mWinAnimator.mAnimation != null || atoken.mAppAnimator.animation != null);
+ || mWinAnimator.isAnimationSet() || atoken.mAppAnimator.animation != null);
}
- return !isParentWindowHidden() || mWinAnimator.mAnimation != null;
+ return !isParentWindowHidden() || mWinAnimator.isAnimationSet();
}
/**
@@ -1505,7 +1514,7 @@
}
return mHasSurface && mPolicyVisibility && !mDestroying
&& ((!isParentWindowHidden() && mViewVisibility == View.VISIBLE && !mToken.hidden)
- || mWinAnimator.mAnimation != null
+ || mWinAnimator.isAnimationSet()
|| ((mAppToken != null) && (mAppToken.mAppAnimator.animation != null)));
}
@@ -1520,8 +1529,7 @@
// started.
final boolean appAnimationStarting = mAppToken != null
&& mAppToken.mAppAnimator.isAnimationStarting();
- final boolean exitingSelf = mAnimatingExit && (!mWinAnimator.isAnimationStarting()
- && !appAnimationStarting);
+ final boolean exitingSelf = mAnimatingExit && !appAnimationStarting;
final boolean appExiting = mAppToken != null && mAppToken.hidden && !appAnimationStarting;
final boolean exiting = exitingSelf || mDestroying || appExiting;
@@ -1544,7 +1552,7 @@
return isDrawnLw() && mPolicyVisibility
&& ((!isParentWindowHidden() &&
(atoken == null || !atoken.hiddenRequested))
- || mWinAnimator.mAnimating
+ || mWinAnimator.isAnimationSet()
|| (atoken != null && atoken.mAppAnimator.animation != null));
}
@@ -1553,7 +1561,7 @@
*/
@Override
public boolean isAnimatingLw() {
- return mWinAnimator.mAnimation != null
+ return mWinAnimator.isAnimationSet()
|| (mAppToken != null && mAppToken.mAppAnimator.animation != null);
}
@@ -1600,7 +1608,7 @@
// to determine if it's occluding apps.
return ((!mIsWallpaper && mAttrs.format == PixelFormat.OPAQUE)
|| (mIsWallpaper && mWallpaperVisible))
- && isDrawnLw() && mWinAnimator.mAnimation == null
+ && isDrawnLw() && !mWinAnimator.isAnimationSet()
&& (mAppToken == null || mAppToken.mAppAnimator.animation == null);
}
@@ -1732,7 +1740,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() {
+ void handleWindowMovedIfNeeded(Transaction t) {
if (!hasMoved()) {
return;
}
@@ -1750,7 +1758,7 @@
&& !isDragResizing() && !adjustedForMinimizedDockOrIme
&& getWindowConfiguration().hasMovementAnimations()
&& !mWinAnimator.mLastHidden) {
- mWinAnimator.setMoveAnimation(left, top);
+ startMoveAnimation(t, left, top);
}
//TODO (multidisplay): Accessibility supported only for the default display.
@@ -2457,10 +2465,10 @@
if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this);
if (doAnimation) {
if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
- + mPolicyVisibility + " mAnimation=" + mWinAnimator.mAnimation);
+ + mPolicyVisibility + " isAnimationSet=" + mWinAnimator.isAnimationSet());
if (!mToken.okToAnimate()) {
doAnimation = false;
- } else if (mPolicyVisibility && mWinAnimator.mAnimation == null) {
+ } else if (mPolicyVisibility && !mWinAnimator.isAnimationSet()) {
// Check for the case where we are currently visible and
// not animating; we do not want to do animation at such a
// point to become visible when we already are.
@@ -2499,7 +2507,7 @@
}
if (doAnimation) {
mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
- if (mWinAnimator.mAnimation == null) {
+ if (!mWinAnimator.isAnimationSet()) {
doAnimation = false;
}
}
@@ -2599,14 +2607,6 @@
return mAnimatingExit || (mService.mClosingApps.contains(mAppToken));
}
- @Override
- boolean isAnimating() {
- if (mWinAnimator.isAnimationSet() || mAnimatingExit) {
- return true;
- }
- return super.isAnimating();
- }
-
void addWinAnimatorToList(ArrayList<WindowStateAnimator> animators) {
animators.add(mWinAnimator);
@@ -3114,6 +3114,8 @@
mContentFrame.writeToProto(proto, CONTENT_FRAME);
mContentInsets.writeToProto(proto, CONTENT_INSETS);
mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS);
+ mSurfacePosition.writeToProto(proto, SURFACE_POSITION);
+ mShownPosition.writeToProto(proto, SHOWN_POSITION);
mWinAnimator.writeToProto(proto, ANIMATOR);
proto.write(ANIMATING_EXIT, mAnimatingExit);
for (int i = 0; i < mChildren.size(); i++) {
@@ -3133,6 +3135,7 @@
proto.end(token);
}
+ @Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
final TaskStack stack = getStack();
pw.print(prefix); pw.print("mDisplayId="); pw.print(getDisplayId());
@@ -3275,6 +3278,7 @@
pw.print(" cutout=" + mLastDisplayCutout);
pw.println();
}
+ super.dump(pw, prefix, dumpAll);
pw.print(prefix); pw.print(mWinAnimator); pw.println(":");
mWinAnimator.dump(pw, prefix + " ", dumpAll);
if (mAnimatingExit || mRemoveOnExit || mDestroying || mRemoved) {
@@ -3333,7 +3337,7 @@
@Override
String getName() {
return Integer.toHexString(System.identityHashCode(this))
- + " " + getWindowTag();
+ + " " + getWindowTag();
}
CharSequence getWindowTag() {
@@ -3694,7 +3698,7 @@
+ " tok.hiddenRequested="
+ (mAppToken != null && mAppToken.hiddenRequested)
+ " tok.hidden=" + (mAppToken != null && mAppToken.hidden)
- + " animating=" + mWinAnimator.mAnimating
+ + " animationSet=" + mWinAnimator.isAnimationSet()
+ " tok animating="
+ (mWinAnimator.mAppAnimator != null && mWinAnimator.mAppAnimator.animating)
+ " Callers=" + Debug.getCallers(4));
@@ -3900,23 +3904,10 @@
return null;
}
- boolean isWindowAnimationSet() {
- if (mWinAnimator.isWindowAnimationSet()) {
- return true;
- }
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowState c = mChildren.get(i);
- if (c.isWindowAnimationSet()) {
- return true;
- }
- }
- return false;
- }
-
void onExitAnimationDone() {
if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
+ ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
- + " windowAnimating=" + mWinAnimator.isWindowAnimationSet());
+ + " selfAnimating=" + isSelfAnimating());
if (!mChildren.isEmpty()) {
// Copying to a different list as multiple children can be removed.
@@ -3940,18 +3931,16 @@
}
}
- if (!mWinAnimator.isWindowAnimationSet()) {
- //TODO (multidisplay): Accessibility is supported only for the default display.
- if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
- mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
- }
- }
-
- if (!mAnimatingExit) {
+ if (isSelfAnimating()) {
return;
}
- if (mWinAnimator.isWindowAnimationSet()) {
+ //TODO (multidisplay): Accessibility is supported only for the default display.
+ if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+ mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+ }
+
+ if (!mAnimatingExit) {
return;
}
@@ -4005,10 +3994,6 @@
mAnimatingExit = false;
didSomething = true;
}
- if (mWinAnimator.mAnimating) {
- mWinAnimator.mAnimating = false;
- didSomething = true;
- }
if (mDestroying) {
mDestroying = false;
mService.mDestroySurface.remove(this);
@@ -4097,7 +4082,7 @@
+ " mDrawState=" + mWinAnimator.mDrawState
+ " ph=" + isParentWindowHidden()
+ " th=" + (mAppToken != null ? mAppToken.hiddenRequested : false)
- + " a=" + mWinAnimator.mAnimating);
+ + " a=" + mWinAnimator.isAnimationSet());
}
}
@@ -4300,6 +4285,58 @@
mLastDisplayCutout = mDisplayCutout;
}
+ void startAnimation(Animation anim) {
+ final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
+ anim.initialize(mFrame.width(), mFrame.height(),
+ displayInfo.appWidth, displayInfo.appHeight);
+ anim.restrictDuration(MAX_ANIMATION_DURATION);
+ anim.scaleCurrentDuration(mService.getWindowAnimationScaleLocked());
+ final AnimationAdapter adapter = new LocalAnimationAdapter(
+ new WindowAnimationSpec(anim, mSurfacePosition), mService.mSurfaceAnimationRunner);
+ startAnimation(mPendingTransaction, adapter);
+ commitPendingTransaction();
+ }
+
+ private void startMoveAnimation(Transaction t, 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();
+ transformFrameToSurfacePosition(mLastFrame.left, mLastFrame.top, oldPosition);
+ transformFrameToSurfacePosition(left, top, newPosition);
+ final AnimationAdapter adapter = new LocalAnimationAdapter(
+ new MoveAnimationSpec(oldPosition.x, oldPosition.y, newPosition.x, newPosition.y),
+ mService.mSurfaceAnimationRunner);
+ startAnimation(t, adapter);
+ }
+
+ private void startAnimation(Transaction t, AnimationAdapter adapter) {
+ startAnimation(t, adapter, mWinAnimator.mLastHidden);
+ }
+
+ @Override
+ protected void onAnimationFinished() {
+ mWinAnimator.onAnimationFinished();
+ }
+
+ /**
+ * Retrieves the current transformation matrix of the window, relative to the display.
+ *
+ * @param float9 A temporary array of 9 floats.
+ * @param outMatrix Matrix to fill in the transformation.
+ */
+ void getTransformationMatrix(float[] float9, Matrix outMatrix) {
+ float9[Matrix.MSCALE_X] = mWinAnimator.mDsDx;
+ float9[Matrix.MSKEW_Y] = mWinAnimator.mDtDx;
+ float9[Matrix.MSKEW_X] = mWinAnimator.mDtDy;
+ float9[Matrix.MSCALE_Y] = mWinAnimator.mDsDy;
+ float9[Matrix.MTRANS_X] = mSurfacePosition.x + mShownPosition.x;
+ float9[Matrix.MTRANS_Y] = mSurfacePosition.y + mShownPosition.y;
+ float9[Matrix.MPERSP_0] = 0;
+ float9[Matrix.MPERSP_1] = 0;
+ float9[Matrix.MPERSP_2] = 1;
+ outMatrix.setValues(float9);
+ }
+
// TODO: Hack to work around the number of states AppWindowToken needs to access without having
// access to its windows children. Need to investigate re-writing
// {@link AppWindowToken#updateReportedVisibilityLocked} so this can be removed.
@@ -4381,11 +4418,6 @@
return getAnimLayerAdjustment() > 0 || mWillReplaceWindow;
}
- @Override
- SurfaceControl.Builder makeSurface() {
- return getParent().makeChildSurface(this);
- }
-
private void applyDims(Dimmer dimmer) {
if (!mAnimatingExit && mAppDied) {
mIsDimming = true;
@@ -4405,11 +4437,51 @@
applyDims(dimmer);
}
+ updateSurfacePosition(mPendingTransaction);
+
mWinAnimator.prepareSurfaceLocked(true);
super.prepareSurfaces();
}
@Override
+ public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
+ super.onAnimationLeashCreated(t, leash);
+
+ // Leash is now responsible for position, so set our position to 0.
+ t.setPosition(mSurfaceControl, 0, 0);
+ }
+
+ @Override
+ public void onAnimationLeashDestroyed(Transaction t) {
+ super.onAnimationLeashDestroyed(t);
+ updateSurfacePosition(t);
+ }
+
+ void updateSurfacePosition(Transaction t) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+
+ transformFrameToSurfacePosition(mFrame.left, mFrame.top, mSurfacePosition);
+ if (!mSurfaceAnimator.hasLeash()) {
+ t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
+ }
+ }
+
+ private void transformFrameToSurfacePosition(int left, int top, Point outPoint) {
+ outPoint.set(left, top);
+ if (isChildWindow()) {
+ // TODO: This probably falls apart at some point and we should
+ // actually compute relative coordinates.
+ final WindowState parent = getParentWindow();
+ outPoint.offset(-parent.mFrame.left, -parent.mFrame.top);
+ }
+
+ // Expand for surface insets. See WindowState.expandForSurfaceInsets.
+ outPoint.offset(-mAttrs.surfaceInsets.left, -mAttrs.surfaceInsets.top);
+ }
+
+ @Override
void assignLayer(Transaction t, int layer) {
// See comment in assignRelativeLayerForImeTargetChild
if (!isChildWindow()
@@ -4449,4 +4521,34 @@
layer++;
}
}
+
+ private final class MoveAnimationSpec implements AnimationSpec {
+
+ private final long mDuration;
+ private Interpolator mInterpolator;
+ private Point mFrom = new Point();
+ private Point mTo = new Point();
+
+ private MoveAnimationSpec(int fromX, int fromY, int toX, int toY) {
+ final Animation anim = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.window_move_from_decor);
+ mDuration = anim.computeDurationHint();
+ mInterpolator = anim.getInterpolator();
+ mFrom.set(fromX, fromY);
+ mTo.set(toX, toY);
+ }
+
+ @Override
+ public long getDuration() {
+ return mDuration;
+ }
+
+ @Override
+ public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
+ final float fraction = (float) currentPlayTime / getDuration();
+ final float v = mInterpolator.getInterpolation(fraction);
+ t.setPosition(leash, mFrom.x + (mTo.x - mFrom.x) * v,
+ mFrom.y + (mTo.y - mFrom.y) * v);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 54c84ea..0eabc89 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -23,16 +23,16 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.AppWindowAnimator.sDummyAnimation;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_CROP;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
@@ -40,7 +40,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
-import static com.android.server.wm.WindowManagerService.localLOGV;
import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
@@ -65,14 +64,13 @@
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
import com.android.server.policy.WindowManagerPolicy;
-import java.io.PrintWriter;
import java.io.FileDescriptor;
+import java.io.PrintWriter;
/**
* Keep track of animations and surface operations for a single WindowState.
@@ -112,20 +110,9 @@
final boolean mIsWallpaper;
private final WallpaperController mWallpaperControllerLocked;
- // Currently running animation.
- boolean mAnimating;
- boolean mLocalAnimating;
- Animation mAnimation;
boolean mAnimationIsEntrance;
- boolean mHasTransformation;
- boolean mHasLocalTransformation;
- final Transformation mTransformation = new Transformation();
- boolean mWasAnimating; // Were we animating going into the most recent animation step?
int mAnimLayer;
int mLastLayer;
- long mAnimationStartTime;
- long mLastAnimationTime;
- int mStackClip = STACK_CLIP_BEFORE_ANIM;
/**
* Set when we have changed the size of the surface, to know that
@@ -168,13 +155,6 @@
private final Rect mSystemDecorRect = new Rect();
private final Rect mLastSystemDecorRect = new Rect();
- // Used to save animation distances between the time they are calculated and when they are used.
- private int mAnimDx;
- private int mAnimDy;
-
- /** Is the next animation to be started a window move animation? */
- private boolean mAnimateMove = false;
-
float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
private float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
@@ -226,8 +206,6 @@
int mAttrType;
- static final long PENDING_TRANSACTION_FINISH_WAIT_TIME = 100;
-
boolean mForceScaleUntilResize;
// WindowState.mHScale and WindowState.mVScale contain the
@@ -247,15 +225,6 @@
mAnimator = service.mAnimator;
mPolicy = service.mPolicy;
mContext = service.mContext;
- final DisplayContent displayContent = win.getDisplayContent();
- if (displayContent != null) {
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- mAnimDx = displayInfo.appWidth;
- mAnimDy = displayInfo.appHeight;
- } else {
- Slog.w(TAG, "WindowStateAnimator ctor: Display has been removed");
- // This is checked on return and dealt with.
- }
mWin = win;
mParentWinAnimator = !win.isChildWindow() ? null : win.getParentWindow().mWinAnimator;
@@ -266,54 +235,11 @@
mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
}
- public void setAnimation(Animation anim, long startTime, int stackClip) {
- if (localLOGV) Slog.v(TAG, "Setting animation in " + this + ": " + anim);
- mAnimating = false;
- mLocalAnimating = false;
- mAnimation = anim;
- mAnimation.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
- mAnimation.scaleCurrentDuration(mService.getWindowAnimationScaleLocked());
- // Start out animation gone if window is gone, or visible if window is visible.
- mTransformation.clear();
- mTransformation.setAlpha(mLastHidden ? 0 : 1);
- mHasLocalTransformation = true;
- mAnimationStartTime = startTime;
- mStackClip = stackClip;
- }
-
- public void setAnimation(Animation anim, int stackClip) {
- setAnimation(anim, -1, stackClip);
- }
-
- public void setAnimation(Animation anim) {
- setAnimation(anim, -1, STACK_CLIP_AFTER_ANIM);
- }
-
- public void clearAnimation() {
- if (mAnimation != null) {
- mAnimating = true;
- mLocalAnimating = false;
- mAnimation.cancel();
- mAnimation = null;
- mStackClip = STACK_CLIP_BEFORE_ANIM;
- }
- }
-
/**
* Is the window or its container currently set to animate or currently animating?
*/
boolean isAnimationSet() {
- return mAnimation != null
- || (mParentWinAnimator != null && mParentWinAnimator.mAnimation != null)
- || (mAppAnimator != null && mAppAnimator.isAnimating());
- }
-
- /**
- * @return whether an animation is about to start, i.e. the animation is set already but we
- * haven't processed the first frame yet.
- */
- boolean isAnimationStarting() {
- return isAnimationSet() && !mAnimating;
+ return mWin.isAnimating();
}
/** Is the window animating the DummyAnimation? */
@@ -323,13 +249,6 @@
}
/**
- * Is this window currently set to animate or currently animating?
- */
- boolean isWindowAnimationSet() {
- return mAnimation != null;
- }
-
- /**
* Is this window currently waiting to run an opening animation?
*/
boolean isWaitingForOpening() {
@@ -341,130 +260,23 @@
if (DEBUG_ANIM) Slog.d(TAG,
"cancelExitAnimationForNextAnimationLocked: " + mWin);
- if (mAnimation != null) {
- mAnimation.cancel();
- mAnimation = null;
- mLocalAnimating = false;
- mWin.destroySurfaceUnchecked();
- }
+ mWin.cancelAnimation();
+ mWin.destroySurfaceUnchecked();
}
- private boolean stepAnimation(long currentTime) {
- if ((mAnimation == null) || !mLocalAnimating) {
- return false;
- }
- currentTime = getAnimationFrameTime(mAnimation, currentTime);
- mTransformation.clear();
- final boolean more = mAnimation.getTransformation(currentTime, mTransformation);
- if (mAnimationStartDelayed && mAnimationIsEntrance) {
- mTransformation.setAlpha(0f);
- }
- if (false && DEBUG_ANIM) Slog.v(TAG, "Stepped animation in " + this + ": more=" + more
- + ", xform=" + mTransformation);
- return more;
- }
-
- // This must be called while inside a transaction. Returns true if
- // there is more animation to run.
- boolean stepAnimationLocked(long currentTime) {
- // Save the animation state as it was before this step so WindowManagerService can tell if
- // we just started or just stopped animating by comparing mWasAnimating with isAnimationSet().
- mWasAnimating = mAnimating;
- final DisplayContent displayContent = mWin.getDisplayContent();
- if (mWin.mToken.okToAnimate()) {
- // We will run animations as long as the display isn't frozen.
-
- if (mWin.isDrawnLw() && mAnimation != null) {
- mHasTransformation = true;
- mHasLocalTransformation = true;
- if (!mLocalAnimating) {
- if (DEBUG_ANIM) Slog.v(
- TAG, "Starting animation in " + this +
- " @ " + currentTime + ": ww=" + mWin.mFrame.width() +
- " wh=" + mWin.mFrame.height() +
- " dx=" + mAnimDx + " dy=" + mAnimDy +
- " scale=" + mService.getWindowAnimationScaleLocked());
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- if (mAnimateMove) {
- mAnimateMove = false;
- mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
- mAnimDx, mAnimDy);
- } else {
- mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
- displayInfo.appWidth, displayInfo.appHeight);
- }
- mAnimDx = displayInfo.appWidth;
- mAnimDy = displayInfo.appHeight;
- mAnimation.setStartTime(mAnimationStartTime != -1
- ? mAnimationStartTime
- : currentTime);
- mLocalAnimating = true;
- mAnimating = true;
- }
- if ((mAnimation != null) && mLocalAnimating) {
- mLastAnimationTime = currentTime;
- if (stepAnimation(currentTime)) {
- return true;
- }
- }
- if (DEBUG_ANIM) Slog.v(
- TAG, "Finished animation in " + this +
- " @ " + currentTime);
- //WindowManagerService.this.dump();
- }
- mHasLocalTransformation = false;
- if ((!mLocalAnimating || mAnimationIsEntrance) && mAppAnimator != null
- && mAppAnimator.animation != null) {
- // When our app token is animating, we kind-of pretend like
- // we are as well. Note the mLocalAnimating mAnimationIsEntrance
- // part of this check means that we will only do this if
- // our window is not currently exiting, or it is not
- // locally animating itself. The idea being that one that
- // is exiting and doing a local animation should be removed
- // once that animation is done.
- mAnimating = true;
- mHasTransformation = true;
- mTransformation.clear();
- return false;
- } else if (mHasTransformation) {
- // Little trick to get through the path below to act like
- // we have finished an animation.
- mAnimating = true;
- } else if (isAnimationSet()) {
- mAnimating = true;
- }
- } else if (mAnimation != null) {
- // If the display is frozen, and there is a pending animation,
- // clear it and make sure we run the cleanup code.
- mAnimating = true;
- }
-
- if (!mAnimating && !mLocalAnimating) {
- return false;
- }
-
+ void onAnimationFinished() {
// Done animating, clean up.
if (DEBUG_ANIM) Slog.v(
- TAG, "Animation done in " + this + ": exiting=" + mWin.mAnimatingExit
- + ", reportedVisible="
- + (mWin.mAppToken != null ? mWin.mAppToken.reportedVisible : false));
+ TAG, "Animation done in " + this + ": exiting=" + mWin.mAnimatingExit
+ + ", reportedVisible="
+ + (mWin.mAppToken != null ? mWin.mAppToken.reportedVisible : false));
- mAnimating = false;
- mLocalAnimating = false;
- if (mAnimation != null) {
- mAnimation.cancel();
- mAnimation = null;
- }
if (mAnimator.mWindowDetachedWallpaper == mWin) {
mAnimator.mWindowDetachedWallpaper = null;
}
- mAnimLayer = mWin.getSpecialWindowAnimLayerAdjustment();
- if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this + " anim layer: " + mAnimLayer);
- mHasTransformation = false;
- mHasLocalTransformation = false;
- mStackClip = STACK_CLIP_BEFORE_ANIM;
+
mWin.checkPolicyVisibilityChange();
- mTransformation.clear();
+ final DisplayContent displayContent = mWin.getDisplayContent();
if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) {
// Upon completion of a not-visible to visible status bar animation a relayout is
// required.
@@ -475,7 +287,11 @@
mWin.onExitAnimationDone();
final int displayId = mWin.getDisplayId();
- mAnimator.setPendingLayoutChanges(displayId, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
+ int pendingLayoutChanges = FINISH_LAYOUT_REDO_ANIM;
+ if (displayContent.mWallpaperController.isWallpaperTarget(mWin)) {
+ pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ }
+ mAnimator.setPendingLayoutChanges(displayId, pendingLayoutChanges);
if (DEBUG_LAYOUT_REPEATS)
mService.mWindowPlacerLocked.debugLayoutRepeats(
"WindowStateAnimator", mAnimator.getPendingLayoutChanges(displayId));
@@ -483,8 +299,6 @@
if (mWin.mAppToken != null) {
mWin.mAppToken.updateReportedVisibilityLocked();
}
-
- return false;
}
void hide(String reason) {
@@ -683,8 +497,8 @@
}
mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
- attrs.getTitle().toString(),
- width, height, format, flags, this, windowType, ownerUid);
+ attrs.getTitle().toString(), width, height, format, flags, this,
+ windowType, ownerUid);
mSurfaceFormat = format;
w.setHasSurface(true);
@@ -853,35 +667,8 @@
}
void computeShownFrameLocked() {
- final boolean selfTransformation = mHasLocalTransformation;
Transformation appTransformation = (mAppAnimator != null && mAppAnimator.hasTransformation)
? mAppAnimator.transformation : null;
- Transformation wallpaperTargetTransformation = null;
-
- // Wallpapers are animated based on the "real" window they
- // are currently targeting.
- final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
- if (mIsWallpaper && wallpaperTarget != null && mService.mAnimateWallpaperWithTarget) {
- final WindowStateAnimator wallpaperAnimator = wallpaperTarget.mWinAnimator;
- if (wallpaperAnimator.mHasLocalTransformation &&
- wallpaperAnimator.mAnimation != null &&
- !wallpaperAnimator.mAnimation.getDetachWallpaper()) {
- wallpaperTargetTransformation = wallpaperAnimator.mTransformation;
- if (DEBUG_WALLPAPER && wallpaperTargetTransformation != null) {
- Slog.v(TAG, "WP target attached xform: " + wallpaperTargetTransformation);
- }
- }
- final AppWindowAnimator wpAppAnimator = wallpaperTarget.mAppToken == null ?
- null : wallpaperTarget.mAppToken.mAppAnimator;
- if (wpAppAnimator != null && wpAppAnimator.hasTransformation
- && wpAppAnimator.animation != null
- && !wpAppAnimator.animation.getDetachWallpaper()) {
- appTransformation = wpAppAnimator.transformation;
- if (DEBUG_WALLPAPER && appTransformation != null) {
- Slog.v(TAG, "WP target app xform: " + appTransformation);
- }
- }
- }
final int displayId = mWin.getDisplayId();
final ScreenRotationAnimation screenRotationAnimation =
@@ -890,8 +677,7 @@
screenRotationAnimation != null && screenRotationAnimation.isAnimating();
mHasClipRect = false;
- if (selfTransformation || wallpaperTargetTransformation != null
- || appTransformation != null || screenAnimation) {
+ if (appTransformation != null || screenAnimation) {
// cache often used attributes locally
final Rect frame = mWin.mFrame;
final float tmpFloats[] = mService.mTmpFloats;
@@ -917,34 +703,16 @@
tmpMatrix.reset();
}
tmpMatrix.postScale(mWin.mGlobalScale, mWin.mGlobalScale);
- if (selfTransformation) {
- tmpMatrix.postConcat(mTransformation.getMatrix());
- }
- if (wallpaperTargetTransformation != null) {
- tmpMatrix.postConcat(wallpaperTargetTransformation.getMatrix());
- }
+ // WindowState.prepareSurfaces expands for surface insets (in order they don't get
+ // clipped by the WindowState surface), so we need to go into the other direction here.
+ tmpMatrix.postTranslate(mWin.mXOffset + mWin.mAttrs.surfaceInsets.left,
+ mWin.mYOffset + mWin.mAttrs.surfaceInsets.top);
+
if (appTransformation != null) {
tmpMatrix.postConcat(appTransformation.getMatrix());
}
- int left = frame.left;
- int top = frame.top;
- if (mWin.isChildWindow()) {
- WindowState parent = mWin.getParentWindow();
- left -= parent.mFrame.left;
- top -= parent.mFrame.top;
- }
-
- // The translation that applies the position of the window needs to be applied at the
- // end in case that other translations include scaling. Otherwise the scaling will
- // affect this translation. But it needs to be set before the screen rotation animation
- // so the pivot point is at the center of the screen for all windows.
- tmpMatrix.postTranslate(left + mWin.mXOffset, top + mWin.mYOffset);
- if (screenAnimation) {
- tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
- }
-
// "convert" it into SurfaceFlinger's format
// (a 2x2 matrix + an offset)
// Here we must not transform the position of the surface
@@ -972,12 +740,6 @@
|| (mWin.isIdentityMatrix(mDsDx, mDtDx, mDtDy, mDsDy)
&& x == frame.left && y == frame.top))) {
//Slog.i(TAG_WM, "Applying alpha transform");
- if (selfTransformation) {
- mShownAlpha *= mTransformation.getAlpha();
- }
- if (wallpaperTargetTransformation != null) {
- mShownAlpha *= wallpaperTargetTransformation.getAlpha();
- }
if (appTransformation != null) {
mShownAlpha *= appTransformation.getAlpha();
if (appTransformation.hasClipRect()) {
@@ -1006,9 +768,6 @@
if ((DEBUG_ANIM || WindowManagerService.localLOGV)
&& (mShownAlpha == 1.0 || mShownAlpha == 0.0)) Slog.v(
TAG, "computeShownFrameLocked: Animating " + this + " mAlpha=" + mAlpha
- + " self=" + (selfTransformation ? mTransformation.getAlpha() : "null")
- + " attached=" + (wallpaperTargetTransformation == null ?
- "null" : wallpaperTargetTransformation.getAlpha())
+ " app=" + (appTransformation == null ? "null" : appTransformation.getAlpha())
+ " screen=" + (screenAnimation ?
screenRotationAnimation.getEnterTransformation().getAlpha() : "null"));
@@ -1028,10 +787,10 @@
TAG, "computeShownFrameLocked: " + this +
" not attached, mAlpha=" + mAlpha);
- mWin.mShownPosition.set(mWin.mFrame.left, mWin.mFrame.top);
- if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
- mWin.mShownPosition.offset(mWin.mXOffset, mWin.mYOffset);
- }
+ // WindowState.prepareSurfaces expands for surface insets (in order they don't get
+ // clipped by the WindowState surface), so we need to go into the other direction here.
+ mWin.mShownPosition.set(mWin.mXOffset + mWin.mAttrs.surfaceInsets.left,
+ mWin.mYOffset + mWin.mAttrs.surfaceInsets.top);
mShownAlpha = mAlpha;
mHaveMatrix = false;
mDsDx = mWin.mGlobalScale;
@@ -1182,7 +941,7 @@
if (mAppAnimator != null && mAppAnimator.animation != null) {
return mAppAnimator.getStackClip();
} else {
- return mStackClip;
+ return STACK_CLIP_AFTER_ANIM;
}
}
@@ -1434,7 +1193,7 @@
if (mSurfaceResized) {
mReportSurfaceResized = true;
mAnimator.setPendingLayoutChanges(w.getDisplayId(),
- WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
+ FINISH_LAYOUT_REDO_WALLPAPER);
}
}
@@ -1546,7 +1305,7 @@
// Run another pass through performLayout to set mHasContent in the
// LogicalDisplay.
mAnimator.setPendingLayoutChanges(w.getDisplayId(),
- WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
+ FINISH_LAYOUT_REDO_ANIM);
} else {
w.setOrientationChanging(false);
}
@@ -1720,12 +1479,18 @@
* @return true if an animation has been loaded.
*/
boolean applyAnimationLocked(int transit, boolean isEntrance) {
- if (mLocalAnimating && mAnimationIsEntrance == isEntrance) {
+ if (mWin.isSelfAnimating() && mAnimationIsEntrance == isEntrance) {
// If we are trying to apply an animation, but already running
// an animation of the same type, then just leave that one alone.
return true;
}
+ if (isEntrance && mWin.mAttrs.type == TYPE_INPUT_METHOD) {
+ mWin.getDisplayContent().adjustForImeIfNeeded();
+ mWin.setDisplayLayoutNeeded();
+ mService.mWindowPlacerLocked.requestTraversal();
+ }
+
// Only apply an animation if the display isn't frozen. If it is
// frozen, there is no reason to animate and it can cause strange
// artifacts when we unfreeze the display if some different animation
@@ -1764,44 +1529,19 @@
+ " isEntrance=" + isEntrance + " Callers " + Debug.getCallers(3));
if (a != null) {
if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
- setAnimation(a);
+ mWin.startAnimation(a);
mAnimationIsEntrance = isEntrance;
}
} else {
- clearAnimation();
+ mWin.cancelAnimation();
}
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
- if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {
+ if (!isEntrance && mWin.mAttrs.type == TYPE_INPUT_METHOD) {
mWin.getDisplayContent().adjustForImeIfNeeded();
- if (isEntrance) {
- mWin.setDisplayLayoutNeeded();
- mService.mWindowPlacerLocked.requestTraversal();
- }
}
- return mAnimation != null;
- }
- private void applyFadeoutDuringKeyguardExitAnimation() {
- long startTime = mAnimation.getStartTime();
- long duration = mAnimation.getDuration();
- long elapsed = mLastAnimationTime - startTime;
- long fadeDuration = duration - elapsed;
- if (fadeDuration <= 0) {
- // Never mind, this would be no visible animation, so abort the animation change.
- return;
- }
- AnimationSet newAnimation = new AnimationSet(false /* shareInterpolator */);
- newAnimation.setDuration(duration);
- newAnimation.setStartTime(startTime);
- newAnimation.addAnimation(mAnimation);
- Animation fadeOut = AnimationUtils.loadAnimation(
- mContext, com.android.internal.R.anim.app_starting_exit);
- fadeOut.setDuration(fadeDuration);
- fadeOut.setStartOffset(elapsed);
- newAnimation.addAnimation(fadeOut);
- newAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(), mAnimDx, mAnimDy);
- mAnimation = newAnimation;
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ return isAnimationSet();
}
void writeToProto(ProtoOutputStream proto, long fieldId) {
@@ -1814,20 +1554,8 @@
}
public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
- if (mAnimating || mLocalAnimating || mAnimationIsEntrance
- || mAnimation != null) {
- pw.print(prefix); pw.print("mAnimating="); pw.print(mAnimating);
- pw.print(" mLocalAnimating="); pw.print(mLocalAnimating);
- pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
- pw.print(" mAnimation="); pw.print(mAnimation);
- pw.print(" mStackClip="); pw.println(mStackClip);
- }
- if (mHasTransformation || mHasLocalTransformation) {
- pw.print(prefix); pw.print("XForm: has=");
- pw.print(mHasTransformation);
- pw.print(" hasLocal="); pw.print(mHasLocalTransformation);
- pw.print(" "); mTransformation.printShortString(pw);
- pw.println();
+ if (mAnimationIsEntrance) {
+ pw.print(prefix); pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
}
if (mSurfaceController != null) {
mSurfaceController.dump(pw, prefix, dumpAll);
@@ -1907,44 +1635,6 @@
}
}
- void setMoveAnimation(int left, int top) {
- final Animation a = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.window_move_from_decor);
- setAnimation(a);
- mAnimDx = mWin.mLastFrame.left - left;
- mAnimDy = mWin.mLastFrame.top - top;
- mAnimateMove = true;
- }
-
- void deferTransactionUntilParentFrame(long frameNumber) {
- if (!mWin.isChildWindow()) {
- return;
- }
- mSurfaceController.deferTransactionUntil(
- mWin.getParentWindow().mWinAnimator.mSurfaceController.getHandle(), frameNumber);
- }
-
- /**
- * Sometimes we need to synchronize the first frame of animation with some external event.
- * To achieve this, we prolong the start of the animation and keep producing the first frame of
- * the animation.
- */
- private long getAnimationFrameTime(Animation animation, long currentTime) {
- if (mAnimationStartDelayed) {
- animation.setStartTime(currentTime);
- return currentTime + 1;
- }
- return currentTime;
- }
-
- void startDelayingAnimationStart() {
- mAnimationStartDelayed = true;
- }
-
- void endDelayingAnimationStart() {
- mAnimationStartDelayed = false;
- }
-
void seamlesslyRotateWindow(int oldRotation, int newRotation) {
final WindowState w = mWin;
if (!w.isVisibleNow() || w.mIsWallpaper) {
@@ -2026,4 +1716,8 @@
mSurfaceController.detachChildren();
}
}
+
+ int getLayer() {
+ return mLastLayer;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 6746754..e26a362 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -59,8 +59,8 @@
private boolean mSurfaceShown = false;
private float mSurfaceX = 0;
private float mSurfaceY = 0;
- private float mSurfaceW = 0;
- private float mSurfaceH = 0;
+ private int mSurfaceW = 0;
+ private int mSurfaceH = 0;
// Initialize to the identity matrix.
private float mLastDsdx = 1;
@@ -517,11 +517,11 @@
return mSurfaceY;
}
- float getWidth() {
+ int getWidth() {
return mSurfaceW;
}
- float getHeight() {
+ int getHeight() {
return mSurfaceH;
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 169d0a3..d8e7457 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -462,6 +462,7 @@
appAnimator.setNullAnimation();
// TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
// animating?
+ wtoken.setAllAppWinAnimators();
wtoken.setVisibility(animLp, false, transit, false, voiceInteraction);
wtoken.updateReportedVisibilityLocked();
// setAllAppWinAnimators so the windows get onExitAnimationDone once the animation is
@@ -606,7 +607,7 @@
+ ", oldWallpaper=" + oldWallpaper
+ ", openingApps=" + openingApps
+ ", closingApps=" + closingApps);
- mService.mAnimateWallpaperWithTarget = false;
+
if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) {
transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
@@ -645,8 +646,6 @@
transit = TRANSIT_WALLPAPER_OPEN;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: "
+ AppTransition.appTransitionToString(transit));
- } else {
- mService.mAnimateWallpaperWithTarget = true;
}
}
return transit;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 5d76304..e53aa81 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -115,7 +115,7 @@
],
static_libs: [
- "android.hardware.broadcastradio@common-utils-lib",
+ "android.hardware.broadcastradio@common-utils-1x-lib",
"libscrypt_static",
],
}
diff --git a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
index e1dbdeb..9892146 100644
--- a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
+++ b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
@@ -25,7 +25,7 @@
#include <android/hardware/broadcastradio/1.1/IBroadcastRadio.h>
#include <android/hardware/broadcastradio/1.2/IBroadcastRadioFactory.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
-#include <broadcastradio-utils/Utils.h>
+#include <broadcastradio-utils-1x/Utils.h>
#include <core_jni_helpers.h>
#include <hidl/ServiceManagement.h>
#include <nativehelper/JNIHelp.h>
@@ -257,7 +257,7 @@
{ "nativeFinalize", "(J)V", (void*)nativeFinalize },
{ "nativeLoadModules", "(J)Ljava/util/List;", (void*)nativeLoadModules },
{ "nativeOpenTuner", "(JILandroid/hardware/radio/RadioManager$BandConfig;Z"
- "Landroid/hardware/radio/ITunerCallback;)Lcom/android/server/broadcastradio/Tuner;",
+ "Landroid/hardware/radio/ITunerCallback;)Lcom/android/server/broadcastradio/hal1/Tuner;",
(void*)nativeOpenTuner },
};
@@ -270,7 +270,7 @@
register_android_server_broadcastradio_convert(env);
- auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/Tuner");
+ auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/Tuner");
gjni.Tuner.clazz = MakeGlobalRefOrDie(env, tunerClass);
gjni.Tuner.cstor = GetMethodIDOrDie(env, tunerClass, "<init>",
"(Landroid/hardware/radio/ITunerCallback;IIZI)V");
@@ -281,7 +281,7 @@
gjni.ArrayList.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
auto res = jniRegisterNativeMethods(env,
- "com/android/server/broadcastradio/BroadcastRadioService",
+ "com/android/server/broadcastradio/hal1/BroadcastRadioService",
gRadioServiceMethods, NELEM(gRadioServiceMethods));
LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
}
diff --git a/services/core/jni/BroadcastRadio/Tuner.cpp b/services/core/jni/BroadcastRadio/Tuner.cpp
index df53fee..42eb873 100644
--- a/services/core/jni/BroadcastRadio/Tuner.cpp
+++ b/services/core/jni/BroadcastRadio/Tuner.cpp
@@ -24,7 +24,7 @@
#include <android/hardware/broadcastradio/1.2/IBroadcastRadioFactory.h>
#include <binder/IPCThreadState.h>
-#include <broadcastradio-utils/Utils.h>
+#include <broadcastradio-utils-1x/Utils.h>
#include <core_jni_helpers.h>
#include <media/AudioSystem.h>
#include <nativehelper/JNIHelp.h>
@@ -592,18 +592,18 @@
register_android_server_broadcastradio_TunerCallback(vm, env);
- auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/Tuner");
+ auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/Tuner");
gjni.Tuner.nativeContext = GetFieldIDOrDie(env, tunerClass, "mNativeContext", "J");
gjni.Tuner.region = GetFieldIDOrDie(env, tunerClass, "mRegion", "I");
gjni.Tuner.tunerCallback = GetFieldIDOrDie(env, tunerClass, "mTunerCallback",
- "Lcom/android/server/broadcastradio/TunerCallback;");
+ "Lcom/android/server/broadcastradio/hal1/TunerCallback;");
auto arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
gjni.ArrayList.clazz = MakeGlobalRefOrDie(env, arrayListClass);
gjni.ArrayList.cstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V");
gjni.ArrayList.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
- auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/Tuner",
+ auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/hal1/Tuner",
gTunerMethods, NELEM(gTunerMethods));
LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
}
diff --git a/services/core/jni/BroadcastRadio/TunerCallback.cpp b/services/core/jni/BroadcastRadio/TunerCallback.cpp
index d624df6..39f2c05 100644
--- a/services/core/jni/BroadcastRadio/TunerCallback.cpp
+++ b/services/core/jni/BroadcastRadio/TunerCallback.cpp
@@ -22,7 +22,7 @@
#include "Tuner.h"
#include "convert.h"
-#include <broadcastradio-utils/Utils.h>
+#include <broadcastradio-utils-1x/Utils.h>
#include <core_jni_helpers.h>
#include <nativehelper/JNIHelp.h>
#include <utils/Log.h>
@@ -406,7 +406,7 @@
}
static const JNINativeMethod gTunerCallbackMethods[] = {
- { "nativeInit", "(Lcom/android/server/broadcastradio/Tuner;I)J", (void*)nativeInit },
+ { "nativeInit", "(Lcom/android/server/broadcastradio/hal1/Tuner;I)J", (void*)nativeInit },
{ "nativeFinalize", "(J)V", (void*)nativeFinalize },
{ "nativeDetach", "(J)V", (void*)nativeDetach },
};
@@ -420,7 +420,7 @@
gvm = vm;
- auto tunerCbClass = FindClassOrDie(env, "com/android/server/broadcastradio/TunerCallback");
+ auto tunerCbClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/TunerCallback");
gjni.TunerCallback.clazz = MakeGlobalRefOrDie(env, tunerCbClass);
gjni.TunerCallback.nativeContext = GetFieldIDOrDie(env, tunerCbClass, "mNativeContext", "J");
gjni.TunerCallback.handleHwFailure = GetMethodIDOrDie(env, tunerCbClass, "handleHwFailure", "()V");
@@ -444,7 +444,7 @@
gjni.TunerCallback.onParametersUpdated = GetMethodIDOrDie(env, tunerCbClass,
"onParametersUpdated", "(Ljava/util/Map;)V");
- auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/TunerCallback",
+ auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/hal1/TunerCallback",
gTunerCallbackMethods, NELEM(gTunerCallbackMethods));
LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
}
diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp
index 734ce79..be1ad72 100644
--- a/services/core/jni/BroadcastRadio/convert.cpp
+++ b/services/core/jni/BroadcastRadio/convert.cpp
@@ -21,7 +21,7 @@
#include "regions.h"
-#include <broadcastradio-utils/Utils.h>
+#include <broadcastradio-utils-1x/Utils.h>
#include <core_jni_helpers.h>
#include <nativehelper/JNIHelp.h>
#include <utils/Log.h>
@@ -678,7 +678,7 @@
gjni.AmBandDescriptor.cstor = GetMethodIDOrDie(env, amBandDescriptorClass,
"<init>", "(IIIIIZ)V");
- auto convertClass = FindClassOrDie(env, "com/android/server/broadcastradio/Convert");
+ auto convertClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/Convert");
gjni.Convert.clazz = MakeGlobalRefOrDie(env, convertClass);
gjni.Convert.stringMapToNative = GetStaticMethodIDOrDie(env, convertClass, "stringMapToNative",
"(Ljava/util/Map;)[[Ljava/lang/String;");
diff --git a/services/core/jni/BroadcastRadio/regions.cpp b/services/core/jni/BroadcastRadio/regions.cpp
index b7fd0f3..1757a4d 100644
--- a/services/core/jni/BroadcastRadio/regions.cpp
+++ b/services/core/jni/BroadcastRadio/regions.cpp
@@ -19,7 +19,7 @@
#include "regions.h"
-#include <broadcastradio-utils/Utils.h>
+#include <broadcastradio-utils-1x/Utils.h>
#include <utils/Log.h>
namespace android {
diff --git a/services/core/jni/com_android_server_GraphicsStatsService.cpp b/services/core/jni/com_android_server_GraphicsStatsService.cpp
index 8385020..d1d253b 100644
--- a/services/core/jni/com_android_server_GraphicsStatsService.cpp
+++ b/services/core/jni/com_android_server_GraphicsStatsService.cpp
@@ -39,7 +39,7 @@
}
static void addToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath, jstring jpackage,
- jint versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
+ jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
std::string path;
const ProfileData* data = nullptr;
LOG_ALWAYS_FATAL_IF(jdata == nullptr && jpath == nullptr, "Path and data can't both be null");
@@ -78,7 +78,7 @@
}
static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jstring jpackage,
- jint versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
+ jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
ScopedByteArrayRO buffer(env, jdata);
LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
"Buffer size %zu doesn't match expected %zu!", buffer.size(), sizeof(ProfileData));
@@ -96,10 +96,10 @@
static const JNINativeMethod sMethods[] = {
{ "nGetAshmemSize", "()I", (void*) getAshmemSize },
{ "nCreateDump", "(IZ)J", (void*) createDump },
- { "nAddToDump", "(JLjava/lang/String;Ljava/lang/String;IJJ[B)V", (void*) addToDump },
+ { "nAddToDump", "(JLjava/lang/String;Ljava/lang/String;JJJ[B)V", (void*) addToDump },
{ "nAddToDump", "(JLjava/lang/String;)V", (void*) addFileToDump },
{ "nFinishDump", "(J)V", (void*) finishDump },
- { "nSaveBuffer", "(Ljava/lang/String;Ljava/lang/String;IJJ[B)V", (void*) saveBuffer },
+ { "nSaveBuffer", "(Ljava/lang/String;Ljava/lang/String;JJJ[B)V", (void*) saveBuffer },
};
int register_android_server_GraphicsStatsService(JNIEnv* env)
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 39cc953..02ad6c7 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -101,7 +101,7 @@
return -1;
}
ALOGV("Registering callback...");
- set_wakeup_callback(&wakeup_callback);
+ autosuspend_set_wakeup_callback(&wakeup_callback);
}
// Wait for wakeup.
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 4cbe64d..246bd42 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -18,8 +18,11 @@
#define LOG_NDEBUG 0
+#include <android/hardware/gnss/1.0/IGnss.h>
#include <android/hardware/gnss/1.1/IGnss.h>
+#include <android/hardware/gnss/1.0/IGnssMeasurement.h>
+#include <android/hardware/gnss/1.1/IGnssMeasurement.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
#include "hardware_legacy/power.h"
@@ -77,23 +80,23 @@
using android::hardware::hidl_vec;
using android::hardware::hidl_death_recipient;
using android::hidl::base::V1_0::IBase;
-
using android::hardware::gnss::V1_0::GnssLocation;
using android::hardware::gnss::V1_0::GnssLocationFlags;
-
+using IGnss_V1_1 = android::hardware::gnss::V1_1::IGnss;
+using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement;
+using IGnssMeasurement_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurement;
+using android::hardware::gnss::V1_0::IGnss;
using android::hardware::gnss::V1_0::IAGnss;
using android::hardware::gnss::V1_0::IAGnssCallback;
using android::hardware::gnss::V1_0::IAGnssCallback;
using android::hardware::gnss::V1_0::IAGnssRil;
using android::hardware::gnss::V1_0::IAGnssRilCallback;
-using android::hardware::gnss::V1_0::IGnss;
using android::hardware::gnss::V1_0::IGnssBatching;
using android::hardware::gnss::V1_0::IGnssBatchingCallback;
using android::hardware::gnss::V1_0::IGnssConfiguration;
using android::hardware::gnss::V1_0::IGnssDebug;
using android::hardware::gnss::V1_0::IGnssGeofenceCallback;
using android::hardware::gnss::V1_0::IGnssGeofencing;
-using android::hardware::gnss::V1_0::IGnssMeasurement;
using android::hardware::gnss::V1_0::IGnssMeasurementCallback;
using android::hardware::gnss::V1_0::IGnssNavigationMessage;
using android::hardware::gnss::V1_0::IGnssNavigationMessageCallback;
@@ -101,8 +104,6 @@
using android::hardware::gnss::V1_0::IGnssNiCallback;
using android::hardware::gnss::V1_0::IGnssXtra;
using android::hardware::gnss::V1_0::IGnssXtraCallback;
-
-using IGnssV1_1 = android::hardware::gnss::V1_1::IGnss;
using android::hardware::gnss::V1_1::IGnssCallback;
struct GnssDeathRecipient : virtual public hidl_death_recipient
@@ -118,7 +119,7 @@
sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
sp<IGnss> gnssHal = nullptr;
-sp<IGnssV1_1> gnssHalV1_1 = nullptr;
+sp<IGnss_V1_1> gnssHal_V1_1 = nullptr;
sp<IGnssXtra> gnssXtraIface = nullptr;
sp<IAGnssRil> agnssRilIface = nullptr;
sp<IGnssGeofencing> gnssGeofencingIface = nullptr;
@@ -127,7 +128,8 @@
sp<IGnssDebug> gnssDebugIface = nullptr;
sp<IGnssConfiguration> gnssConfigurationIface = nullptr;
sp<IGnssNi> gnssNiIface = nullptr;
-sp<IGnssMeasurement> gnssMeasurementIface = nullptr;
+sp<IGnssMeasurement_V1_0> gnssMeasurementIface = nullptr;
+sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr;
sp<IGnssNavigationMessage> gnssNavigationMessageIface = nullptr;
#define WAKE_LOCK_NAME "GPS"
@@ -1068,11 +1070,13 @@
LOG_ALWAYS_FATAL("Unable to get Java VM. Error: %d", jvmStatus);
}
- gnssHal = gnssHalV1_1 = IGnssV1_1::getService();
-
- if (gnssHal == nullptr) {
+ // TODO(b/31632518)
+ gnssHal_V1_1 = IGnss_V1_1::getService();
+ if (gnssHal_V1_1 == nullptr) {
ALOGD("gnssHal 1.1 was null, trying 1.0");
gnssHal = IGnss::getService();
+ } else {
+ gnssHal = gnssHal_V1_1;
}
if (gnssHal != nullptr) {
@@ -1116,11 +1120,21 @@
gnssNavigationMessageIface = gnssNavigationMessage;
}
- auto gnssMeasurement = gnssHal->getExtensionGnssMeasurement();
- if (!gnssMeasurement.isOk()) {
- ALOGD("Unable to get a handle to GnssMeasurement");
+ if (gnssHal_V1_1 != nullptr) {
+ auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1();
+ if (!gnssMeasurement.isOk()) {
+ ALOGD("Unable to get a handle to GnssMeasurement");
+ } else {
+ gnssMeasurementIface_V1_1 = gnssMeasurement;
+ gnssMeasurementIface = gnssMeasurementIface_V1_1;
+ }
} else {
- gnssMeasurementIface = gnssMeasurement;
+ auto gnssMeasurement_V1_0 = gnssHal->getExtensionGnssMeasurement();
+ if (!gnssMeasurement_V1_0.isOk()) {
+ ALOGD("Unable to get a handle to GnssMeasurement");
+ } else {
+ gnssMeasurementIface = gnssMeasurement_V1_0;
+ }
}
auto gnssDebug = gnssHal->getExtensionGnssDebug();
@@ -1195,8 +1209,8 @@
sp<IGnssCallback> gnssCbIface = new GnssCallback();
Return<bool> result = false;
- if (gnssHalV1_1 != nullptr) {
- result = gnssHalV1_1->setCallback_1_1(gnssCbIface);
+ if (gnssHal_V1_1 != nullptr) {
+ result = gnssHal_V1_1->setCallback_1_1(gnssCbIface);
} else {
result = gnssHal->setCallback(gnssCbIface);
}
@@ -1255,21 +1269,27 @@
static jboolean android_location_GnssLocationProvider_set_position_mode(JNIEnv* /* env */,
jobject /* obj */, jint mode, jint recurrence, jint min_interval, jint preferred_accuracy,
- jint preferred_time) {
- if (gnssHal != nullptr) {
- auto result = gnssHal->setPositionMode(static_cast<IGnss::GnssPositionMode>(mode),
- static_cast<IGnss::GnssPositionRecurrence>(recurrence),
- min_interval,
- preferred_accuracy,
- preferred_time);
- if (!result.isOk()) {
- ALOGE("%s: GNSS setPositionMode failed\n", __func__);
- return JNI_FALSE;
- } else {
- return result;
- }
+ jint preferred_time, jboolean low_power_mode) {
+ Return<bool> result = false;
+ if (gnssHal_V1_1 != nullptr) {
+ result = gnssHal_V1_1->setPositionMode_1_1(static_cast<IGnss::GnssPositionMode>(mode),
+ static_cast<IGnss::GnssPositionRecurrence>(recurrence),
+ min_interval,
+ preferred_accuracy,
+ preferred_time,
+ low_power_mode);
+ } else if (gnssHal != nullptr) {
+ result = gnssHal->setPositionMode(static_cast<IGnss::GnssPositionMode>(mode),
+ static_cast<IGnss::GnssPositionRecurrence>(recurrence),
+ min_interval,
+ preferred_accuracy,
+ preferred_time);
+ }
+ if (!result.isOk()) {
+ ALOGE("%s: GNSS setPositionMode failed\n", __func__);
+ return JNI_FALSE;
} else {
- return JNI_FALSE;
+ return result;
}
}
@@ -1679,16 +1699,29 @@
}
static jboolean android_location_GnssLocationProvider_start_measurement_collection(
- JNIEnv* env,
- jobject obj) {
+ JNIEnv* /* env */,
+ jobject /* obj */,
+ jboolean enableFullTracking) {
if (gnssMeasurementIface == nullptr) {
ALOGE("GNSS Measurement interface is not available.");
return JNI_FALSE;
}
sp<GnssMeasurementCallback> cbIface = new GnssMeasurementCallback();
- IGnssMeasurement::GnssMeasurementStatus result = gnssMeasurementIface->setCallback(cbIface);
- if (result != IGnssMeasurement::GnssMeasurementStatus::SUCCESS) {
+ IGnssMeasurement_V1_0::GnssMeasurementStatus result =
+ IGnssMeasurement_V1_0::GnssMeasurementStatus::ERROR_GENERIC;;
+ if (gnssMeasurementIface_V1_1 != nullptr) {
+ result = gnssMeasurementIface_V1_1->setCallback_1_1(cbIface,
+ enableFullTracking);
+ } else {
+ if (enableFullTracking == JNI_TRUE) {
+ // full tracking mode not supported in 1.0 HAL
+ return JNI_FALSE;
+ }
+ result = gnssMeasurementIface->setCallback(cbIface);
+ }
+
+ if (result != IGnssMeasurement_V1_0::GnssMeasurementStatus::SUCCESS) {
ALOGE("An error has been found on GnssMeasurementInterface::init, status=%d",
static_cast<int32_t>(result));
return JNI_FALSE;
@@ -1941,8 +1974,8 @@
{"native_cleanup", "()V", reinterpret_cast<void *>(
android_location_GnssLocationProvider_cleanup)},
{"native_set_position_mode",
- "(IIIII)Z",
- reinterpret_cast<void*>(android_location_GnssLocationProvider_set_position_mode)},
+ "(IIIIIZ)Z",
+ reinterpret_cast<void*>(android_location_GnssLocationProvider_set_position_mode)},
{"native_start", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_start)},
{"native_stop", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_stop)},
{"native_delete_aiding_data",
@@ -2010,7 +2043,7 @@
reinterpret_cast<void *>(
android_location_GnssLocationProvider_is_measurement_supported)},
{"native_start_measurement_collection",
- "()Z",
+ "(Z)Z",
reinterpret_cast<void *>(
android_location_GnssLocationProvider_start_measurement_collection)},
{"native_stop_measurement_collection",
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 84cfabe..e55d4ea 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -15,12 +15,18 @@
*/
package com.android.server.devicepolicy;
+import android.annotation.UserIdInt;
import android.app.admin.IDevicePolicyManager;
import android.content.ComponentName;
+import android.os.PersistableBundle;
+import android.security.keymaster.KeymasterCertificateChain;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
import com.android.internal.R;
import com.android.server.SystemService;
+import java.util.List;
+
/**
* Defines the required interface for IDevicePolicyManager implemenation.
*
@@ -55,6 +61,39 @@
* @see {@link SystemService#onStopUser}
*/
abstract void handleStopUser(int userId);
-
+
public void setSystemSetting(ComponentName who, String setting, String value){}
+
+ public void transferOwner(ComponentName admin, ComponentName target, PersistableBundle bundle) {}
+
+ public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm,
+ ParcelableKeyGenParameterSpec keySpec, KeymasterCertificateChain attestationChain) {
+ return false;
+ }
+
+ @Override
+ public boolean setPasswordBlacklist(ComponentName who, String name, List<String> blacklist,
+ boolean parent) {
+ return false;
+ }
+
+ @Override
+ public String getPasswordBlacklistName(ComponentName who, @UserIdInt int userId,
+ boolean parent) {
+ return null;
+ }
+
+ @Override
+ public boolean isPasswordBlacklisted(@UserIdInt int userId, String password) {
+ return false;
+ }
+
+ public boolean isUsingUnifiedPassword(ComponentName who) {
+ return true;
+ }
+
+ public boolean setKeyPairCertificate(ComponentName who, String callerPackage, String alias,
+ byte[] cert, byte[] chain, boolean isUserSelectable) {
+ return false;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 91ba87e..387818b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -20,6 +20,7 @@
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.USER_OP_SUCCESS;
+import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
import static android.app.admin.DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED;
import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE;
@@ -40,10 +41,13 @@
import static android.app.admin.DevicePolicyManager.DELEGATION_BLOCK_UNINSTALL;
import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
import static android.app.admin.DevicePolicyManager.DELEGATION_ENABLE_SYSTEM_APP;
+import static android.app.admin.DevicePolicyManager.DELEGATION_INSTALL_EXISTING_PACKAGE;
import static android.app.admin.DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_PACKAGES;
import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS;
import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT;
+import static android.app.admin.DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
import static android.app.admin.DevicePolicyManager.START_USER_IN_BACKGROUND;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
@@ -146,13 +150,16 @@
import android.provider.ContactsInternal;
import android.provider.Settings;
import android.provider.Settings.Global;
+import android.security.Credentials;
import android.security.IKeyChainAliasCallback;
import android.security.IKeyChainService;
import android.security.KeyChain;
import android.security.KeyChain.KeyChainConnection;
+import android.security.keymaster.KeymasterCertificateChain;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.ParcelableKeyGenParameterSpec;
import android.security.KeyStore;
+import android.security.keystore.AttestationUtils;
import android.service.persistentdata.PersistentDataBlockManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -215,6 +222,7 @@
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Function;
/**
* Implementation of the device policy APIs.
@@ -288,6 +296,8 @@
private static final String ATTR_APPLICATION_RESTRICTIONS_MANAGER
= "application-restrictions-manager";
+ private static final String MANAGED_PROVISIONING_PKG = "com.android.managedprovisioning";
+
// Comprehensive list of delegations.
private static final String DELEGATIONS[] = {
DELEGATION_CERT_INSTALL,
@@ -296,7 +306,9 @@
DELEGATION_ENABLE_SYSTEM_APP,
DELEGATION_KEEP_UNINSTALLED_PACKAGES,
DELEGATION_PACKAGE_ACCESS,
- DELEGATION_PERMISSION_GRANT
+ DELEGATION_PERMISSION_GRANT,
+ DELEGATION_INSTALL_EXISTING_PACKAGE,
+ DELEGATION_KEEP_UNINSTALLED_PACKAGES
};
/**
@@ -387,6 +399,7 @@
private final LockPatternUtils mLockPatternUtils;
private final DevicePolicyConstants mConstants;
private final DeviceAdminServiceController mDeviceAdminServiceController;
+ private final OverlayPackagesProvider mOverlayPackagesProvider;
/**
* Contains (package-user) pairs to remove. An entry (p, u) implies that removal of package p
@@ -721,6 +734,7 @@
private static final String TAG_PASSWORD_HISTORY_LENGTH = "password-history-length";
private static final String TAG_MIN_PASSWORD_LENGTH = "min-password-length";
private static final String ATTR_VALUE = "value";
+ private static final String TAG_PASSWORD_BLACKLIST = "password-blacklist";
private static final String TAG_PASSWORD_QUALITY = "password-quality";
private static final String TAG_POLICIES = "policies";
private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS =
@@ -741,7 +755,7 @@
private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled";
- final DeviceAdminInfo info;
+ DeviceAdminInfo info;
static final int DEF_PASSWORD_HISTORY_LENGTH = 0;
@@ -756,7 +770,7 @@
static final int DEF_MINIMUM_PASSWORD_NON_LETTER = 0;
@NonNull
PasswordMetrics minimumPasswordMetrics = new PasswordMetrics(
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, DEF_MINIMUM_PASSWORD_LENGTH,
+ PASSWORD_QUALITY_UNSPECIFIED, DEF_MINIMUM_PASSWORD_LENGTH,
DEF_MINIMUM_PASSWORD_LETTERS, DEF_MINIMUM_PASSWORD_UPPER_CASE,
DEF_MINIMUM_PASSWORD_LOWER_CASE, DEF_MINIMUM_PASSWORD_NUMERIC,
DEF_MINIMUM_PASSWORD_SYMBOLS, DEF_MINIMUM_PASSWORD_NON_LETTER);
@@ -853,6 +867,9 @@
// Default title of confirm credentials screen
String organizationName = null;
+ // The blacklist data is stored in a file whose name is stored in the XML
+ String passwordBlacklistFile = null;
+
ActiveAdmin(DeviceAdminInfo _info, boolean parent) {
info = _info;
isParent = parent;
@@ -882,8 +899,7 @@
out.startTag(null, TAG_POLICIES);
info.writePoliciesToXml(out);
out.endTag(null, TAG_POLICIES);
- if (minimumPasswordMetrics.quality
- != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+ if (minimumPasswordMetrics.quality != PASSWORD_QUALITY_UNSPECIFIED) {
out.startTag(null, TAG_PASSWORD_QUALITY);
out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.quality));
out.endTag(null, TAG_PASSWORD_QUALITY);
@@ -935,6 +951,11 @@
out.endTag(null, TAG_MIN_PASSWORD_NONLETTER);
}
}
+ if (passwordBlacklistFile != null) {
+ out.startTag(null, TAG_PASSWORD_BLACKLIST);
+ out.attribute(null, ATTR_VALUE, passwordBlacklistFile);
+ out.endTag(null, TAG_PASSWORD_BLACKLIST);
+ }
if (maximumTimeToUnlock != DEF_MAXIMUM_TIME_TO_UNLOCK) {
out.startTag(null, TAG_MAX_TIME_TO_UNLOCK);
out.attribute(null, ATTR_VALUE, Long.toString(maximumTimeToUnlock));
@@ -1174,7 +1195,9 @@
} else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) {
minimumPasswordMetrics.nonLetter = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
- } else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) {
+ } else if (TAG_PASSWORD_BLACKLIST.equals(tag)) {
+ passwordBlacklistFile = parser.getAttributeValue(null, ATTR_VALUE);
+ }else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) {
maximumTimeToUnlock = Long.parseLong(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_STRONG_AUTH_UNLOCK_TIMEOUT.equals(tag)) {
@@ -1393,6 +1416,13 @@
return userRestrictions;
}
+ public void transfer(DeviceAdminInfo deviceAdminInfo) {
+ if (hasParentActiveAdmin()) {
+ parentAdmin.info = deviceAdminInfo;
+ }
+ info = deviceAdminInfo;
+ }
+
void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("uid="); pw.println(getUid());
pw.print(prefix); pw.print("testOnlyAdmin=");
@@ -1422,6 +1452,8 @@
pw.println(minimumPasswordMetrics.symbols);
pw.print(prefix); pw.print("minimumPasswordNonLetter=");
pw.println(minimumPasswordMetrics.nonLetter);
+ pw.print(prefix); pw.print("passwordBlacklist=");
+ pw.println(passwordBlacklistFile != null);
pw.print(prefix); pw.print("maximumTimeToUnlock=");
pw.println(maximumTimeToUnlock);
pw.print(prefix); pw.print("strongAuthUnlockTimeout=");
@@ -1674,6 +1706,10 @@
return new LockPatternUtils(mContext);
}
+ PasswordBlacklist newPasswordBlacklist(File file) {
+ return new PasswordBlacklist(file);
+ }
+
boolean storageManagerIsFileBasedEncryptionEnabled() {
return StorageManager.isFileEncryptedNativeOnly();
}
@@ -1900,6 +1936,8 @@
mDeviceAdminServiceController = new DeviceAdminServiceController(this, mConstants);
+ mOverlayPackagesProvider = new OverlayPackagesProvider(mContext);
+
if (!mHasFeature) {
// Skip the rest of the initialization
return;
@@ -2530,7 +2568,7 @@
public DeviceAdminInfo findAdmin(ComponentName adminName, int userHandle,
- boolean throwForMissiongPermission) {
+ boolean throwForMissingPermission) {
if (!mHasFeature) {
return null;
}
@@ -2553,7 +2591,7 @@
final String message = "DeviceAdminReceiver " + adminName + " must be protected with "
+ permission.BIND_DEVICE_ADMIN;
Slog.w(LOG_TAG, message);
- if (throwForMissiongPermission &&
+ if (throwForMissingPermission &&
ai.applicationInfo.targetSdkVersion > Build.VERSION_CODES.M) {
throw new IllegalArgumentException(message);
}
@@ -2568,11 +2606,15 @@
}
}
- private JournaledFile makeJournaledFile(int userHandle) {
- final String base = userHandle == UserHandle.USER_SYSTEM
- ? mInjector.getDevicePolicyFilePathForSystemUser() + DEVICE_POLICIES_XML
- : new File(mInjector.environmentGetUserSystemDirectory(userHandle),
- DEVICE_POLICIES_XML).getAbsolutePath();
+ private File getPolicyFileDirectory(@UserIdInt int userId) {
+ return userId == UserHandle.USER_SYSTEM
+ ? new File(mInjector.getDevicePolicyFilePathForSystemUser())
+ : mInjector.environmentGetUserSystemDirectory(userId);
+ }
+
+ private JournaledFile makeJournaledFile(@UserIdInt int userId) {
+ final String base = new File(getPolicyFileDirectory(userId), DEVICE_POLICIES_XML)
+ .getAbsolutePath();
if (VERBOSE_LOG) {
Log.v(LOG_TAG, "Opening " + base);
}
@@ -2878,7 +2920,7 @@
try {
DeviceAdminInfo dai = findAdmin(
ComponentName.unflattenFromString(name), userHandle,
- /* throwForMissionPermission= */ false);
+ /* throwForMissingPermission= */ false);
if (VERBOSE_LOG
&& (UserHandle.getUserId(dai.getActivityInfo().applicationInfo.uid)
!= userHandle)) {
@@ -3282,19 +3324,9 @@
DevicePolicyData policy = getUserData(userHandle);
DeviceAdminInfo info = findAdmin(adminReceiver, userHandle,
- /* throwForMissionPermission= */ true);
- if (info == null) {
- throw new IllegalArgumentException("Bad admin: " + adminReceiver);
- }
- if (!info.getActivityInfo().applicationInfo.isInternal()) {
- throw new IllegalArgumentException("Only apps in internal storage can be active admin: "
- + adminReceiver);
- }
- if (info.getActivityInfo().applicationInfo.isInstantApp()) {
- throw new IllegalArgumentException("Instant apps cannot be device admins: "
- + adminReceiver);
- }
+ /* throwForMissingPermission= */ true);
synchronized (this) {
+ checkActiveAdminPrecondition(adminReceiver, info, policy);
long ident = mInjector.binderClearCallingIdentity();
try {
final ActiveAdmin existingAdmin
@@ -3302,10 +3334,6 @@
if (!refreshing && existingAdmin != null) {
throw new IllegalArgumentException("Admin is already added");
}
- if (policy.mRemovingAdmins.contains(adminReceiver)) {
- throw new IllegalArgumentException(
- "Trying to set an admin which is being removed");
- }
ActiveAdmin newAdmin = new ActiveAdmin(info, /* parent */ false);
newAdmin.testOnlyAdmin =
(existingAdmin != null) ? existingAdmin.testOnlyAdmin
@@ -3335,6 +3363,46 @@
}
}
+ private void transferActiveAdminUncheckedLocked(ComponentName incomingReceiver,
+ ComponentName outgoingReceiver, int userHandle) {
+ final DevicePolicyData policy = getUserData(userHandle);
+ final DeviceAdminInfo incomingDeviceInfo = findAdmin(incomingReceiver, userHandle,
+ /* throwForMissingPermission= */ true);
+ final ActiveAdmin adminToTransfer = policy.mAdminMap.get(outgoingReceiver);
+ final int oldAdminUid = adminToTransfer.getUid();
+
+ adminToTransfer.transfer(incomingDeviceInfo);
+ policy.mAdminMap.remove(outgoingReceiver);
+ policy.mAdminMap.put(incomingReceiver, adminToTransfer);
+ if (policy.mPasswordOwner == oldAdminUid) {
+ policy.mPasswordOwner = adminToTransfer.getUid();
+ }
+
+ saveSettingsLocked(userHandle);
+ //TODO: Make sure we revert back when we detect a failure.
+ sendAdminCommandLocked(adminToTransfer, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
+ null, null);
+ }
+
+ private void checkActiveAdminPrecondition(ComponentName adminReceiver, DeviceAdminInfo info,
+ DevicePolicyData policy) {
+ if (info == null) {
+ throw new IllegalArgumentException("Bad admin: " + adminReceiver);
+ }
+ if (!info.getActivityInfo().applicationInfo.isInternal()) {
+ throw new IllegalArgumentException("Only apps in internal storage can be active admin: "
+ + adminReceiver);
+ }
+ if (info.getActivityInfo().applicationInfo.isInstantApp()) {
+ throw new IllegalArgumentException("Instant apps cannot be device admins: "
+ + adminReceiver);
+ }
+ if (policy.mRemovingAdmins.contains(adminReceiver)) {
+ throw new IllegalArgumentException(
+ "Trying to set an admin which is being removed");
+ }
+ }
+
@Override
public boolean isAdminActive(ComponentName adminReceiver, int userHandle) {
if (!mHasFeature) {
@@ -3568,11 +3636,11 @@
@Override
public int getPasswordQuality(ComponentName who, int userHandle, boolean parent) {
if (!mHasFeature) {
- return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ return PASSWORD_QUALITY_UNSPECIFIED;
}
enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
- int mode = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ int mode = PASSWORD_QUALITY_UNSPECIFIED;
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
@@ -3655,30 +3723,8 @@
@Override
public int getPasswordMinimumLength(ComponentName who, int userHandle, boolean parent) {
- if (!mHasFeature) {
- return 0;
- }
- enforceFullCrossUsersPermission(userHandle);
- synchronized (this) {
- int length = 0;
-
- if (who != null) {
- ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.minimumPasswordMetrics.length : length;
- }
-
- // Return the strictest policy across all participating admins.
- List<ActiveAdmin> admins =
- getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent);
- final int N = admins.size();
- for (int i = 0; i < N; i++) {
- ActiveAdmin admin = admins.get(i);
- if (length < admin.minimumPasswordMetrics.length) {
- length = admin.minimumPasswordMetrics.length;
- }
- }
- return length;
- }
+ return getStrictestPasswordRequirement(who, userHandle, parent,
+ admin -> admin.minimumPasswordMetrics.length, PASSWORD_QUALITY_UNSPECIFIED);
}
@Override
@@ -3700,31 +3746,8 @@
@Override
public int getPasswordHistoryLength(ComponentName who, int userHandle, boolean parent) {
- if (!mHasFeature) {
- return 0;
- }
- enforceFullCrossUsersPermission(userHandle);
- synchronized (this) {
- int length = 0;
-
- if (who != null) {
- ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.passwordHistoryLength : length;
- }
-
- // Return the strictest policy across all participating admins.
- List<ActiveAdmin> admins =
- getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent);
- final int N = admins.size();
- for (int i = 0; i < N; i++) {
- ActiveAdmin admin = admins.get(i);
- if (length < admin.passwordHistoryLength) {
- length = admin.passwordHistoryLength;
- }
- }
-
- return length;
- }
+ return getStrictestPasswordRequirement(who, userHandle, parent,
+ admin -> admin.passwordHistoryLength, PASSWORD_QUALITY_UNSPECIFIED);
}
@Override
@@ -3913,30 +3936,8 @@
@Override
public int getPasswordMinimumUpperCase(ComponentName who, int userHandle, boolean parent) {
- if (!mHasFeature) {
- return 0;
- }
- enforceFullCrossUsersPermission(userHandle);
- synchronized (this) {
- int length = 0;
-
- if (who != null) {
- ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.minimumPasswordMetrics.upperCase : length;
- }
-
- // Return the strictest policy across all participating admins.
- List<ActiveAdmin> admins =
- getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent);
- final int N = admins.size();
- for (int i = 0; i < N; i++) {
- ActiveAdmin admin = admins.get(i);
- if (length < admin.minimumPasswordMetrics.upperCase) {
- length = admin.minimumPasswordMetrics.upperCase;
- }
- }
- return length;
- }
+ return getStrictestPasswordRequirement(who, userHandle, parent,
+ admin -> admin.minimumPasswordMetrics.upperCase, PASSWORD_QUALITY_COMPLEX);
}
@Override
@@ -3955,30 +3956,8 @@
@Override
public int getPasswordMinimumLowerCase(ComponentName who, int userHandle, boolean parent) {
- if (!mHasFeature) {
- return 0;
- }
- enforceFullCrossUsersPermission(userHandle);
- synchronized (this) {
- int length = 0;
-
- if (who != null) {
- ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.minimumPasswordMetrics.lowerCase : length;
- }
-
- // Return the strictest policy across all participating admins.
- List<ActiveAdmin> admins =
- getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent);
- final int N = admins.size();
- for (int i = 0; i < N; i++) {
- ActiveAdmin admin = admins.get(i);
- if (length < admin.minimumPasswordMetrics.lowerCase) {
- length = admin.minimumPasswordMetrics.lowerCase;
- }
- }
- return length;
- }
+ return getStrictestPasswordRequirement(who, userHandle, parent,
+ admin -> admin.minimumPasswordMetrics.lowerCase, PASSWORD_QUALITY_COMPLEX);
}
@Override
@@ -4000,33 +3979,8 @@
@Override
public int getPasswordMinimumLetters(ComponentName who, int userHandle, boolean parent) {
- if (!mHasFeature) {
- return 0;
- }
- enforceFullCrossUsersPermission(userHandle);
- synchronized (this) {
- int length = 0;
-
- if (who != null) {
- ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.minimumPasswordMetrics.letters : length;
- }
-
- // Return the strictest policy across all participating admins.
- List<ActiveAdmin> admins =
- getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent);
- final int N = admins.size();
- for (int i = 0; i < N; i++) {
- ActiveAdmin admin = admins.get(i);
- if (!isLimitPasswordAllowed(admin, PASSWORD_QUALITY_COMPLEX)) {
- continue;
- }
- if (length < admin.minimumPasswordMetrics.letters) {
- length = admin.minimumPasswordMetrics.letters;
- }
- }
- return length;
- }
+ return getStrictestPasswordRequirement(who, userHandle, parent,
+ admin -> admin.minimumPasswordMetrics.letters, PASSWORD_QUALITY_COMPLEX);
}
@Override
@@ -4048,34 +4002,9 @@
@Override
public int getPasswordMinimumNumeric(ComponentName who, int userHandle, boolean parent) {
- if (!mHasFeature) {
- return 0;
- }
- enforceFullCrossUsersPermission(userHandle);
- synchronized (this) {
- int length = 0;
-
- if (who != null) {
- ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.minimumPasswordMetrics.numeric : length;
- }
-
- // Return the strictest policy across all participating admins.
- List<ActiveAdmin> admins =
- getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent);
- final int N = admins.size();
- for (int i = 0; i < N; i++) {
- ActiveAdmin admin = admins.get(i);
- if (!isLimitPasswordAllowed(admin, PASSWORD_QUALITY_COMPLEX)) {
- continue;
- }
- if (length < admin.minimumPasswordMetrics.numeric) {
- length = admin.minimumPasswordMetrics.numeric;
- }
- }
- return length;
- }
- }
+ return getStrictestPasswordRequirement(who, userHandle, parent,
+ admin -> admin.minimumPasswordMetrics.numeric, PASSWORD_QUALITY_COMPLEX);
+ }
@Override
public void setPasswordMinimumSymbols(ComponentName who, int length, boolean parent) {
@@ -4096,33 +4025,8 @@
@Override
public int getPasswordMinimumSymbols(ComponentName who, int userHandle, boolean parent) {
- if (!mHasFeature) {
- return 0;
- }
- enforceFullCrossUsersPermission(userHandle);
- synchronized (this) {
- int length = 0;
-
- if (who != null) {
- ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.minimumPasswordMetrics.symbols : length;
- }
-
- // Return the strictest policy across all participating admins.
- List<ActiveAdmin> admins =
- getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent);
- final int N = admins.size();
- for (int i = 0; i < N; i++) {
- ActiveAdmin admin = admins.get(i);
- if (!isLimitPasswordAllowed(admin, PASSWORD_QUALITY_COMPLEX)) {
- continue;
- }
- if (length < admin.minimumPasswordMetrics.symbols) {
- length = admin.minimumPasswordMetrics.symbols;
- }
- }
- return length;
- }
+ return getStrictestPasswordRequirement(who, userHandle, parent,
+ admin -> admin.minimumPasswordMetrics.symbols, PASSWORD_QUALITY_COMPLEX);
}
@Override
@@ -4144,35 +4048,173 @@
@Override
public int getPasswordMinimumNonLetter(ComponentName who, int userHandle, boolean parent) {
+ return getStrictestPasswordRequirement(who, userHandle, parent,
+ admin -> admin.minimumPasswordMetrics.nonLetter, PASSWORD_QUALITY_COMPLEX);
+ }
+
+ /**
+ * Calculates strictest (maximum) value for a given password property enforced by admin[s].
+ */
+ private int getStrictestPasswordRequirement(ComponentName who, int userHandle,
+ boolean parent, Function<ActiveAdmin, Integer> getter, int minimumPasswordQuality) {
if (!mHasFeature) {
return 0;
}
enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
- int length = 0;
-
if (who != null) {
- ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.minimumPasswordMetrics.nonLetter : length;
+ final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
+ return admin != null ? getter.apply(admin) : 0;
}
- // Return the strictest policy across all participating admins.
- List<ActiveAdmin> admins =
+ int maxValue = 0;
+ final List<ActiveAdmin> admins =
getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent);
final int N = admins.size();
for (int i = 0; i < N; i++) {
- ActiveAdmin admin = admins.get(i);
- if (!isLimitPasswordAllowed(admin, PASSWORD_QUALITY_COMPLEX)) {
+ final ActiveAdmin admin = admins.get(i);
+ if (!isLimitPasswordAllowed(admin, minimumPasswordQuality)) {
continue;
}
- if (length < admin.minimumPasswordMetrics.nonLetter) {
- length = admin.minimumPasswordMetrics.nonLetter;
+ final Integer adminValue = getter.apply(admin);
+ if (adminValue > maxValue) {
+ maxValue = adminValue;
}
}
- return length;
+ return maxValue;
}
}
+ /* @return the password blacklist set by the admin or {@code null} if none. */
+ PasswordBlacklist getAdminPasswordBlacklistLocked(@NonNull ActiveAdmin admin) {
+ final int userId = UserHandle.getUserId(admin.getUid());
+ return admin.passwordBlacklistFile == null ? null : new PasswordBlacklist(
+ new File(getPolicyFileDirectory(userId), admin.passwordBlacklistFile));
+ }
+
+ private static final String PASSWORD_BLACKLIST_FILE_PREFIX = "password-blacklist-";
+ private static final String PASSWORD_BLACKLIST_FILE_SUFFIX = "";
+
+ @Override
+ public boolean setPasswordBlacklist(ComponentName who, String name, List<String> blacklist,
+ boolean parent) {
+ if (!mHasFeature) {
+ return false;
+ }
+ Preconditions.checkNotNull(who, "who is null");
+
+ synchronized (this) {
+ final ActiveAdmin admin = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
+ final int userId = mInjector.userHandleGetCallingUserId();
+ PasswordBlacklist adminBlacklist = getAdminPasswordBlacklistLocked(admin);
+
+ if (blacklist == null || blacklist.isEmpty()) {
+ // Remove the adminBlacklist
+ admin.passwordBlacklistFile = null;
+ saveSettingsLocked(userId);
+ if (adminBlacklist != null) {
+ adminBlacklist.delete();
+ }
+ return true;
+ }
+
+ // Validate server side
+ Preconditions.checkNotNull(name, "name is null");
+ DevicePolicyManager.enforcePasswordBlacklistSize(blacklist);
+
+ // Blacklist is case insensitive so normalize to lower case
+ final int blacklistSize = blacklist.size();
+ for (int i = 0; i < blacklistSize; ++i) {
+ blacklist.set(i, blacklist.get(i).toLowerCase());
+ }
+
+ final boolean isNewBlacklist = adminBlacklist == null;
+ if (isNewBlacklist) {
+ // Create a new file for the blacklist. There could be multiple admins, each setting
+ // different blacklists, to restrict a user's credential, for example a managed
+ // profile can impose restrictions on its parent while the parent is already
+ // restricted by its own admin. A deterministic naming scheme would be fragile if
+ // new types of admin are introduced so we generate and save the file name instead.
+ // This isn't a temporary file but it reuses the name generation logic
+ final File file;
+ try {
+ file = File.createTempFile(PASSWORD_BLACKLIST_FILE_PREFIX,
+ PASSWORD_BLACKLIST_FILE_SUFFIX, getPolicyFileDirectory(userId));
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "Failed to make a file for the blacklist", e);
+ return false;
+ }
+ adminBlacklist = mInjector.newPasswordBlacklist(file);
+ }
+
+ if (adminBlacklist.savePasswordBlacklist(name, blacklist)) {
+ if (isNewBlacklist) {
+ // The blacklist was saved so point the admin to the file
+ admin.passwordBlacklistFile = adminBlacklist.getFile().getName();
+ saveSettingsLocked(userId);
+ }
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public String getPasswordBlacklistName(ComponentName who, @UserIdInt int userId,
+ boolean parent) {
+ if (!mHasFeature) {
+ return null;
+ }
+ Preconditions.checkNotNull(who, "who is null");
+ enforceFullCrossUsersPermission(userId);
+ synchronized (this) {
+ final ActiveAdmin admin = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
+ final PasswordBlacklist blacklist = getAdminPasswordBlacklistLocked(admin);
+ if (blacklist == null) {
+ return null;
+ }
+ return blacklist.getName();
+ }
+ }
+
+ @Override
+ public boolean isPasswordBlacklisted(@UserIdInt int userId, String password) {
+ if (!mHasFeature) {
+ return false;
+ }
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.TEST_BLACKLISTED_PASSWORD, null);
+ return isPasswordBlacklistedInternal(userId, password);
+ }
+
+ private boolean isPasswordBlacklistedInternal(@UserIdInt int userId, String password) {
+ Preconditions.checkNotNull(password, "Password is null");
+ enforceFullCrossUsersPermission(userId);
+
+ // Normalize to lower case for case insensitive blacklist match
+ final String lowerCasePassword = password.toLowerCase();
+
+ synchronized (this) {
+ final List<ActiveAdmin> admins =
+ getActiveAdminsForLockscreenPoliciesLocked(userId, /* parent */ false);
+ final int N = admins.size();
+ for (int i = 0; i < N; i++) {
+ final PasswordBlacklist blacklist
+ = getAdminPasswordBlacklistLocked(admins.get(i));
+ if (blacklist != null) {
+ if (blacklist.isPasswordBlacklisted(lowerCasePassword)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
@Override
public boolean isActivePasswordSufficient(int userHandle, boolean parent) {
if (!mHasFeature) {
@@ -4191,6 +4233,17 @@
}
@Override
+ public boolean isUsingUnifiedPassword(ComponentName admin) {
+ if (!mHasFeature) {
+ return true;
+ }
+ final int userId = mInjector.userHandleGetCallingUserId();
+ enforceProfileOrDeviceOwner(admin);
+ enforceManagedProfile(userId, "query unified challenge status");
+ return !isSeparateProfileChallengeEnabled(userId);
+ }
+
+ @Override
public boolean isProfileActivePasswordSufficientForParent(int userHandle) {
if (!mHasFeature) {
return true;
@@ -4209,7 +4262,7 @@
private boolean isActivePasswordSufficientForUserLocked(
DevicePolicyData policy, int userHandle, boolean parent) {
final int requiredPasswordQuality = getPasswordQuality(null, userHandle, parent);
- if (requiredPasswordQuality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+ if (requiredPasswordQuality == PASSWORD_QUALITY_UNSPECIFIED) {
// A special case is when there is no required password quality, then we just return
// true since any password would be sufficient. This is for the scenario when a work
// profile is first created so there is no information about the current password but
@@ -4449,10 +4502,10 @@
synchronized (this) {
quality = getPasswordQuality(null, userHandle, /* parent */ false);
if (quality == DevicePolicyManager.PASSWORD_QUALITY_MANAGED) {
- quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ quality = PASSWORD_QUALITY_UNSPECIFIED;
}
final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
- if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+ if (quality != PASSWORD_QUALITY_UNSPECIFIED) {
final int realQuality = metrics.quality;
if (realQuality < quality
&& quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
@@ -4518,6 +4571,11 @@
return false;
}
}
+
+ if (isPasswordBlacklistedInternal(userHandle, password)) {
+ Slog.w(LOG_TAG, "resetPassword: the password is blacklisted");
+ return false;
+ }
}
DevicePolicyData policy = getUserData(userHandle);
@@ -5025,19 +5083,22 @@
@Override
public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm,
- ParcelableKeyGenParameterSpec parcelableKeySpec) {
+ ParcelableKeyGenParameterSpec parcelableKeySpec,
+ KeymasterCertificateChain attestationChain) {
enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
DELEGATION_CERT_INSTALL);
final KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec();
if (TextUtils.isEmpty(keySpec.getKeystoreAlias())) {
throw new IllegalArgumentException("Empty alias provided.");
}
+ final String alias = keySpec.getKeystoreAlias();
// As the caller will be granted access to the key, ensure no UID was specified, as
// it will not have the desired effect.
if (keySpec.getUid() != KeyStore.UID_SELF) {
Log.e(LOG_TAG, "Only the caller can be granted access to the generated keypair.");
return false;
}
+
final int callingUid = mInjector.binderGetCallingUid();
final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
@@ -5046,7 +5107,16 @@
try (KeyChainConnection keyChainConnection =
KeyChain.bindAsUser(mContext, userHandle)) {
IKeyChainService keyChain = keyChainConnection.getService();
- final boolean generationResult = keyChain.generateKeyPair(algorithm, parcelableKeySpec);
+
+ // Copy the provided keySpec, excluding the attestation challenge, which will be
+ // used later for requesting key attestation record.
+ final KeyGenParameterSpec noAttestationSpec =
+ new KeyGenParameterSpec.Builder(keySpec)
+ .setAttestationChallenge(null)
+ .build();
+
+ final boolean generationResult = keyChain.generateKeyPair(algorithm,
+ new ParcelableKeyGenParameterSpec(noAttestationSpec));
if (!generationResult) {
Log.e(LOG_TAG, "KeyChain failed to generate a keypair.");
return false;
@@ -5057,7 +5127,19 @@
// Note the use of the calling UID, since the request for the private
// key will come from the client's process, so the grant has to be for
// that UID.
- keyChain.setGrant(callingUid, keySpec.getKeystoreAlias(), true);
+ keyChain.setGrant(callingUid, alias, true);
+
+ final byte[] attestationChallenge = keySpec.getAttestationChallenge();
+ if (attestationChallenge != null) {
+ final boolean attestationResult = keyChain.attestKey(
+ alias, attestationChallenge, attestationChain);
+ if (!attestationResult) {
+ Log.e(LOG_TAG, String.format(
+ "Attestation for %s failed, deleting key.", alias));
+ keyChain.removeKeyPair(alias);
+ return false;
+ }
+ }
return true;
}
} catch (RemoteException e) {
@@ -5072,6 +5154,33 @@
}
@Override
+ public boolean setKeyPairCertificate(ComponentName who, String callerPackage, String alias,
+ byte[] cert, byte[] chain, boolean isUserSelectable) {
+ enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+ DELEGATION_CERT_INSTALL);
+
+ final int callingUid = mInjector.binderGetCallingUid();
+ final long id = mInjector.binderClearCallingIdentity();
+ try (final KeyChainConnection keyChainConnection =
+ KeyChain.bindAsUser(mContext, UserHandle.getUserHandleForUid(callingUid))) {
+ IKeyChainService keyChain = keyChainConnection.getService();
+ if (!keyChain.setKeyPairCertificate(alias, cert, chain)) {
+ return false;
+ }
+ keyChain.setUserSelectable(alias, isUserSelectable);
+ return true;
+ } catch (InterruptedException e) {
+ Log.w(LOG_TAG, "Interrupted while setting keypair certificate", e);
+ Thread.currentThread().interrupt();
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Failed setting keypair certificate", e);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ return false;
+ }
+
+ @Override
public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias,
final IBinder response) {
// Caller UID needs to be trusted, so we restrict this method to SYSTEM_UID callers.
@@ -8318,6 +8427,7 @@
final boolean ephemeral = (flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0;
final boolean demo = (flags & DevicePolicyManager.MAKE_USER_DEMO) != 0
&& UserManager.isDeviceInDemoMode(mContext);
+ final boolean leaveAllSystemAppsEnabled = (flags & LEAVE_ALL_SYSTEM_APPS_ENABLED) != 0;
// Create user.
UserHandle user = null;
synchronized (this) {
@@ -8332,8 +8442,14 @@
if (demo) {
userInfoFlags |= UserInfo.FLAG_DEMO;
}
+ String[] disallowedPackages = null;
+ if (!leaveAllSystemAppsEnabled) {
+ disallowedPackages = mOverlayPackagesProvider.getNonRequiredApps(admin,
+ UserHandle.myUserId(), ACTION_PROVISION_MANAGED_USER).toArray(
+ new String[0]);
+ }
UserInfo userInfo = mUserManagerInternal.createUserEvenWhenDisallowed(name,
- userInfoFlags);
+ userInfoFlags, disallowedPackages);
if (userInfo != null) {
user = userInfo.getUserHandle();
}
@@ -8344,11 +8460,20 @@
if (user == null) {
return null;
}
+
+ final int userHandle = user.getIdentifier();
+ final Intent intent = new Intent(DevicePolicyManager.ACTION_MANAGED_USER_CREATED)
+ .putExtra(Intent.EXTRA_USER_HANDLE, userHandle)
+ .putExtra(
+ DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
+ leaveAllSystemAppsEnabled)
+ .setPackage(MANAGED_PROVISIONING_PKG)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+
final long id = mInjector.binderClearCallingIdentity();
try {
final String adminPkg = admin.getPackageName();
-
- final int userHandle = user.getIdentifier();
try {
// Install the profile owner if not present.
if (!mIPackageManager.isPackageAvailable(adminPkg, userHandle)) {
@@ -8378,7 +8503,7 @@
if ((flags & START_USER_IN_BACKGROUND) != 0) {
try {
- mInjector.getIActivityManager().startUserInBackground(user.getIdentifier());
+ mInjector.getIActivityManager().startUserInBackground(userHandle);
} catch (RemoteException re) {
// Does not happen, same process
}
@@ -8386,7 +8511,7 @@
return user;
} catch (Throwable re) {
- mUserManager.removeUser(user.getIdentifier());
+ mUserManager.removeUser(userHandle);
return null;
} finally {
mInjector.binderRestoreCallingIdentity(id);
@@ -8538,6 +8663,22 @@
}
@Override
+ public boolean isEphemeralUser(ComponentName who) {
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ synchronized (this) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ }
+
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ return mInjector.getUserManager().isUserEphemeral(callingUserId);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ }
+
+ @Override
public Bundle getApplicationRestrictions(ComponentName who, String callerPackage,
String packageName) {
enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
@@ -8843,6 +8984,39 @@
}
@Override
+ public boolean installExistingPackage(ComponentName who, String callerPackage,
+ String packageName) {
+ synchronized (this) {
+ // Ensure the caller is a PO or an install existing package delegate
+ enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+ DELEGATION_INSTALL_EXISTING_PACKAGE);
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
+ if (!isUserAffiliatedWithDeviceLocked(callingUserId)) {
+ throw new SecurityException("Admin " + who +
+ " is neither the device owner or affiliated user's profile owner.");
+ }
+
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ if (VERBOSE_LOG) {
+ Slog.v(LOG_TAG, "installing " + packageName + " for "
+ + callingUserId);
+ }
+
+ // Install the package.
+ return mIPackageManager.installExistingPackageAsUser(packageName, callingUserId,
+ 0 /*installFlags*/, PackageManager.INSTALL_REASON_POLICY)
+ == PackageManager.INSTALL_SUCCEEDED;
+ } catch (RemoteException re) {
+ // shouldn't happen
+ return false;
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ }
+ }
+
+ @Override
public void setAccountManagementDisabled(ComponentName who, String accountType,
boolean disabled) {
if (!mHasFeature) {
@@ -11554,8 +11728,8 @@
long ident = mInjector.binderClearCallingIdentity();
try {
- return ActivityManager.getService().clearApplicationUserData(packageName, callback,
- userId);
+ return ActivityManager.getService().clearApplicationUserData(packageName, false,
+ callback, userId);
} catch(RemoteException re) {
// Same process, should not happen.
} catch (SecurityException se) {
@@ -11611,4 +11785,67 @@
return (deviceOwner != null) && deviceOwner.isLogoutEnabled;
}
+ @Override
+ public List<String> getDisallowedSystemApps(ComponentName admin, int userId,
+ String provisioningAction) throws RemoteException {
+ enforceCanManageProfileAndDeviceOwners();
+ return new ArrayList<>(
+ mOverlayPackagesProvider.getNonRequiredApps(admin, userId, provisioningAction));
+ }
+
+ //TODO: Add callback information to the javadoc once it is completed.
+ //TODO: Make transferOwner atomic.
+ @Override
+ public void transferOwner(ComponentName admin, ComponentName target, PersistableBundle bundle) {
+ if (!mHasFeature) {
+ return;
+ }
+
+ Preconditions.checkNotNull(admin, "Admin cannot be null.");
+ Preconditions.checkNotNull(target, "Target cannot be null.");
+
+ enforceProfileOrDeviceOwner(admin);
+
+ if (admin.equals(target)) {
+ throw new IllegalArgumentException("Provided administrator and target are "
+ + "the same object.");
+ }
+
+ if (admin.getPackageName().equals(target.getPackageName())) {
+ throw new IllegalArgumentException("Provided administrator and target have "
+ + "the same package name.");
+ }
+
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
+ final DevicePolicyData policy = getUserData(callingUserId);
+ final DeviceAdminInfo incomingDeviceInfo = findAdmin(target, callingUserId,
+ /* throwForMissingPermission= */ true);
+ checkActiveAdminPrecondition(target, incomingDeviceInfo, policy);
+
+ final long id = mInjector.binderClearCallingIdentity();
+ try {
+ //STOPSHIP add support for COMP, DO, edge cases when device is rebooted/work mode off,
+ //transfer callbacks and broadcast
+ if (isProfileOwner(admin, callingUserId)) {
+ transferProfileOwner(admin, target, callingUserId);
+ }
+ } finally {
+ mInjector.binderRestoreCallingIdentity(id);
+ }
+ }
+
+ /**
+ * Transfers the profile owner for user with id profileOwnerUserId from admin to target.
+ */
+ private void transferProfileOwner(ComponentName admin, ComponentName target,
+ int profileOwnerUserId) {
+ synchronized (this) {
+ transferActiveAdminUncheckedLocked(target, admin, profileOwnerUserId);
+ mOwners.transferProfileOwner(target, profileOwnerUserId);
+ Slog.i(LOG_TAG, "Profile owner set: " + target + " on user " + profileOwnerUserId);
+ mOwners.writeProfileOwner(profileOwnerUserId);
+ mDeviceAdminServiceController.startServiceForOwner(
+ target.getPackageName(), profileOwnerUserId, "transfer-profile-owner");
+ }
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
index 4a6bee5..f91f959 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
@@ -29,10 +29,10 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.TimeUnit;
/**
* A Handler class for managing network logging on a background thread.
@@ -81,6 +81,7 @@
}
};
+ @VisibleForTesting
static final int LOG_NETWORK_EVENT_MSG = 1;
/** Network events accumulated so far to be finalized into a batch at some point. */
@@ -106,9 +107,15 @@
private long mLastRetrievedBatchToken;
NetworkLoggingHandler(Looper looper, DevicePolicyManagerService dpm) {
+ this(looper, dpm, 0 /* event id */);
+ }
+
+ @VisibleForTesting
+ NetworkLoggingHandler(Looper looper, DevicePolicyManagerService dpm, long id) {
super(looper);
- mDpm = dpm;
- mAlarmManager = mDpm.mInjector.getAlarmManager();
+ this.mDpm = dpm;
+ this.mAlarmManager = mDpm.mInjector.getAlarmManager();
+ this.mId = id;
}
@Override
@@ -189,7 +196,13 @@
if (mNetworkEvents.size() > 0) {
// Assign ids to the events.
for (NetworkEvent event : mNetworkEvents) {
- event.setId(mId++);
+ event.setId(mId);
+ if (mId == Long.MAX_VALUE) {
+ Slog.i(TAG, "Reached maximum id value; wrapping around ." + mCurrentBatchToken);
+ mId = 0;
+ } else {
+ mId++;
+ }
}
// Finalize the batch and start a new one from scratch.
if (mBatches.size() >= MAX_BATCHES) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
new file mode 100644
index 0000000..d0ec0ee
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java
@@ -0,0 +1,232 @@
+/*
+ * 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 com.android.server.devicepolicy;
+
+import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
+import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
+import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.app.admin.DeviceAdminReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.ArraySet;
+import android.view.inputmethod.InputMethodInfo;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.view.IInputMethodManager;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Class that provides the apps that are not required on a managed device / profile according to the
+ * overlays provided via (vendor_|)required_apps_managed_(profile|device).xml.
+ */
+public class OverlayPackagesProvider {
+
+ protected static final String TAG = "OverlayPackagesProvider";
+
+ private final PackageManager mPm;
+ private final IInputMethodManager mIInputMethodManager;
+ private final Context mContext;
+
+ public OverlayPackagesProvider(Context context) {
+ this(context, getIInputMethodManager());
+ }
+
+ @VisibleForTesting
+ OverlayPackagesProvider(Context context, IInputMethodManager iInputMethodManager) {
+ mContext = context;
+ mPm = checkNotNull(context.getPackageManager());
+ mIInputMethodManager = checkNotNull(iInputMethodManager);
+ }
+
+ /**
+ * Computes non-required apps. All the system apps with a launcher that are not in
+ * the required set of packages will be considered as non-required apps.
+ *
+ * Note: If an app is mistakenly listed as both required and disallowed, it will be treated as
+ * disallowed.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param userId The userId for which the non-required apps needs to be computed.
+ * @param provisioningAction action indicating type of provisioning, should be one of
+ * {@link ACTION_PROVISION_MANAGED_DEVICE}, {@link
+ * ACTION_PROVISION_MANAGED_PROFILE} or
+ * {@link ACTION_PROVISION_MANAGED_USER}.
+ * @return the set of non-required apps.
+ */
+ @NonNull
+ public Set<String> getNonRequiredApps(@NonNull ComponentName admin, int userId,
+ @NonNull String provisioningAction) {
+ final Set<String> nonRequiredApps = getLaunchableApps(userId);
+ // Newly installed system apps are uninstalled when they are not required and are either
+ // disallowed or have a launcher icon.
+ nonRequiredApps.removeAll(getRequiredApps(provisioningAction, admin.getPackageName()));
+ // Don't delete the system input method packages in case of Device owner provisioning.
+ if (ACTION_PROVISION_MANAGED_DEVICE.equals(provisioningAction)
+ || ACTION_PROVISION_MANAGED_USER.equals(provisioningAction)) {
+ nonRequiredApps.removeAll(getSystemInputMethods());
+ }
+ nonRequiredApps.addAll(getDisallowedApps(provisioningAction));
+ return nonRequiredApps;
+ }
+
+ private Set<String> getLaunchableApps(int userId) {
+ final Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
+ launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ final List<ResolveInfo> resolveInfos = mPm.queryIntentActivitiesAsUser(launcherIntent,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ userId);
+ final Set<String> apps = new ArraySet<>();
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ apps.add(resolveInfo.activityInfo.packageName);
+ }
+ return apps;
+ }
+
+ private Set<String> getSystemInputMethods() {
+ // InputMethodManager is final so it cannot be mocked.
+ // So, we're using IInputMethodManager directly because it can be mocked.
+ final List<InputMethodInfo> inputMethods;
+ try {
+ inputMethods = mIInputMethodManager.getInputMethodList();
+ } catch (RemoteException e) {
+ // Should not happen
+ return null;
+ }
+ final Set<String> systemInputMethods = new ArraySet<>();
+ for (InputMethodInfo inputMethodInfo : inputMethods) {
+ ApplicationInfo applicationInfo = inputMethodInfo.getServiceInfo().applicationInfo;
+ if (applicationInfo.isSystemApp()) {
+ systemInputMethods.add(inputMethodInfo.getPackageName());
+ }
+ }
+ return systemInputMethods;
+ }
+
+ private Set<String> getRequiredApps(String provisioningAction, String dpcPackageName) {
+ final Set<String> requiredApps = new ArraySet<>();
+ requiredApps.addAll(getRequiredAppsSet(provisioningAction));
+ requiredApps.addAll(getVendorRequiredAppsSet(provisioningAction));
+ requiredApps.add(dpcPackageName);
+ return requiredApps;
+ }
+
+ private Set<String> getDisallowedApps(String provisioningAction) {
+ final Set<String> disallowedApps = new ArraySet<>();
+ disallowedApps.addAll(getDisallowedAppsSet(provisioningAction));
+ disallowedApps.addAll(getVendorDisallowedAppsSet(provisioningAction));
+ return disallowedApps;
+ }
+
+ private static IInputMethodManager getIInputMethodManager() {
+ final IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
+ return IInputMethodManager.Stub.asInterface(b);
+ }
+
+ private Set<String> getRequiredAppsSet(String provisioningAction) {
+ final int resId;
+ switch (provisioningAction) {
+ case ACTION_PROVISION_MANAGED_USER:
+ resId = R.array.required_apps_managed_user;
+ break;
+ case ACTION_PROVISION_MANAGED_PROFILE:
+ resId = R.array.required_apps_managed_profile;
+ break;
+ case ACTION_PROVISION_MANAGED_DEVICE:
+ resId = R.array.required_apps_managed_device;
+ break;
+ default:
+ throw new IllegalArgumentException("Provisioning type "
+ + provisioningAction + " not supported.");
+ }
+ return new ArraySet<>(Arrays.asList(mContext.getResources().getStringArray(resId)));
+ }
+
+ private Set<String> getDisallowedAppsSet(String provisioningAction) {
+ final int resId;
+ switch (provisioningAction) {
+ case ACTION_PROVISION_MANAGED_USER:
+ resId = R.array.disallowed_apps_managed_user;
+ break;
+ case ACTION_PROVISION_MANAGED_PROFILE:
+ resId = R.array.disallowed_apps_managed_profile;
+ break;
+ case ACTION_PROVISION_MANAGED_DEVICE:
+ resId = R.array.disallowed_apps_managed_device;
+ break;
+ default:
+ throw new IllegalArgumentException("Provisioning type "
+ + provisioningAction + " not supported.");
+ }
+ return new ArraySet<>(Arrays.asList(mContext.getResources().getStringArray(resId)));
+ }
+
+ private Set<String> getVendorRequiredAppsSet(String provisioningAction) {
+ final int resId;
+ switch (provisioningAction) {
+ case ACTION_PROVISION_MANAGED_USER:
+ resId = R.array.vendor_required_apps_managed_user;
+ break;
+ case ACTION_PROVISION_MANAGED_PROFILE:
+ resId = R.array.vendor_required_apps_managed_profile;
+ break;
+ case ACTION_PROVISION_MANAGED_DEVICE:
+ resId = R.array.vendor_required_apps_managed_device;
+ break;
+ default:
+ throw new IllegalArgumentException("Provisioning type "
+ + provisioningAction + " not supported.");
+ }
+ return new ArraySet<>(Arrays.asList(mContext.getResources().getStringArray(resId)));
+ }
+
+ private Set<String> getVendorDisallowedAppsSet(String provisioningAction) {
+ final int resId;
+ switch (provisioningAction) {
+ case ACTION_PROVISION_MANAGED_USER:
+ resId = R.array.vendor_disallowed_apps_managed_user;
+ break;
+ case ACTION_PROVISION_MANAGED_PROFILE:
+ resId = R.array.vendor_disallowed_apps_managed_profile;
+ break;
+ case ACTION_PROVISION_MANAGED_DEVICE:
+ resId = R.array.vendor_disallowed_apps_managed_device;
+ break;
+ default:
+ throw new IllegalArgumentException("Provisioning type "
+ + provisioningAction + " not supported.");
+ }
+ return new ArraySet<>(Arrays.asList(mContext.getResources().getStringArray(resId)));
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index a5500dd..9042a8d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -277,6 +277,17 @@
}
}
+ void transferProfileOwner(ComponentName target, int userId) {
+ synchronized (mLock) {
+ final OwnerInfo ownerInfo = mProfileOwners.get(userId);
+ final OwnerInfo newOwnerInfo = new OwnerInfo(target.getPackageName(), target,
+ ownerInfo.userRestrictionsMigrated, ownerInfo.remoteBugreportUri,
+ ownerInfo.remoteBugreportHash);
+ mProfileOwners.put(userId, newOwnerInfo);
+ pushToPackageManagerLocked();
+ }
+ }
+
ComponentName getProfileOwnerComponent(int userId) {
synchronized (mLock) {
OwnerInfo profileOwner = mProfileOwners.get(userId);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PasswordBlacklist.java b/services/devicepolicy/java/com/android/server/devicepolicy/PasswordBlacklist.java
new file mode 100644
index 0000000..6a9b53a
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PasswordBlacklist.java
@@ -0,0 +1,165 @@
+/*
+ * 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.devicepolicy;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.AtomicFile;
+import android.util.Slog;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Manages the blacklisted passwords.
+ *
+ * This caller must ensure synchronized access.
+ */
+public class PasswordBlacklist {
+ private static final String TAG = "PasswordBlacklist";
+
+ private final AtomicFile mFile;
+
+ /**
+ * Create an object to manage the password blacklist.
+ *
+ * This is a lightweight operation to prepare variables but not perform any IO.
+ */
+ public PasswordBlacklist(File file) {
+ mFile = new AtomicFile(file);
+ }
+
+ /**
+ * Atomically replace the blacklist.
+ *
+ * Pass {@code null} for an empty list.
+ */
+ public boolean savePasswordBlacklist(@NonNull String name, @NonNull List<String> blacklist) {
+ FileOutputStream fos = null;
+ try {
+ fos = mFile.startWrite();
+ final DataOutputStream out = buildStreamForWriting(fos);
+ final Header header = new Header(Header.VERSION_1, name, blacklist.size());
+ header.write(out);
+ final int blacklistSize = blacklist.size();
+ for (int i = 0; i < blacklistSize; ++i) {
+ out.writeUTF(blacklist.get(i));
+ }
+ out.flush();
+ mFile.finishWrite(fos);
+ return true;
+ } catch (IOException e) {
+ mFile.failWrite(fos);
+ return false;
+ }
+ }
+
+ /** @return the name of the blacklist or {@code null} if none set. */
+ public String getName() {
+ try (DataInputStream in = openForReading()) {
+ return Header.read(in).mName;
+ } catch (IOException e) {
+ Slog.wtf(TAG, "Failed to read blacklist file", e);
+ }
+ return null;
+ }
+
+ /** @return the number of blacklisted passwords. */
+ public int getSize() {
+ final int blacklistSize;
+ try (DataInputStream in = openForReading()) {
+ return Header.read(in).mSize;
+ } catch (IOException e) {
+ Slog.wtf(TAG, "Failed to read blacklist file", e);
+ }
+ return 0;
+ }
+
+ /** @return whether the password matches an blacklisted item. */
+ public boolean isPasswordBlacklisted(@NonNull String password) {
+ final int blacklistSize;
+ try (DataInputStream in = openForReading()) {
+ final Header header = Header.read(in);
+ for (int i = 0; i < header.mSize; ++i) {
+ if (in.readUTF().equals(password)) {
+ return true;
+ }
+ }
+ } catch (IOException e) {
+ Slog.wtf(TAG, "Failed to read blacklist file", e);
+ // Fail safe and block all passwords. Setting a new blacklist should resolve this
+ // problem which can be identified by examining the log.
+ return true;
+ }
+ return false;
+ }
+
+ /** Delete the blacklist completely from disk. */
+ public void delete() {
+ mFile.delete();
+ }
+
+ /** Get the file the blacklist is stored in. */
+ public File getFile() {
+ return mFile.getBaseFile();
+ }
+
+ private DataOutputStream buildStreamForWriting(FileOutputStream fos) {
+ return new DataOutputStream(new BufferedOutputStream(fos));
+ }
+
+ private DataInputStream openForReading() throws IOException {
+ return new DataInputStream(new BufferedInputStream(mFile.openRead()));
+ }
+
+ /**
+ * Helper to read and write the header of the blacklist file.
+ */
+ private static class Header {
+ static final int VERSION_1 = 1;
+
+ final int mVersion; // File format version
+ final String mName;
+ final int mSize;
+
+ Header(int version, String name, int size) {
+ mVersion = version;
+ mName = name;
+ mSize = size;
+ }
+
+ void write(DataOutputStream out) throws IOException {
+ out.writeInt(mVersion);
+ out.writeUTF(mName);
+ out.writeInt(mSize);
+ }
+
+ static Header read(DataInputStream in) throws IOException {
+ final int version = in.readInt();
+ final String name = in.readUTF();
+ final int size = in.readInt();
+ return new Header(version, name, size);
+ }
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4c994b9..4310a98 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources.Theme;
+import android.database.sqlite.SQLiteCompatibilityWalFlags;
import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
@@ -327,6 +328,8 @@
// The system server should never make non-oneway calls
Binder.setWarnOnBlocking(true);
+ // Deactivate SQLiteCompatibilityWalFlags until settings provider is initialized
+ SQLiteCompatibilityWalFlags.init(null);
// Here we go!
Slog.i(TAG, "Entered the Android system server!");
@@ -803,6 +806,8 @@
traceBeginAndSlog("InstallSystemProviders");
mActivityManagerService.installSystemProviders();
+ // Now that SettingsProvider is ready, reactivate SQLiteCompatibilityWalFlags
+ SQLiteCompatibilityWalFlags.reset();
traceEnd();
traceBeginAndSlog("StartVibratorService");
@@ -1088,14 +1093,17 @@
}
traceEnd();
- // Wifi Service must be started first for wifi-related services.
- traceBeginAndSlog("StartWifi");
- mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
- traceEnd();
- traceBeginAndSlog("StartWifiScanning");
- mSystemServiceManager.startService(
- "com.android.server.wifi.scanner.WifiScanningService");
- traceEnd();
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI)) {
+ // Wifi Service must be started first for wifi-related services.
+ traceBeginAndSlog("StartWifi");
+ mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+ traceEnd();
+ traceBeginAndSlog("StartWifiScanning");
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.scanner.WifiScanningService");
+ traceEnd();
+ }
if (!disableRtt) {
traceBeginAndSlog("StartWifiRtt");
diff --git a/services/net/OWNERS b/services/net/OWNERS
index 2d71c20..6f77e04 100644
--- a/services/net/OWNERS
+++ b/services/net/OWNERS
@@ -1,10 +1,7 @@
set noparent
-per-file Android.mk = build.master@android.com
-per-file Android.mk = ek@google.com
-per-file Android.mk = hugobenichi@google.com
-per-file Android.mk = lorenzo@google.com
-
ek@google.com
hugobenichi@google.com
+jchalard@google.com
lorenzo@google.com
+satk@google.com
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 114929c..b5e4af7 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -40,15 +40,15 @@
# Include the testing libraries (JUnit4 + Robolectric libs).
LOCAL_STATIC_JAVA_LIBRARIES := \
+ platform-robolectric-android-all-stubs \
android-support-test \
mockito-robolectric-prebuilt \
platform-test-annotations \
truth-prebuilt
-# TODO(b/69254249): Migrate to Robolectric 3.4.2
LOCAL_JAVA_LIBRARIES := \
junit \
- platform-robolectric-3.1.1-prebuilt
+ platform-robolectric-3.5.1-prebuilt
LOCAL_INSTRUMENTATION_FOR := FrameworksServicesLib
LOCAL_MODULE := FrameworksServicesRoboTests
@@ -73,5 +73,4 @@
LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))backup/java
-# TODO(b/69254249): Migrate to Robolectric 3.4.2
-include prebuilts/misc/common/robolectric/3.1.1/run_robotests.mk
+include prebuilts/misc/common/robolectric/3.5.1/run_robotests.mk
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
index 3d5851f..13623e5 100644
--- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
@@ -18,8 +18,13 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.mock;
+import static junit.framework.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.robolectric.shadow.api.Shadow.extract;
+
+import android.annotation.Nullable;
import android.app.backup.BackupManager;
import android.content.ComponentName;
import android.content.Intent;
@@ -28,27 +33,31 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.IBinder;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
-import com.android.server.backup.testing.BackupTransportStub;
-import com.android.server.backup.testing.DefaultPackageManagerWithQueryIntentServicesAsUser;
+import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.server.backup.testing.ShadowBackupTransportStub;
import com.android.server.backup.testing.ShadowContextImplForBackup;
+import com.android.server.backup.testing.ShadowPackageManagerForBackup;
import com.android.server.backup.testing.TransportBoundListenerStub;
import com.android.server.backup.testing.TransportReadyCallbackStub;
import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportNotRegisteredException;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderClasses;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import org.robolectric.res.builder.RobolectricPackageManager;
import org.robolectric.shadows.ShadowLog;
import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.shadows.ShadowPackageManager;
import java.util.ArrayList;
import java.util.Arrays;
@@ -56,15 +65,17 @@
import java.util.HashSet;
import java.util.List;
-@RunWith(RobolectricTestRunner.class)
+@RunWith(FrameworkRobolectricTestRunner.class)
@Config(
manifest = Config.NONE,
- sdk = 23,
+ sdk = 26,
shadows = {
ShadowContextImplForBackup.class,
- ShadowBackupTransportStub.class
+ ShadowBackupTransportStub.class,
+ ShadowPackageManagerForBackup.class
}
)
+@SystemLoaderClasses({TransportManager.class})
@Presubmit
public class TransportManagerTest {
private static final String PACKAGE_NAME = "some.package.name";
@@ -73,7 +84,7 @@
private TransportInfo mTransport1;
private TransportInfo mTransport2;
- private RobolectricPackageManager mPackageManager;
+ private ShadowPackageManager mPackageManagerShadow;
private final TransportBoundListenerStub mTransportBoundListenerStub =
new TransportBoundListenerStub(true);
@@ -86,19 +97,34 @@
MockitoAnnotations.initMocks(this);
ShadowLog.stream = System.out;
- mPackageManager = new DefaultPackageManagerWithQueryIntentServicesAsUser(
- RuntimeEnvironment.getAppResourceLoader());
- RuntimeEnvironment.setRobolectricPackageManager(mPackageManager);
- mTransport1 = new TransportInfo(PACKAGE_NAME, "transport1.name");
- mTransport2 = new TransportInfo(PACKAGE_NAME, "transport2.name");
+ mPackageManagerShadow =
+ (ShadowPackageManagerForBackup)
+ extract(RuntimeEnvironment.application.getPackageManager());
+
+ mTransport1 = new TransportInfo(
+ PACKAGE_NAME,
+ "transport1.name",
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel");
+ mTransport2 = new TransportInfo(
+ PACKAGE_NAME,
+ "transport2.name",
+ new Intent(),
+ "currentDestinationString",
+ new Intent(),
+ "dataManagementLabel");
ShadowContextImplForBackup.sComponentBinderMap.put(mTransport1.componentName,
mTransport1.binder);
ShadowContextImplForBackup.sComponentBinderMap.put(mTransport2.componentName,
mTransport2.binder);
- ShadowBackupTransportStub.sBinderTransportMap.put(mTransport1.binder, mTransport1.stub);
- ShadowBackupTransportStub.sBinderTransportMap.put(mTransport2.binder, mTransport2.stub);
+ ShadowBackupTransportStub.sBinderTransportMap.put(
+ mTransport1.binder, mTransport1.binderInterface);
+ ShadowBackupTransportStub.sBinderTransportMap.put(
+ mTransport2.binder, mTransport2.binderInterface);
}
@After
@@ -124,8 +150,10 @@
Arrays.asList(mTransport1.componentName, mTransport2.componentName));
assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
Arrays.asList(mTransport1.name, mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isTrue();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
+ .isTrue();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
+ .isTrue();
}
@Test
@@ -148,8 +176,10 @@
Collections.singleton(mTransport2.componentName));
assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
Collections.singleton(mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
+ .isFalse();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
+ .isTrue();
}
@Test
@@ -188,8 +218,10 @@
Collections.singleton(mTransport2.componentName));
assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
Collections.singleton(mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
+ .isFalse();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
+ .isTrue();
}
@Test
@@ -245,8 +277,10 @@
Arrays.asList(mTransport1.componentName, mTransport2.componentName));
assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
Arrays.asList(mTransport1.name, mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
+ .isFalse();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
+ .isTrue();
}
@Test
@@ -260,8 +294,10 @@
Arrays.asList(mTransport1.componentName, mTransport2.componentName));
assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
Arrays.asList(mTransport1.name, mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isFalse();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
+ .isFalse();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
+ .isFalse();
}
@Test
@@ -275,8 +311,10 @@
Arrays.asList(mTransport1.componentName, mTransport2.componentName));
assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
Arrays.asList(mTransport1.name, mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isFalse();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
+ .isFalse();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
+ .isFalse();
}
@Test
@@ -290,8 +328,10 @@
Arrays.asList(mTransport1.componentName, mTransport2.componentName));
assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
Arrays.asList(mTransport1.name, mTransport2.name));
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
- assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
+ .isFalse();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
+ .isTrue();
}
@Test
@@ -300,9 +340,9 @@
Arrays.asList(mTransport1, mTransport2), mTransport1.name);
assertThat(transportManager.getTransportBinder(mTransport1.name)).isEqualTo(
- mTransport1.stub);
+ mTransport1.binderInterface);
assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo(
- mTransport2.stub);
+ mTransport2.binderInterface);
}
@Test
@@ -321,7 +361,7 @@
assertThat(transportManager.getTransportBinder(mTransport1.name)).isNull();
assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo(
- mTransport2.stub);
+ mTransport2.binderInterface);
}
@Test
@@ -351,7 +391,8 @@
TransportManager transportManager = createTransportManagerAndSetUpTransports(
Arrays.asList(mTransport1, mTransport2), mTransport1.name);
- assertThat(transportManager.getCurrentTransportBinder()).isEqualTo(mTransport1.stub);
+ assertThat(transportManager.getCurrentTransportBinder())
+ .isEqualTo(mTransport1.binderInterface);
}
@Test
@@ -370,8 +411,10 @@
TransportManager transportManager = createTransportManagerAndSetUpTransports(
Arrays.asList(mTransport1, mTransport2), mTransport1.name);
- assertThat(transportManager.getTransportName(mTransport1.stub)).isEqualTo(mTransport1.name);
- assertThat(transportManager.getTransportName(mTransport2.stub)).isEqualTo(mTransport2.name);
+ assertThat(transportManager.getTransportName(mTransport1.binderInterface))
+ .isEqualTo(mTransport1.name);
+ assertThat(transportManager.getTransportName(mTransport2.binderInterface))
+ .isEqualTo(mTransport2.name);
}
@Test
@@ -380,8 +423,9 @@
createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2),
Collections.singletonList(mTransport1), mTransport1.name);
- assertThat(transportManager.getTransportName(mTransport1.stub)).isNull();
- assertThat(transportManager.getTransportName(mTransport2.stub)).isEqualTo(mTransport2.name);
+ assertThat(transportManager.getTransportName(mTransport1.binderInterface)).isNull();
+ assertThat(transportManager.getTransportName(mTransport2.binderInterface))
+ .isEqualTo(mTransport2.name);
}
@Test
@@ -494,7 +538,7 @@
TransportManager transportManager =
createTransportManagerAndSetUpTransports(
Arrays.asList(mTransport1, mTransport2), mTransport1.name);
- transportManager.describeTransport(
+ transportManager.updateTransportAttributes(
mTransport1.componentName, "newName", null, "destinationString", null, null);
TransportClient transportClient =
@@ -509,7 +553,7 @@
TransportManager transportManager =
createTransportManagerAndSetUpTransports(
Arrays.asList(mTransport1, mTransport2), mTransport1.name);
- transportManager.describeTransport(
+ transportManager.updateTransportAttributes(
mTransport1.componentName, "newName", null, "destinationString", null, null);
TransportClient transportClient =
@@ -524,7 +568,7 @@
TransportManager transportManager =
createTransportManagerAndSetUpTransports(
Arrays.asList(mTransport1, mTransport2), mTransport1.name);
- transportManager.describeTransport(
+ transportManager.updateTransportAttributes(
mTransport1.componentName, "newName", null, "destinationString", null, null);
String transportName = transportManager.getTransportName(mTransport1.componentName);
@@ -532,6 +576,60 @@
assertThat(transportName).isEqualTo("newName");
}
+ @Test
+ public void isTransportRegistered_returnsCorrectly() throws Exception {
+ TransportManager transportManager =
+ createTransportManagerAndSetUpTransports(
+ Collections.singletonList(mTransport1),
+ Collections.singletonList(mTransport2),
+ mTransport1.name);
+
+ assertThat(transportManager.isTransportRegistered(mTransport1.name)).isTrue();
+ assertThat(transportManager.isTransportRegistered(mTransport2.name)).isFalse();
+ }
+
+ @Test
+ public void getTransportAttributes_forRegisteredTransport_returnsCorrectValues()
+ throws Exception {
+ TransportManager transportManager =
+ createTransportManagerAndSetUpTransports(
+ Collections.singletonList(mTransport1),
+ mTransport1.name);
+
+ assertThat(transportManager.getTransportConfigurationIntent(mTransport1.name))
+ .isEqualTo(mTransport1.binderInterface.configurationIntent());
+ assertThat(transportManager.getTransportDataManagementIntent(mTransport1.name))
+ .isEqualTo(mTransport1.binderInterface.dataManagementIntent());
+ assertThat(transportManager.getTransportDataManagementLabel(mTransport1.name))
+ .isEqualTo(mTransport1.binderInterface.dataManagementLabel());
+ assertThat(transportManager.getTransportDirName(mTransport1.name))
+ .isEqualTo(mTransport1.binderInterface.transportDirName());
+ }
+
+ @Test
+ public void getTransportAttributes_forUnregisteredTransport_throws()
+ throws Exception {
+ TransportManager transportManager =
+ createTransportManagerAndSetUpTransports(
+ Collections.singletonList(mTransport1),
+ Collections.singletonList(mTransport2),
+ mTransport1.name);
+
+ expectThrows(
+ TransportNotRegisteredException.class,
+ () -> transportManager.getTransportConfigurationIntent(mTransport2.name));
+ expectThrows(
+ TransportNotRegisteredException.class,
+ () -> transportManager.getTransportDataManagementIntent(
+ mTransport2.name));
+ expectThrows(
+ TransportNotRegisteredException.class,
+ () -> transportManager.getTransportDataManagementLabel(mTransport2.name));
+ expectThrows(
+ TransportNotRegisteredException.class,
+ () -> transportManager.getTransportDirName(mTransport2.name));
+ }
+
private void setUpPackageWithTransports(String packageName, List<TransportInfo> transports,
int flags) throws Exception {
PackageInfo packageInfo = new PackageInfo();
@@ -539,7 +637,7 @@
packageInfo.applicationInfo = new ApplicationInfo();
packageInfo.applicationInfo.privateFlags = flags;
- mPackageManager.addPackage(packageInfo);
+ mPackageManagerShadow.addPackage(packageInfo);
List<ResolveInfo> transportsInfo = new ArrayList<>();
for (TransportInfo transport : transports) {
@@ -553,7 +651,7 @@
Intent intent = new Intent(TransportManager.SERVICE_ACTION_TRANSPORT_HOST);
intent.setPackage(packageName);
- mPackageManager.addResolveInfoForIntent(intent, transportsInfo);
+ mPackageManagerShadow.addResolveInfoForIntent(intent, transportsInfo);
}
private TransportManager createTransportManagerAndSetUpTransports(
@@ -599,10 +697,12 @@
assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
availableTransportsNames);
for (TransportInfo transport : availableTransports) {
- assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.stub)).isTrue();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.binderInterface))
+ .isTrue();
}
for (TransportInfo transport : unavailableTransports) {
- assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.stub)).isFalse();
+ assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.binderInterface))
+ .isFalse();
}
mTransportBoundListenerStub.resetState();
@@ -610,19 +710,46 @@
return transportManager;
}
+ private static <T extends Throwable> void expectThrows(
+ Class<T> throwableClass, ThrowingRunnable runnable) {
+ try {
+ runnable.runOrThrow();
+ fail("Expected to throw " + throwableClass.getSimpleName());
+ } catch (Throwable t) {
+ assertThat(t).isInstanceOf(throwableClass);
+ }
+ }
+
private static class TransportInfo {
public final String packageName;
public final String name;
public final ComponentName componentName;
- public final BackupTransportStub stub;
+ public final IBackupTransport binderInterface;
public final IBinder binder;
- TransportInfo(String packageName, String name) {
+ TransportInfo(
+ String packageName,
+ String name,
+ @Nullable Intent configurationIntent,
+ String currentDestinationString,
+ @Nullable Intent dataManagementIntent,
+ String dataManagementLabel) {
this.packageName = packageName;
this.name = name;
this.componentName = new ComponentName(packageName, name);
- this.stub = new BackupTransportStub(name);
this.binder = mock(IBinder.class);
+ IBackupTransport transport = mock(IBackupTransport.class);
+ try {
+ when(transport.name()).thenReturn(name);
+ when(transport.configurationIntent()).thenReturn(configurationIntent);
+ when(transport.currentDestinationString()).thenReturn(currentDestinationString);
+ when(transport.dataManagementIntent()).thenReturn(dataManagementIntent);
+ when(transport.dataManagementLabel()).thenReturn(dataManagementLabel);
+ } catch (RemoteException e) {
+ // Only here to mock methods that throw RemoteException
+ }
+ this.binderInterface = transport;
}
}
+
}
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java b/services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java
deleted file mode 100644
index ec09f90..0000000
--- a/services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java
+++ /dev/null
@@ -1,179 +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.backup.testing;
-
-import android.app.backup.RestoreDescription;
-import android.app.backup.RestoreSet;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-
-import com.android.internal.backup.IBackupTransport;
-
-/**
- * Stub backup transport, doing nothing and returning default values.
- */
-public class BackupTransportStub implements IBackupTransport {
-
- private final String mName;
-
- public BackupTransportStub(String name) {
- mName = name;
- }
-
- @Override
- public IBinder asBinder() {
- return null;
- }
-
- @Override
- public String name() throws RemoteException {
- return mName;
- }
-
- @Override
- public Intent configurationIntent() throws RemoteException {
- return null;
- }
-
- @Override
- public String currentDestinationString() throws RemoteException {
- return null;
- }
-
- @Override
- public Intent dataManagementIntent() throws RemoteException {
- return null;
- }
-
- @Override
- public String dataManagementLabel() throws RemoteException {
- return null;
- }
-
- @Override
- public String transportDirName() throws RemoteException {
- return null;
- }
-
- @Override
- public long requestBackupTime() throws RemoteException {
- return 0;
- }
-
- @Override
- public int initializeDevice() throws RemoteException {
- return 0;
- }
-
- @Override
- public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags)
- throws RemoteException {
- return 0;
- }
-
- @Override
- public int clearBackupData(PackageInfo packageInfo) throws RemoteException {
- return 0;
- }
-
- @Override
- public int finishBackup() throws RemoteException {
- return 0;
- }
-
- @Override
- public RestoreSet[] getAvailableRestoreSets() throws RemoteException {
- return new RestoreSet[0];
- }
-
- @Override
- public long getCurrentRestoreSet() throws RemoteException {
- return 0;
- }
-
- @Override
- public int startRestore(long token, PackageInfo[] packages) throws RemoteException {
- return 0;
- }
-
- @Override
- public RestoreDescription nextRestorePackage() throws RemoteException {
- return null;
- }
-
- @Override
- public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException {
- return 0;
- }
-
- @Override
- public void finishRestore() throws RemoteException {
-
- }
-
- @Override
- public long requestFullBackupTime() throws RemoteException {
- return 0;
- }
-
- @Override
- public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket,
- int flags)
- throws RemoteException {
- return 0;
- }
-
- @Override
- public int checkFullBackupSize(long size) throws RemoteException {
- return 0;
- }
-
- @Override
- public int sendBackupData(int numBytes) throws RemoteException {
- return 0;
- }
-
- @Override
- public void cancelFullBackup() throws RemoteException {
-
- }
-
- @Override
- public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup)
- throws RemoteException {
- return false;
- }
-
- @Override
- public long getBackupQuota(String packageName, boolean isFullBackup)
- throws RemoteException {
- return 0;
- }
-
- @Override
- public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) throws RemoteException {
- return 0;
- }
-
- @Override
- public int abortFullRestore() throws RemoteException {
- return 0;
- }
-}
diff --git a/services/robotests/src/com/android/server/backup/testing/DefaultPackageManagerWithQueryIntentServicesAsUser.java b/services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java
similarity index 69%
rename from services/robotests/src/com/android/server/backup/testing/DefaultPackageManagerWithQueryIntentServicesAsUser.java
rename to services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java
index 5a0967b..b64b59d 100644
--- a/services/robotests/src/com/android/server/backup/testing/DefaultPackageManagerWithQueryIntentServicesAsUser.java
+++ b/services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java
@@ -16,28 +16,22 @@
package com.android.server.backup.testing;
+import android.app.ApplicationPackageManager;
import android.content.Intent;
import android.content.pm.ResolveInfo;
-import org.robolectric.res.ResourceLoader;
-import org.robolectric.res.builder.DefaultPackageManager;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowApplicationPackageManager;
import java.util.List;
/**
* Implementation of PackageManager for Robolectric which handles queryIntentServicesAsUser().
*/
-public class DefaultPackageManagerWithQueryIntentServicesAsUser extends
- DefaultPackageManager {
-
- /* package */
- public DefaultPackageManagerWithQueryIntentServicesAsUser(
- ResourceLoader appResourceLoader) {
- super(appResourceLoader);
- }
-
+@Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true)
+public class ShadowPackageManagerForBackup extends ShadowApplicationPackageManager {
@Override
public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
- return super.queryIntentServices(intent, flags);
+ return queryIntentServices(intent, flags);
}
}
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
index 4a3277f..4462d2a 100644
--- a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
+++ b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
@@ -17,6 +17,7 @@
package com.android.server.backup.transport;
import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -37,31 +38,34 @@
import android.platform.test.annotations.Presubmit;
import com.android.internal.backup.IBackupTransport;
+import com.android.server.backup.TransportManager;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderClasses;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLooper;
-@RunWith(RobolectricTestRunner.class)
-@Config(manifest = Config.NONE, sdk = 23)
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderClasses({TransportManager.class, TransportClient.class})
@Presubmit
public class TransportClientTest {
private static final String PACKAGE_NAME = "some.package.name";
- private static final ComponentName TRANSPORT_COMPONENT =
- new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".transport.Transport");
- private static final String TRANSPORT_DIR_NAME = TRANSPORT_COMPONENT.toString();
@Mock private Context mContext;
@Mock private TransportConnectionListener mTransportConnectionListener;
@Mock private TransportConnectionListener mTransportConnectionListener2;
@Mock private IBackupTransport.Stub mIBackupTransport;
private TransportClient mTransportClient;
+ private ComponentName mTransportComponent;
+ private String mTransportDirName;
private Intent mBindIntent;
private ShadowLooper mShadowLooper;
@@ -71,13 +75,16 @@
Looper mainLooper = Looper.getMainLooper();
mShadowLooper = shadowOf(mainLooper);
- mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(TRANSPORT_COMPONENT);
+ mTransportComponent =
+ new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".transport.Transport");
+ mTransportDirName = mTransportComponent.toString();
+ mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent);
mTransportClient =
new TransportClient(
mContext,
mBindIntent,
- TRANSPORT_COMPONENT,
- TRANSPORT_DIR_NAME,
+ mTransportComponent,
+ mTransportDirName,
"1",
new Handler(mainLooper));
@@ -91,12 +98,12 @@
@Test
public void testGetTransportDirName_returnsTransportDirName() {
- assertThat(mTransportClient.getTransportDirName()).isEqualTo(TRANSPORT_DIR_NAME);
+ assertThat(mTransportClient.getTransportDirName()).isEqualTo(mTransportDirName);
}
@Test
public void testGetTransportComponent_returnsTransportComponent() {
- assertThat(mTransportClient.getTransportComponent()).isEqualTo(TRANSPORT_COMPONENT);
+ assertThat(mTransportClient.getTransportComponent()).isEqualTo(mTransportComponent);
}
@Test
@@ -117,7 +124,7 @@
// Simulate framework connecting
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
- connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
mShadowLooper.runToEndOfTasks();
verify(mTransportConnectionListener)
@@ -132,7 +139,7 @@
mTransportClient.connectAsync(mTransportConnectionListener2, "caller2");
- connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
mShadowLooper.runToEndOfTasks();
verify(mTransportConnectionListener)
@@ -145,7 +152,7 @@
public void testConnectAsync_whenAlreadyConnected_callsListener() throws Exception {
mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
- connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
mTransportClient.connectAsync(mTransportConnectionListener2, "caller2");
@@ -190,8 +197,8 @@
throws Exception {
mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
- connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport);
- connection.onServiceDisconnected(TRANSPORT_COMPONENT);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
+ connection.onServiceDisconnected(mTransportComponent);
mTransportClient.connectAsync(mTransportConnectionListener2, "caller1");
@@ -204,9 +211,9 @@
throws Exception {
mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
- connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport);
- connection.onServiceDisconnected(TRANSPORT_COMPONENT);
- connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
+ connection.onServiceDisconnected(mTransportComponent);
+ connection.onServiceConnected(mTransportComponent, mIBackupTransport);
mTransportClient.connectAsync(mTransportConnectionListener2, "caller1");
@@ -221,7 +228,7 @@
mTransportClient.connectAsync(mTransportListener, "caller");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
- connection.onBindingDied(TRANSPORT_COMPONENT);
+ connection.onBindingDied(mTransportComponent);
mShadowLooper.runToEndOfTasks();
verify(mTransportListener).onTransportBound(isNull(), eq(mTransportClient));
@@ -235,7 +242,7 @@
mTransportClient.connectAsync(mTransportListener2, "caller2");
- connection.onBindingDied(TRANSPORT_COMPONENT);
+ connection.onBindingDied(mTransportComponent);
mShadowLooper.runToEndOfTasks();
verify(mTransportListener).onTransportBound(isNull(), eq(mTransportClient));
diff --git a/services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java b/services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java
new file mode 100644
index 0000000..78ac4ed
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java
@@ -0,0 +1,162 @@
+/*
+ * 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.testing;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.internal.SandboxFactory;
+import org.robolectric.internal.SdkEnvironment;
+import org.robolectric.internal.bytecode.InstrumentationConfiguration;
+import org.robolectric.internal.bytecode.SandboxClassLoader;
+import org.robolectric.util.Util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+
+/**
+ * HACK
+ * Robolectric loads up Android environment from prebuilt android jars before running a method.
+ * These jars are versioned according to the SDK level configured for the method (or class). The
+ * jars represent a snapshot of the Android APIs in that SDK level. For Robolectric tests that are
+ * testing Android components themselves we don't want certain classes (usually the
+ * class-under-test) to be loaded from the prebuilt jar, we want it instead to be loaded from the
+ * dependencies of our test target, i.e. the system class loader. That way we can write tests
+ * against the actual classes that are in the tree, not a past version of them. Ideally we would
+ * have a locally built jar referenced by Robolectric, but until that happens one can use this
+ * class.
+ * This class reads the {@link SystemLoaderClasses} annotation on test classes and for each class
+ * in that annotation value it will bypass the android jar and load it from the system class loader.
+ * Allowing the test to test the actual class in the tree.
+ *
+ * Implementation note: One could think about overriding
+ * {@link RobolectricTestRunner#createClassLoaderConfig(FrameworkMethod)} method and putting the
+ * classes in the annotation in the {@link InstrumentationConfiguration} list of classes not to
+ * acquire. Unfortunately, this will not work because we will not be instrumenting the class.
+ * Instead, we have to load the class bytes from the system class loader but still instrument it, we
+ * do this by overriding {@link SandboxClassLoader#getByteCode(String)} and loading the class bytes
+ * from the system class loader if it in the {@link SystemLoaderClasses} annotation. This way the
+ * {@link SandboxClassLoader} still instruments the class, but it's not loaded from the android jar.
+ * Finally, we inject the custom class loader in place of the default one.
+ *
+ * TODO: Remove this when we are using locally built android jars in the method's environment.
+ */
+public class FrameworkRobolectricTestRunner extends RobolectricTestRunner {
+ private final SandboxFactory mSandboxFactory;
+
+ public FrameworkRobolectricTestRunner(Class<?> testClass) throws InitializationError {
+ super(testClass);
+ SystemLoaderClasses annotation = testClass.getAnnotation(SystemLoaderClasses.class);
+ Class<?>[] systemLoaderClasses =
+ (annotation != null) ? annotation.value() : new Class<?>[0];
+ Set<String> systemLoaderClassNames = classesToClassNames(systemLoaderClasses);
+ mSandboxFactory = new FrameworkSandboxFactory(systemLoaderClassNames);
+ }
+
+ @Nonnull
+ @Override
+ protected SdkEnvironment getSandbox(FrameworkMethod method) {
+ // HACK: Calling super just to get SdkConfig via sandbox.getSdkConfig(), because
+ // RobolectricFrameworkMethod, the runtime class of method, is package-protected
+ SdkEnvironment sandbox = super.getSandbox(method);
+ return mSandboxFactory.getSdkEnvironment(
+ createClassLoaderConfig(method),
+ getJarResolver(),
+ sandbox.getSdkConfig());
+ }
+
+ private static class FrameworkClassLoader extends SandboxClassLoader {
+ private final Set<String> mSystemLoaderClasses;
+
+ private FrameworkClassLoader(
+ Set<String> systemLoaderClasses,
+ ClassLoader systemClassLoader,
+ InstrumentationConfiguration instrumentationConfig,
+ URL... urls) {
+ super(systemClassLoader, instrumentationConfig, urls);
+ mSystemLoaderClasses = systemLoaderClasses;
+ }
+
+ @Override
+ protected byte[] getByteCode(String className) throws ClassNotFoundException {
+ String classFileName = className.replace('.', '/') + ".class";
+ if (shouldLoadFromSystemLoader(className)) {
+ try (InputStream classByteStream = getResourceAsStream(classFileName)) {
+ if (classByteStream == null) {
+ throw new ClassNotFoundException(className);
+ }
+ return Util.readBytes(classByteStream);
+ } catch (IOException e) {
+ throw new ClassNotFoundException(
+ "Couldn't load " + className + " from system class loader", e);
+ }
+ }
+ return super.getByteCode(className);
+ }
+
+ /**
+ * Classes like com.package.ClassName$InnerClass should also be loaded from the system class
+ * loader, so we test if the classes in the annotation are prefixes of the class to load.
+ */
+ private boolean shouldLoadFromSystemLoader(String className) {
+ for (String classNamePrefix : mSystemLoaderClasses) {
+ if (className.startsWith(classNamePrefix)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ private static class FrameworkSandboxFactory extends SandboxFactory {
+ private final Set<String> mSystemLoaderClasses;
+
+ private FrameworkSandboxFactory(Set<String> systemLoaderClasses) {
+ mSystemLoaderClasses = systemLoaderClasses;
+ }
+
+ @Nonnull
+ @Override
+ public ClassLoader createClassLoader(
+ InstrumentationConfiguration instrumentationConfig, URL... urls) {
+ return new FrameworkClassLoader(
+ mSystemLoaderClasses,
+ ClassLoader.getSystemClassLoader(),
+ instrumentationConfig,
+ urls);
+ }
+ }
+
+ private static Set<String> classesToClassNames(Class<?>[] classes) {
+ ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+ for (Class<?> classObject : classes) {
+ builder.add(classObject.getName());
+ }
+ return builder.build();
+ }
+}
diff --git a/services/robotests/src/com/android/server/testing/SystemLoaderClasses.java b/services/robotests/src/com/android/server/testing/SystemLoaderClasses.java
new file mode 100644
index 0000000..646a413
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/SystemLoaderClasses.java
@@ -0,0 +1,35 @@
+/*
+ * 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.testing;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to be used in test classes that run with {@link FrameworkRobolectricTestRunner}.
+ * This will make the classes specified be loaded from the system class loader, NOT from the
+ * Robolectric android jar.
+ *
+ * @see FrameworkRobolectricTestRunner
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SystemLoaderClasses {
+ Class<?>[] value() default {};
+}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
index 82ff0d8..6874624 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
@@ -52,10 +52,10 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import org.mockito.compat.ArgumentMatcher;
import java.util.ArrayList;
import java.util.List;
@@ -82,13 +82,9 @@
MockitoAnnotations.initMocks(this);
mAvailableServices = new ArrayList<>();
when(mMockContext.getPackageManager()).thenReturn(mMockPm);
- when(mMockPm.queryIntentServices(Mockito.argThat(new ArgumentMatcher<Intent>() {
- @Override
- public boolean matchesObject(Object object) {
- Intent intent = (Intent) object;
- return NetworkScoreManager.ACTION_RECOMMEND_NETWORKS.equals(intent.getAction());
- }
- }), eq(PackageManager.GET_META_DATA))).thenReturn(mAvailableServices);
+ when(mMockPm.queryIntentServices(Mockito.argThat(
+ intent -> NetworkScoreManager.ACTION_RECOMMEND_NETWORKS.equals(intent.getAction())),
+ eq(PackageManager.GET_META_DATA))).thenReturn(mAvailableServices);
when(mMockContext.getResources()).thenReturn(mResources);
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
@@ -664,15 +660,10 @@
final int flags = PackageManager.GET_META_DATA;
when(mMockPm.resolveService(
- Mockito.argThat(new ArgumentMatcher<Intent>() {
- @Override
- public boolean matchesObject(Object object) {
- Intent intent = (Intent) object;
- return NetworkScoreManager.ACTION_RECOMMEND_NETWORKS
- .equals(intent.getAction())
- && compName.getPackageName().equals(intent.getPackage());
- }
- }), Mockito.eq(flags))).thenReturn(serviceInfo);
+ Mockito.argThat(intent -> NetworkScoreManager.ACTION_RECOMMEND_NETWORKS
+ .equals(intent.getAction())
+ && compName.getPackageName().equals(intent.getPackage())),
+ Mockito.eq(flags))).thenReturn(serviceInfo);
mAvailableServices.add(serviceInfo);
}
@@ -685,13 +676,9 @@
final int flags = 0;
when(mMockPm.resolveActivity(
- Mockito.argThat(new ArgumentMatcher<Intent>() {
- @Override
- public boolean matchesObject(Object object) {
- Intent intent = (Intent) object;
- return NetworkScoreManager.ACTION_CUSTOM_ENABLE.equals(intent.getAction())
- && useOpenWifiComp.getPackageName().equals(intent.getPackage());
- }
- }), Mockito.eq(flags))).thenReturn(resolveActivityInfo);
+ Mockito.argThat(intent ->
+ NetworkScoreManager.ACTION_CUSTOM_ENABLE.equals(intent.getAction())
+ && useOpenWifiComp.getPackageName().equals(intent.getPackage())),
+ Mockito.eq(flags))).thenReturn(resolveActivityInfo);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index c14f74c..0462b14 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -62,7 +62,7 @@
@Mock AccessibilityServiceInfo mMockServiceInfo;
@Mock ResolveInfo mMockResolveInfo;
@Mock AccessibilityManagerService.SecurityPolicy mMockSecurityPolicy;
- @Mock AccessibilityClientConnection.SystemSupport mMockSystemSupport;
+ @Mock AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
@Mock WindowManagerInternal mMockWindowManagerInternal;
@Mock GlobalActionPerformer mMockGlobalActionPerformer;
@Mock KeyEventDispatcher mMockKeyEventDispatcher;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index d286b3f..6cacb1f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -65,7 +65,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.compat.ArgumentMatcher;
/**
* Tests for MotionEventInjector
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index dbebd01..8853db2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -59,7 +59,7 @@
@Mock AccessibilityServiceInfo mMockServiceInfo;
@Mock ResolveInfo mMockResolveInfo;
@Mock AccessibilityManagerService.SecurityPolicy mMockSecurityPolicy;
- @Mock AccessibilityClientConnection.SystemSupport mMockSystemSupport;
+ @Mock AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
@Mock WindowManagerInternal mMockWindowManagerInternal;
@Mock GlobalActionPerformer mMockGlobalActionPerformer;
@Mock IBinder mMockOwner;
@@ -153,13 +153,14 @@
}
@Test
- public void uiAutomationBinderDiesBeforeConnecting_shouldNotCrash() throws Exception {
+ public void uiAutomationBinderDiesBeforeConnecting_notifiesSystem() throws Exception {
register(0);
ArgumentCaptor<IBinder.DeathRecipient> captor = ArgumentCaptor.forClass(
IBinder.DeathRecipient.class);
verify(mMockOwner).linkToDeath(captor.capture(), anyInt());
captor.getValue().binderDied();
mMessageCapturingHandler.sendAllMessages();
+ verify(mMockSystemSupport).onClientChange(false);
}
private void register(int flags) {
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 0d03863..f384044 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -22,6 +22,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -36,6 +37,7 @@
import static java.lang.Integer.MAX_VALUE;
+import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.ComponentName;
@@ -181,18 +183,6 @@
assertTrue(mCallbacksRecorder.removed.contains(mTasks.get(1)));
mCallbacksRecorder.clear();
- // Add a task which will trigger the trimming of another
- TaskRecord documentTask1 = createDocumentTask(".DocumentTask1");
- documentTask1.maxRecents = 1;
- TaskRecord documentTask2 = createDocumentTask(".DocumentTask1");
- mRecentTasks.add(documentTask1);
- mRecentTasks.add(documentTask2);
- assertTrue(mCallbacksRecorder.added.contains(documentTask1));
- assertTrue(mCallbacksRecorder.added.contains(documentTask2));
- assertTrue(mCallbacksRecorder.trimmed.contains(documentTask1));
- assertTrue(mCallbacksRecorder.removed.contains(documentTask1));
- mCallbacksRecorder.clear();
-
// Remove the callback, ensure we don't get any calls
mRecentTasks.unregisterCallback(mCallbacksRecorder);
mRecentTasks.add(mTasks.get(0));
@@ -203,6 +193,65 @@
}
@Test
+ public void testAddTasksNoMultiple_expectNoTrim() throws Exception {
+ // Add same non-multiple-task document tasks will remove the task (to re-add it) but not
+ // trim it
+ TaskRecord documentTask1 = createDocumentTask(".DocumentTask1");
+ TaskRecord documentTask2 = createDocumentTask(".DocumentTask1");
+ mRecentTasks.add(documentTask1);
+ mRecentTasks.add(documentTask2);
+ assertTrue(mCallbacksRecorder.added.contains(documentTask1));
+ assertTrue(mCallbacksRecorder.added.contains(documentTask2));
+ assertTrue(mCallbacksRecorder.trimmed.isEmpty());
+ assertTrue(mCallbacksRecorder.removed.contains(documentTask1));
+ }
+
+ @Test
+ public void testAddTasksMaxTaskRecents_expectNoTrim() throws Exception {
+ // Add a task hitting max-recents for that app will remove the task (to add the next one)
+ // but not trim it
+ TaskRecord documentTask1 = createDocumentTask(".DocumentTask1");
+ TaskRecord documentTask2 = createDocumentTask(".DocumentTask1");
+ documentTask1.maxRecents = 1;
+ documentTask2.maxRecents = 1;
+ mRecentTasks.add(documentTask1);
+ mRecentTasks.add(documentTask2);
+ assertTrue(mCallbacksRecorder.added.contains(documentTask1));
+ assertTrue(mCallbacksRecorder.added.contains(documentTask2));
+ assertTrue(mCallbacksRecorder.trimmed.isEmpty());
+ assertTrue(mCallbacksRecorder.removed.contains(documentTask1));
+ }
+
+ @Test
+ public void testAddTasksSameTask_expectNoTrim() throws Exception {
+ // Add a task that is already in the task list does not trigger any callbacks, it just
+ // moves in the list
+ TaskRecord documentTask1 = createDocumentTask(".DocumentTask1");
+ mRecentTasks.add(documentTask1);
+ mRecentTasks.add(documentTask1);
+ assertTrue(mCallbacksRecorder.added.size() == 1);
+ assertTrue(mCallbacksRecorder.added.contains(documentTask1));
+ assertTrue(mCallbacksRecorder.trimmed.isEmpty());
+ assertTrue(mCallbacksRecorder.removed.isEmpty());
+ }
+
+ @Test
+ public void testAddTasksMultipleTasks_expectNoTrim() throws Exception {
+ // Add same multiple-task document tasks does not trim the first tasks
+ TaskRecord documentTask1 = createDocumentTask(".DocumentTask1",
+ FLAG_ACTIVITY_MULTIPLE_TASK);
+ TaskRecord documentTask2 = createDocumentTask(".DocumentTask1",
+ FLAG_ACTIVITY_MULTIPLE_TASK);
+ mRecentTasks.add(documentTask1);
+ mRecentTasks.add(documentTask2);
+ assertTrue(mCallbacksRecorder.added.size() == 2);
+ assertTrue(mCallbacksRecorder.added.contains(documentTask1));
+ assertTrue(mCallbacksRecorder.added.contains(documentTask2));
+ assertTrue(mCallbacksRecorder.trimmed.isEmpty());
+ assertTrue(mCallbacksRecorder.removed.isEmpty());
+ }
+
+ @Test
public void testUsersTasks() throws Exception {
mRecentTasks.setOnlyTestVisibleRange();
@@ -549,10 +598,15 @@
}
private TaskRecord createDocumentTask(String className) {
+ return createDocumentTask(className, 0);
+ }
+
+ private TaskRecord createDocumentTask(String className, int flags) {
TaskRecord task = createTaskBuilder(className)
- .setFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
+ .setFlags(FLAG_ACTIVITY_NEW_DOCUMENT | flags)
.build();
task.affinity = null;
+ task.maxRecents = ActivityManager.getMaxAppRecentsLimitStatic();
return task;
}
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
index 5520bd7..c91e22f 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
@@ -17,33 +17,46 @@
package com.android.server.am;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.res.XmlResourceParser;
+import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.service.voice.IVoiceInteractionSession;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
+import android.util.Xml;
+import com.android.frameworks.servicestests.R;
import com.android.internal.app.IVoiceInteractor;
import com.android.server.am.TaskRecord.TaskRecordFactory;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.nio.file.Files;
import java.util.ArrayList;
+import java.util.Comparator;
/**
* Tests for exercising {@link TaskRecord}.
@@ -54,11 +67,33 @@
@MediumTest
@Presubmit
@RunWith(AndroidJUnit4.class)
-public class TaskRecordTests {
+public class TaskRecordTests extends ActivityTestsBase {
+
+ private static final String TASK_TAG = "task";
+
+ private ActivityManagerService mService;
@Before
public void setUp() throws Exception {
+ super.setUp();
TaskRecord.setTaskRecordFactory(null);
+ mService = createActivityManagerService();
+ }
+
+ @Test
+ public void testRestoreWindowedTask() throws Exception {
+ final TaskRecord expected = createTaskRecord(64);
+ expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100);
+
+ final File serializedFile = serializeToFile(expected);
+
+ try {
+ final TaskRecord actual = restoreFromFile(serializedFile);
+ assertEquals(expected.taskId, actual.taskId);
+ assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
+ } finally {
+ serializedFile.delete();
+ }
}
@Test
@@ -78,6 +113,38 @@
assertTrue(factory.mCreated);
}
+ private File serializeToFile(TaskRecord r) throws IOException, XmlPullParserException {
+ final File tmpFile = File.createTempFile(r.taskId + "_task_", "xml");
+
+ try (final OutputStream os = new FileOutputStream(tmpFile)) {
+ final XmlSerializer serializer = Xml.newSerializer();
+ serializer.setOutput(os, "UTF-8");
+ serializer.startDocument(null, true);
+ serializer.startTag(null, TASK_TAG);
+ r.saveToXml(serializer);
+ serializer.endTag(null, TASK_TAG);
+ serializer.endDocument();
+ }
+
+ return tmpFile;
+ }
+
+ private TaskRecord restoreFromFile(File file) throws IOException, XmlPullParserException {
+ try (final Reader reader = new BufferedReader(new FileReader(file))) {
+ final XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(reader);
+ assertEquals(XmlPullParser.START_TAG, parser.next());
+ assertEquals(TASK_TAG, parser.getName());
+ return TaskRecord.restoreFromXml(parser, mService.mStackSupervisor);
+ }
+ }
+
+ private TaskRecord createTaskRecord(int taskId) {
+ return new TaskRecord(mService, taskId, new Intent(), null, null, null, null, null, false,
+ false, false, 0, 10050, null, new ArrayList<>(), 0, false, null, 0, 0, 0, 0, 0,
+ null, 0, false, false, false, 0, 0);
+ }
+
private static class TestTaskRecordFactory extends TaskRecordFactory {
private boolean mCreated = false;
diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
index 362856c..f4c5442 100644
--- a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -117,7 +117,7 @@
"dataManagementLabel");
verify(mTransportManager)
- .describeTransport(
+ .updateTransportAttributes(
eq(TRANSPORT_COMPONENT),
eq(TRANSPORT_NAME),
eq(configurationIntent),
@@ -247,7 +247,7 @@
null);
verify(mTransportManager)
- .describeTransport(
+ .updateTransportAttributes(
eq(TRANSPORT_COMPONENT),
eq(TRANSPORT_NAME),
eq(configurationIntent),
@@ -274,7 +274,7 @@
"dataManagementLabel");
verify(mTransportManager)
- .describeTransport(
+ .updateTransportAttributes(
eq(TRANSPORT_COMPONENT),
eq(TRANSPORT_NAME),
eq(configurationIntent),
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 d168479..0650acb 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -102,6 +102,11 @@
this.context = injector.context;
}
+ @Override
+ public boolean isPasswordBlacklisted(int userId, String password) {
+ return false;
+ }
+
public void notifyChangeToContentObserver(Uri uri, int userHandle) {
ContentObserver co = mMockInjector.mContentObservers.get(new Pair<>(uri, userHandle));
@@ -205,6 +210,11 @@
}
@Override
+ PasswordBlacklist newPasswordBlacklist(File file) {
+ return services.passwordBlacklist;
+ }
+
+ @Override
boolean storageManagerIsFileBasedEncryptionEnabled() {
return services.storageManager.isFileBasedEncryptionEnabled();
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index ca918c6..4779474 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3765,6 +3765,36 @@
assertTrue(dpm.clearResetPasswordToken(admin1));
}
+ public void testSetPasswordBlacklistCannotBeCalledByNonAdmin() throws Exception {
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setPasswordBlacklist(admin1, null, null));
+ verifyZeroInteractions(getServices().passwordBlacklist);
+ }
+
+ public void testClearingPasswordBlacklistDoesNotCreateNewBlacklist() throws Exception {
+ setupProfileOwner();
+ dpm.setPasswordBlacklist(admin1, null, null);
+ verifyZeroInteractions(getServices().passwordBlacklist);
+ }
+
+ public void testSetPasswordBlacklistCreatesNewBlacklist() throws Exception {
+ final String name = "myblacklist";
+ final List<String> explicit = Arrays.asList("password", "letmein");
+ setupProfileOwner();
+ dpm.setPasswordBlacklist(admin1, name, explicit);
+ verify(getServices().passwordBlacklist).savePasswordBlacklist(name, explicit);
+ }
+
+ public void testSetPasswordBlacklistOnlyConvertsExplicitToLowerCase() throws Exception {
+ final List<String> mixedCase = Arrays.asList("password", "LETMEIN", "FooTBAll");
+ final List<String> lowerCase = Arrays.asList("password", "letmein", "football");
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ final String name = "Name of the Blacklist";
+ dpm.setPasswordBlacklist(admin1, name, mixedCase);
+ verify(getServices().passwordBlacklist).savePasswordBlacklist(name, lowerCase);
+ }
+
public void testIsActivePasswordSufficient() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
mContext.packageName = admin1.getPackageName();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 4ee5ba6..8cb0459 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -86,6 +86,7 @@
public final IBackupManager ibackupManager;
public final IAudioService iaudioService;
public final LockPatternUtils lockPatternUtils;
+ public final PasswordBlacklist passwordBlacklist;
public final StorageManagerForMock storageManager;
public final WifiManager wifiManager;
public final SettingsForMock settings;
@@ -120,6 +121,7 @@
ibackupManager = mock(IBackupManager.class);
iaudioService = mock(IAudioService.class);
lockPatternUtils = mock(LockPatternUtils.class);
+ passwordBlacklist = mock(PasswordBlacklist.class);
storageManager = mock(StorageManagerForMock.class);
wifiManager = mock(WifiManager.class);
settings = mock(SettingsForMock.class);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java
index a92d1db..c698312 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java
@@ -15,13 +15,131 @@
*/
package com.android.server.devicepolicy;
+import static com.android.server.devicepolicy.NetworkLoggingHandler.LOG_NETWORK_EVENT_MSG;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
import android.app.admin.ConnectEvent;
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DnsEvent;
+import android.app.admin.NetworkEvent;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Message;
import android.os.Parcel;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+import org.mockito.ArgumentCaptor;
+
+import java.util.List;
+
@SmallTest
public class NetworkEventTest extends DpmTestBase {
+ private static final int MAX_EVENTS_PER_BATCH = 1200;
+
+ private DpmMockContext mSpiedDpmMockContext;
+ private DevicePolicyManagerServiceTestable mDpmTestable;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mSpiedDpmMockContext = spy(mMockContext);
+ mSpiedDpmMockContext.callerPermissions.add(
+ android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+ doNothing().when(mSpiedDpmMockContext).sendBroadcastAsUser(any(Intent.class),
+ any(UserHandle.class));
+ LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+ mDpmTestable = new DevicePolicyManagerServiceTestable(getServices(), mSpiedDpmMockContext);
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
+ mDpmTestable.setActiveAdmin(admin1, true, DpmMockContext.CALLER_USER_HANDLE);
+ }
+
+ public void testNetworkEventId_monotonicallyIncreasing() throws Exception {
+ // GIVEN the handler has not processed any events.
+ long startingId = 0;
+
+ // WHEN the handler has processed the events.
+ List<NetworkEvent> events = fillHandlerWithFullBatchOfEvents(startingId);
+
+ // THEN the events are in a batch.
+ assertTrue("Batch not at the returned token.",
+ events != null && events.size() == MAX_EVENTS_PER_BATCH);
+ // THEN event ids are monotonically increasing.
+ long expectedId = startingId;
+ for (int i = 0; i < MAX_EVENTS_PER_BATCH; i++) {
+ assertEquals("At index " + i + ", the event has the wrong id.", expectedId,
+ events.get(i).getId());
+ expectedId++;
+ }
+ }
+
+ public void testNetworkEventId_wrapsAround() throws Exception {
+ // GIVEN the handler has almost processed Long.MAX_VALUE events.
+ int gap = 5;
+ long startingId = Long.MAX_VALUE - gap;
+
+ // WHEN the handler has processed the events.
+ List<NetworkEvent> events = fillHandlerWithFullBatchOfEvents(startingId);
+
+ // THEN the events are in a batch.
+ assertTrue("Batch not at the returned token.",
+ events != null && events.size() == MAX_EVENTS_PER_BATCH);
+ // THEN event ids are monotonically increasing.
+ long expectedId = startingId;
+ for (int i = 0; i < gap; i++) {
+ assertEquals("At index " + i + ", the event has the wrong id.", expectedId,
+ events.get(i).getId());
+ expectedId++;
+ }
+ // THEN event ids are reset when the id reaches the maximum possible value.
+ assertEquals("Event was not assigned the maximum id value.", Long.MAX_VALUE,
+ events.get(gap).getId());
+ assertEquals("Event id was not reset.", 0, events.get(gap + 1).getId());
+ // THEN event ids are monotonically increasing.
+ expectedId = 0;
+ for (int i = gap + 1; i < MAX_EVENTS_PER_BATCH; i++) {
+ assertEquals("At index " + i + ", the event has the wrong id.", expectedId,
+ events.get(i).getId());
+ expectedId++;
+ }
+ }
+
+ private List<NetworkEvent> fillHandlerWithFullBatchOfEvents(long startingId) throws Exception {
+ // GIVEN a handler with events
+ NetworkLoggingHandler handler = new NetworkLoggingHandler(new TestLooper().getLooper(),
+ mDpmTestable, startingId);
+ // GIVEN network events are sent to the handler.
+ for (int i = 0; i < MAX_EVENTS_PER_BATCH; i++) {
+ ConnectEvent event = new ConnectEvent("some_ip_address", 800, "com.google.foo",
+ SystemClock.currentThreadTimeMillis());
+ Message msg = new Message();
+ msg.what = LOG_NETWORK_EVENT_MSG;
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(NetworkLoggingHandler.NETWORK_EVENT_KEY, event);
+ msg.setData(bundle);
+ handler.handleMessage(msg);
+ }
+
+ // WHEN the handler processes the events.
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mSpiedDpmMockContext).sendBroadcastAsUser(intentCaptor.capture(),
+ any(UserHandle.class));
+ assertEquals(intentCaptor.getValue().getAction(),
+ DeviceAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE);
+ long token = intentCaptor.getValue().getExtras().getLong(
+ DeviceAdminReceiver.EXTRA_NETWORK_LOGS_TOKEN, 0);
+ return handler.retrieveFullLogBatch(token);
+ }
/**
* Test parceling and unparceling of a ConnectEvent.
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
new file mode 100644
index 0000000..4447fe9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java
@@ -0,0 +1,390 @@
+/*
+ * 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 com.android.server.devicepolicy;
+
+import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
+import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
+import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
+import android.test.AndroidTestCase;
+import android.test.mock.MockPackageManager;
+import android.view.inputmethod.InputMethodInfo;
+
+import com.android.internal.R;
+import com.android.internal.view.IInputMethodManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class OverlayPackagesProviderTest extends AndroidTestCase {
+ private static final String TEST_DPC_PACKAGE_NAME = "dpc.package.name";
+ private static final ComponentName TEST_MDM_COMPONENT_NAME = new ComponentName(
+ TEST_DPC_PACKAGE_NAME, "pc.package.name.DeviceAdmin");
+ private static final int TEST_USER_ID = 123;
+
+ private @Mock
+ Resources mResources;
+ private @Mock
+ IInputMethodManager mIInputMethodManager;
+ private @Mock
+ Context mTestContext;
+ private Resources mRealResources;
+
+ private FakePackageManager mPackageManager;
+ private String[] mSystemAppsWithLauncher;
+ private OverlayPackagesProvider mHelper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mPackageManager = new FakePackageManager();
+ when(mTestContext.getResources()).thenReturn(mResources);
+ when(mTestContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mTestContext.getFilesDir()).thenReturn(
+ InstrumentationRegistry.getTargetContext().getCacheDir());
+
+ setSystemInputMethods();
+ setRequiredAppsManagedDevice();
+ setVendorRequiredAppsManagedDevice();
+ setDisallowedAppsManagedDevice();
+ setVendorDisallowedAppsManagedDevice();
+ setRequiredAppsManagedProfile();
+ setVendorRequiredAppsManagedProfile();
+ setDisallowedAppsManagedProfile();
+ setVendorDisallowedAppsManagedProfile();
+ setRequiredAppsManagedUser();
+ setVendorRequiredAppsManagedUser();
+ setDisallowedAppsManagedUser();
+ setVendorDisallowedAppsManagedUser();
+
+ mRealResources = InstrumentationRegistry.getTargetContext().getResources();
+ mHelper = new OverlayPackagesProvider(mTestContext, mIInputMethodManager);
+ }
+
+ @Test
+ public void testAppsWithLauncherAreNonRequiredByDefault() {
+ setSystemAppsWithLauncher("app.a", "app.b");
+
+ verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_DEVICE, "app.a", "app.b");
+ }
+
+ @Test
+ public void testDeviceOwnerRequiredApps() {
+ setSystemAppsWithLauncher("app.a", "app.b", "app.c");
+ setRequiredAppsManagedDevice("app.a");
+ setVendorRequiredAppsManagedDevice("app.b");
+
+ verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_DEVICE, "app.c");
+ }
+
+ @Test
+ public void testProfileOwnerRequiredApps() {
+ setSystemAppsWithLauncher("app.a", "app.b", "app.c");
+ setRequiredAppsManagedProfile("app.a");
+ setVendorRequiredAppsManagedProfile("app.b");
+
+ verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_PROFILE, "app.c");
+ }
+
+ @Test
+ public void testManagedUserRequiredApps() {
+ setSystemAppsWithLauncher("app.a", "app.b", "app.c");
+ setRequiredAppsManagedUser("app.a");
+ setVendorRequiredAppsManagedUser("app.b");
+
+ verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_USER, "app.c");
+ }
+
+ @Test
+ public void testDpcIsRequired() {
+ setSystemAppsWithLauncher("app.a", TEST_DPC_PACKAGE_NAME);
+
+ verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_DEVICE, "app.a");
+ }
+
+ @Test
+ public void testDisallowedAppsAreNonRequiredEvenIfNoLauncher() {
+ setSystemAppsWithLauncher();
+ setDisallowedAppsManagedDevice("app.a");
+ setVendorDisallowedAppsManagedDevice("app.b");
+
+ verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_DEVICE, "app.a", "app.b");
+ }
+
+ @Test
+ public void testDeviceOwnerImesAreRequired() {
+ setSystemAppsWithLauncher("app.a", "app.b");
+ setSystemInputMethods("app.a");
+
+ verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_DEVICE, "app.b");
+ }
+
+ @Test
+ public void testProfileOwnerImesAreNonRequired() {
+ setSystemAppsWithLauncher("app.a", "app.b");
+ setSystemInputMethods("app.a");
+
+ verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_PROFILE, "app.a", "app.b");
+ }
+
+ @Test
+ public void testManagedUserImesAreRequired() {
+ setSystemAppsWithLauncher("app.a", "app.b");
+ setSystemInputMethods("app.a");
+
+ verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_USER, "app.b");
+ }
+
+ @Test
+ public void testDisallowedAppsAreNonInstalled() {
+ setSystemAppsWithLauncher("app.a");
+ setDisallowedAppsManagedDevice("app.c");
+
+ verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_DEVICE, "app.a", "app.c");
+ }
+
+ /**
+ * If an app is listed as both required and disallowed, it should be only in the disallowed
+ * list. Therefore, it should be present in the non-required list.
+ */
+ @Test
+ public void testAllowedAndDisallowedAtTheSameTimeManagedDevice() {
+ setDisallowedAppsManagedDevice(TEST_DPC_PACKAGE_NAME);
+ setRequiredAppsManagedDevice(TEST_DPC_PACKAGE_NAME);
+
+ verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_DEVICE, TEST_DPC_PACKAGE_NAME);
+ }
+
+ /**
+ * @see {@link #testAllowedAndDisallowedAtTheSameTimeManagedDevice}
+ */
+ @Test
+ public void testAllowedAndDisallowedAtTheSameTimeManagedUser() {
+ setDisallowedAppsManagedUser(TEST_DPC_PACKAGE_NAME);
+ setRequiredAppsManagedUser(TEST_DPC_PACKAGE_NAME);
+
+ verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_USER, TEST_DPC_PACKAGE_NAME);
+ }
+
+ /**
+ * @see {@link #testAllowedAndDisallowedAtTheSameTimeManagedDevice}
+ */
+ @Test
+ public void testAllowedAndDisallowedAtTheSameTimeManagedProfile() {
+ setDisallowedAppsManagedProfile(TEST_DPC_PACKAGE_NAME);
+ setRequiredAppsManagedProfile(TEST_DPC_PACKAGE_NAME);
+
+ verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_PROFILE, TEST_DPC_PACKAGE_NAME);
+ }
+
+ @Test
+ public void testNotRequiredAndDisallowedInResManagedDevice() {
+ verifyEmptyIntersection(R.array.required_apps_managed_device,
+ R.array.disallowed_apps_managed_device);
+ }
+
+ @Test
+ public void testNotRequiredAndDisallowedInResManagedUser() {
+ verifyEmptyIntersection(R.array.required_apps_managed_user,
+ R.array.disallowed_apps_managed_user);
+ }
+
+ @Test
+ public void testNotRequiredAndDisallowedInResManagedProfile() {
+ verifyEmptyIntersection(R.array.required_apps_managed_profile,
+ R.array.disallowed_apps_managed_profile);
+ }
+
+ @Test
+ public void testNotRequiredAndDisallowedInResManagedDeviceVendor() {
+ verifyEmptyIntersection(R.array.vendor_required_apps_managed_device,
+ R.array.vendor_disallowed_apps_managed_device);
+ }
+
+ @Test
+ public void testNotRequiredAndDisallowedInResManagedUserVendor() {
+ verifyEmptyIntersection(R.array.vendor_required_apps_managed_user,
+ R.array.vendor_disallowed_apps_managed_user);
+ }
+
+ @Test
+ public void testNotRequiredAndDisallowedInResManagedProfileVendor() {
+ verifyEmptyIntersection(R.array.vendor_required_apps_managed_profile,
+ R.array.vendor_disallowed_apps_managed_profile);
+ }
+
+ private ArrayList<String> getStringArrayInRealResources(int id) {
+ return new ArrayList<>(Arrays.asList(mRealResources.getStringArray(id)));
+ }
+
+ private void verifyEmptyIntersection(int requiredId, int disallowedId) {
+ ArrayList<String> required = getStringArrayInRealResources(requiredId);
+ ArrayList<String> disallowed = getStringArrayInRealResources(disallowedId);
+ required.retainAll(disallowed);
+ assertTrue(required.isEmpty());
+ }
+
+ private void verifyAppsAreNonRequired(String action, String... appArray) {
+ assertEquals(listFromArray(appArray),
+ mHelper.getNonRequiredApps(TEST_MDM_COMPONENT_NAME, TEST_USER_ID, action));
+ }
+
+ private void setRequiredAppsManagedDevice(String... apps) {
+ setStringArray(R.array.required_apps_managed_device, apps);
+ }
+
+ private void setVendorRequiredAppsManagedDevice(String... apps) {
+ setStringArray(R.array.vendor_required_apps_managed_device, apps);
+ }
+
+ private void setDisallowedAppsManagedDevice(String... apps) {
+ setStringArray(R.array.disallowed_apps_managed_device, apps);
+ }
+
+ private void setVendorDisallowedAppsManagedDevice(String... apps) {
+ setStringArray(R.array.vendor_disallowed_apps_managed_device, apps);
+ }
+
+ private void setRequiredAppsManagedProfile(String... apps) {
+ setStringArray(R.array.required_apps_managed_profile, apps);
+ }
+
+ private void setVendorRequiredAppsManagedProfile(String... apps) {
+ setStringArray(R.array.vendor_required_apps_managed_profile, apps);
+ }
+
+ private void setDisallowedAppsManagedProfile(String... apps) {
+ setStringArray(R.array.disallowed_apps_managed_profile, apps);
+ }
+
+ private void setVendorDisallowedAppsManagedProfile(String... apps) {
+ setStringArray(R.array.vendor_disallowed_apps_managed_profile, apps);
+ }
+
+ private void setRequiredAppsManagedUser(String... apps) {
+ setStringArray(R.array.required_apps_managed_user, apps);
+ }
+
+ private void setVendorRequiredAppsManagedUser(String... apps) {
+ setStringArray(R.array.vendor_required_apps_managed_user, apps);
+ }
+
+ private void setDisallowedAppsManagedUser(String... apps) {
+ setStringArray(R.array.disallowed_apps_managed_user, apps);
+ }
+
+ private void setVendorDisallowedAppsManagedUser(String... apps) {
+ setStringArray(R.array.vendor_disallowed_apps_managed_user, apps);
+ }
+
+ private void setStringArray(int resourceId, String[] strs) {
+ when(mResources.getStringArray(eq(resourceId)))
+ .thenReturn(strs);
+ }
+
+ private void setSystemInputMethods(String... packageNames) {
+ List<InputMethodInfo> inputMethods = new ArrayList<InputMethodInfo>();
+ for (String packageName : packageNames) {
+ ApplicationInfo aInfo = new ApplicationInfo();
+ aInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+ ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.applicationInfo = aInfo;
+ serviceInfo.packageName = packageName;
+ serviceInfo.name = "";
+ ResolveInfo ri = new ResolveInfo();
+ ri.serviceInfo = serviceInfo;
+ InputMethodInfo inputMethodInfo = new InputMethodInfo(ri, false, null, null, 0, false);
+ inputMethods.add(inputMethodInfo);
+ }
+ try {
+ when(mIInputMethodManager.getInputMethodList()).thenReturn(inputMethods);
+ } catch (RemoteException e) {
+ fail(e.toString());
+ }
+ }
+
+ private void setSystemAppsWithLauncher(String... apps) {
+ mSystemAppsWithLauncher = apps;
+ }
+
+ private <T> Set<T> setFromArray(T... array) {
+ if (array == null) {
+ return null;
+ }
+ return new HashSet<T>(Arrays.asList(array));
+ }
+
+ private <T> List<T> listFromArray(T... array) {
+ if (array == null) {
+ return null;
+ }
+ return Arrays.asList(array);
+ }
+
+ class FakePackageManager extends MockPackageManager {
+ @Override
+ public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) {
+ assertTrue("Expected an intent with action ACTION_MAIN",
+ Intent.ACTION_MAIN.equals(intent.getAction()));
+ assertEquals("Expected an intent with category CATEGORY_LAUNCHER",
+ setFromArray(Intent.CATEGORY_LAUNCHER), intent.getCategories());
+ assertTrue("Expected the flag MATCH_UNINSTALLED_PACKAGES",
+ (flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0);
+ assertTrue("Expected the flag MATCH_DISABLED_COMPONENTS",
+ (flags & PackageManager.MATCH_DISABLED_COMPONENTS) != 0);
+ assertTrue("Expected the flag MATCH_DIRECT_BOOT_AWARE",
+ (flags & PackageManager.MATCH_DIRECT_BOOT_AWARE) != 0);
+ assertTrue("Expected the flag MATCH_DIRECT_BOOT_UNAWARE",
+ (flags & PackageManager.MATCH_DIRECT_BOOT_UNAWARE) != 0);
+ assertEquals(userId, TEST_USER_ID);
+ List<ResolveInfo> result = new ArrayList<>();
+ if (mSystemAppsWithLauncher == null) {
+ return result;
+ }
+ for (String packageName : mSystemAppsWithLauncher) {
+ ActivityInfo ai = new ActivityInfo();
+ ai.packageName = packageName;
+ ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = ai;
+ result.add(ri);
+ }
+ return result;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/PasswordBlacklistTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/PasswordBlacklistTest.java
new file mode 100644
index 0000000..1b3fc2c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/PasswordBlacklistTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.devicepolicy;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link PasswordBlacklist}.
+ *
+ * bit FrameworksServicesTests:com.android.server.devicepolicy.PasswordBlacklistTest
+ * runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/devicepolicy/PasswordBlacklistTest.java
+ */
+@RunWith(AndroidJUnit4.class)
+public final class PasswordBlacklistTest {
+ private File mBlacklistFile;
+ private PasswordBlacklist mBlacklist;
+
+ @Before
+ public void setUp() throws IOException {
+ mBlacklistFile = File.createTempFile("pwdbl", null);
+ mBlacklist = new PasswordBlacklist(mBlacklistFile);
+ }
+
+ @After
+ public void tearDown() {
+ mBlacklist.delete();
+ }
+
+ @Test
+ public void matchIsExact() {
+ // Note: Case sensitivity is handled by the user of PasswordBlacklist by normalizing the
+ // values stored in and tested against it.
+ mBlacklist.savePasswordBlacklist("matchIsExact", Arrays.asList("password", "qWERty"));
+ assertTrue(mBlacklist.isPasswordBlacklisted("password"));
+ assertTrue(mBlacklist.isPasswordBlacklisted("qWERty"));
+ assertFalse(mBlacklist.isPasswordBlacklisted("Password"));
+ assertFalse(mBlacklist.isPasswordBlacklisted("qwert"));
+ assertFalse(mBlacklist.isPasswordBlacklisted("letmein"));
+ }
+
+ @Test
+ public void matchIsNotRegex() {
+ mBlacklist.savePasswordBlacklist("matchIsNotRegex", Arrays.asList("a+b*"));
+ assertTrue(mBlacklist.isPasswordBlacklisted("a+b*"));
+ assertFalse(mBlacklist.isPasswordBlacklisted("aaaa"));
+ assertFalse(mBlacklist.isPasswordBlacklisted("abbbb"));
+ assertFalse(mBlacklist.isPasswordBlacklisted("aaaa"));
+ }
+
+ @Test
+ public void matchFailsSafe() throws IOException {
+ try (FileOutputStream fos = new FileOutputStream(mBlacklistFile)) {
+ // Write a malformed blacklist file
+ fos.write(17);
+ }
+ assertTrue(mBlacklist.isPasswordBlacklisted("anything"));
+ assertTrue(mBlacklist.isPasswordBlacklisted("at"));
+ assertTrue(mBlacklist.isPasswordBlacklisted("ALL"));
+ }
+
+ @Test
+ public void blacklistCanBeNamed() {
+ final String name = "identifier";
+ mBlacklist.savePasswordBlacklist(name, Arrays.asList("one", "two", "three"));
+ assertEquals(mBlacklist.getName(), name);
+ }
+
+ @Test
+ public void reportsTheCorrectNumberOfEntries() {
+ mBlacklist.savePasswordBlacklist("Count Entries", Arrays.asList("1", "2", "3", "4"));
+ assertEquals(mBlacklist.getSize(), 4);
+ }
+
+ @Test
+ public void reportsBlacklistFile() {
+ assertEquals(mBlacklistFile, mBlacklist.getFile());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
new file mode 100644
index 0000000..2629b12
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -0,0 +1,230 @@
+/*
+ * 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 com.android.server.display;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.PowerManager;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Spline;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BrightnessMappingStrategyTest {
+
+ private static final float[] LUX_LEVELS = {
+ 0f,
+ 5f,
+ 20f,
+ 40f,
+ 100f,
+ 325f,
+ 600f,
+ 1250f,
+ 2200f,
+ 4000f,
+ 5000f
+ };
+
+ private static final float[] DISPLAY_LEVELS_NITS = {
+ 13.25f,
+ 54.0f,
+ 78.85f,
+ 105.02f,
+ 132.7f,
+ 170.12f,
+ 212.1f,
+ 265.2f,
+ 335.8f,
+ 415.2f,
+ 478.5f,
+ };
+
+ private static final int[] DISPLAY_LEVELS_BACKLIGHT = {
+ 9,
+ 30,
+ 45,
+ 62,
+ 78,
+ 96,
+ 119,
+ 146,
+ 178,
+ 221,
+ 255
+ };
+
+ private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
+ private static final int[] BACKLIGHT_RANGE = { 1, 255 };
+
+ @Test
+ public void testSimpleStrategyMappingAtControlPoints() {
+ BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(
+ LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
+ null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/);
+ assertNotNull("BrightnessMappingStrategy should not be null", simple);
+ for (int i = 0; i < LUX_LEVELS.length; i++) {
+ final float expectedLevel =
+ (float) DISPLAY_LEVELS_BACKLIGHT[i] / PowerManager.BRIGHTNESS_ON;
+ assertEquals(expectedLevel,
+ simple.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/);
+ }
+ }
+
+ @Test
+ public void testSimpleStrategyMappingBetweenControlPoints() {
+ BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(
+ LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
+ null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/);
+ assertNotNull("BrightnessMappingStrategy should not be null", simple);
+ for (int i = 1; i < LUX_LEVELS.length; i++) {
+ final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
+ final float backlight = simple.getBrightness(lux) * PowerManager.BRIGHTNESS_ON;
+ assertTrue("Desired brightness should be between adjacent control points.",
+ backlight > DISPLAY_LEVELS_BACKLIGHT[i-1]
+ && backlight < DISPLAY_LEVELS_BACKLIGHT[i]);
+ }
+ }
+
+ @Test
+ public void testPhysicalStrategyMappingAtControlPoints() {
+ BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(
+ LUX_LEVELS, null /*brightnessLevelsBacklight*/,
+ DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+ assertNotNull("BrightnessMappingStrategy should not be null", physical);
+ for (int i = 0; i < LUX_LEVELS.length; i++) {
+ final float expectedLevel = DISPLAY_LEVELS_NITS[i] / DISPLAY_RANGE_NITS[1];
+ assertEquals(expectedLevel,
+ physical.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/);
+ }
+ }
+
+ @Test
+ public void testPhysicalStrategyMappingBetweenControlPoints() {
+ BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(
+ LUX_LEVELS, null /*brightnessLevelsBacklight*/,
+ DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+ assertNotNull("BrightnessMappingStrategy should not be null", physical);
+ Spline backlightToBrightness =
+ Spline.createSpline(toFloatArray(BACKLIGHT_RANGE), DISPLAY_RANGE_NITS);
+ for (int i = 1; i < LUX_LEVELS.length; i++) {
+ final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
+ final float backlight = physical.getBrightness(lux) * PowerManager.BRIGHTNESS_ON;
+ final float nits = backlightToBrightness.interpolate(backlight);
+ assertTrue("Desired brightness should be between adjacent control points.",
+ nits > DISPLAY_LEVELS_NITS[i-1] && nits < DISPLAY_LEVELS_NITS[i]);
+ }
+ }
+
+ @Test
+ public void testDefaultStrategyIsPhysical() {
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(
+ LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
+ DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+ assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy);
+ }
+
+ @Test
+ public void testNonStrictlyIncreasingLuxLevelsFails() {
+ final float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length);
+ final int idx = lux.length / 2;
+ float tmp = lux[idx];
+ lux[idx] = lux[idx+1];
+ lux[idx+1] = tmp;
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(
+ lux, null /*brightnessLevelsBacklight*/,
+ DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+ assertNull(strategy);
+
+ // And make sure we get the same result even if it's monotone but not increasing.
+ lux[idx] = lux[idx+1];
+ strategy = BrightnessMappingStrategy.create(
+ lux, null /*brightnessLevelsBacklight*/,
+ DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+ assertNull(strategy);
+ }
+
+ @Test
+ public void testDifferentNumberOfControlPointValuesFails() {
+ //Extra lux level
+ final float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length+1);
+ // Make sure it's strictly increasing so that the only failure is the differing array
+ // lengths
+ lux[lux.length - 1] = lux[lux.length - 2] + 1;
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(
+ lux, null /*brightnessLevelsBacklight*/,
+ DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+ assertNull(strategy);
+
+ strategy = BrightnessMappingStrategy.create(
+ lux, DISPLAY_LEVELS_BACKLIGHT,
+ null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/);
+ assertNull(strategy);
+
+ // Extra backlight level
+ final int[] backlight = Arrays.copyOf(
+ DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length+1);
+ backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1;
+ strategy = BrightnessMappingStrategy.create(
+ LUX_LEVELS, backlight,
+ null /*brightnessLevelsNits*/, null /*nitsRange*/, null /*backlightRange*/);
+ assertNull(strategy);
+
+ // Extra nits level
+ final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length+1);
+ nits[nits.length - 1] = nits[nits.length - 2] + 1;
+ strategy = BrightnessMappingStrategy.create(
+ LUX_LEVELS, null /*brightnessLevelsBacklight*/,
+ nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
+ assertNull(strategy);
+ }
+
+ @Test
+ public void testPhysicalStrategyRequiresNitsMapping() {
+ BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(
+ LUX_LEVELS, null /*brightnessLevelsBacklight*/,
+ DISPLAY_LEVELS_NITS, null, BACKLIGHT_RANGE);
+ assertNull(physical);
+
+ physical = BrightnessMappingStrategy.create(
+ LUX_LEVELS, null /*brightnessLevelsBacklight*/,
+ DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, null);
+ assertNull(physical);
+
+ physical = BrightnessMappingStrategy.create(
+ LUX_LEVELS, null /*brightnessLevelsBacklight*/,
+ DISPLAY_LEVELS_NITS, null, null);
+ assertNull(physical);
+ }
+
+ private static float[] toFloatArray(int[] vals) {
+ float[] newVals = new float[vals.length];
+ for (int i = 0; i < vals.length; i++) {
+ newVals[i] = (float) vals[i];
+ }
+ return newVals;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
new file mode 100644
index 0000000..0cc37b4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
@@ -0,0 +1,223 @@
+/*
+ * 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 com.android.server.display;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.hardware.display.BrightnessConfiguration;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.AtomicFile;
+import android.util.Pair;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PersistentDataStoreTest {
+ private PersistentDataStore mDataStore;
+ private TestInjector mInjector;
+
+ @Before
+ public void setUp() {
+ mInjector = new TestInjector();
+ mDataStore = new PersistentDataStore(mInjector);
+ }
+
+ @Test
+ public void testLoadingBrightnessConfigurations() {
+ String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<display-manager-state>\n"
+ + " <brightness-configurations>\n"
+ + " <brightness-configuration user-serial=\"1\">\n"
+ + " <brightness-curve>\n"
+ + " <brightness-point lux=\"0\" nits=\"13.25\"/>\n"
+ + " <brightness-point lux=\"25\" nits=\"35.94\"/>\n"
+ + " </brightness-curve>\n"
+ + " </brightness-configuration>\n"
+ + " <brightness-configuration user-serial=\"3\">\n"
+ + " <brightness-curve>\n"
+ + " <brightness-point lux=\"0\" nits=\"13.25\"/>\n"
+ + " <brightness-point lux=\"10.2\" nits=\"15\"/>\n"
+ + " </brightness-curve>\n"
+ + " </brightness-configuration>\n"
+ + " </brightness-configurations>\n"
+ + "</display-manager-state>\n";
+ InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
+ mInjector.setReadStream(is);
+ mDataStore.loadIfNeeded();
+ BrightnessConfiguration config = mDataStore.getBrightnessConfiguration(1 /*userSerial*/);
+ Pair<float[], float[]> curve = config.getCurve();
+ float[] expectedLux = { 0f, 25f };
+ float[] expectedNits = { 13.25f, 35.94f };
+ assertArrayEquals(expectedLux, curve.first, "lux");
+ assertArrayEquals(expectedNits, curve.second, "nits");
+
+ config = mDataStore.getBrightnessConfiguration(3 /*userSerial*/);
+ curve = config.getCurve();
+ expectedLux = new float[] { 0f, 10.2f };
+ expectedNits = new float[] { 13.25f, 15f };
+ assertArrayEquals(expectedLux, curve.first, "lux");
+ assertArrayEquals(expectedNits, curve.second, "nits");
+ }
+
+ @Test
+ public void testBrightnessConfigWithInvalidCurveIsIgnored() {
+ String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<display-manager-state>\n"
+ + " <brightness-configurations>\n"
+ + " <brightness-configuration user-serial=\"0\">\n"
+ + " <brightness-curve>\n"
+ + " <brightness-point lux=\"1\" nits=\"13.25\"/>\n"
+ + " <brightness-point lux=\"25\" nits=\"35.94\"/>\n"
+ + " </brightness-curve>\n"
+ + " </brightness-configuration>\n"
+ + " </brightness-configurations>\n"
+ + "</display-manager-state>\n";
+ InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
+ mInjector.setReadStream(is);
+ mDataStore.loadIfNeeded();
+ assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
+ }
+
+ @Test
+ public void testBrightnessConfigWithInvalidFloatsIsIgnored() {
+ String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<display-manager-state>\n"
+ + " <brightness-configurations>\n"
+ + " <brightness-configuration user-serial=\"0\">\n"
+ + " <brightness-curve>\n"
+ + " <brightness-point lux=\"0\" nits=\"13.25\"/>\n"
+ + " <brightness-point lux=\"0xFF\" nits=\"foo\"/>\n"
+ + " </brightness-curve>\n"
+ + " </brightness-configuration>\n"
+ + " </brightness-configurations>\n"
+ + "</display-manager-state>\n";
+ InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
+ mInjector.setReadStream(is);
+ mDataStore.loadIfNeeded();
+ assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
+ }
+
+ @Test
+ public void testEmptyBrightnessConfigurationsDoesntCrash() {
+ String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<display-manager-state>\n"
+ + " <brightness-configurations />\n"
+ + "</display-manager-state>\n";
+ InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
+ mInjector.setReadStream(is);
+ mDataStore.loadIfNeeded();
+ assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
+ }
+
+ @Test
+ public void testStoreAndReloadOfBrightnessConfigurations() {
+ final float[] lux = { 0f, 10f };
+ final float[] nits = {1f, 100f };
+ final BrightnessConfiguration config = new BrightnessConfiguration.Builder()
+ .setCurve(lux, nits)
+ .build();
+ mDataStore.loadIfNeeded();
+ assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
+ mDataStore.setBrightnessConfigurationForUser(config, 0);
+
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ mInjector.setWriteStream(baos);
+ mDataStore.saveIfNeeded();
+ assertTrue(mInjector.wasWriteSuccessful());
+
+ TestInjector newInjector = new TestInjector();
+ PersistentDataStore newDataStore = new PersistentDataStore(newInjector);
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ newInjector.setReadStream(bais);
+ newDataStore.loadIfNeeded();
+ assertNotNull(newDataStore.getBrightnessConfiguration(0 /*userSerial*/));
+ assertEquals(mDataStore.getBrightnessConfiguration(0 /*userSerial*/),
+ newDataStore.getBrightnessConfiguration(0 /*userSerial*/));
+ }
+
+ public class TestInjector extends PersistentDataStore.Injector {
+ private InputStream mReadStream;
+ private OutputStream mWriteStream;
+
+ private boolean mWasSuccessful;
+
+ @Override
+ public InputStream openRead() throws FileNotFoundException {
+ if (mReadStream != null) {
+ return mReadStream;
+ } else {
+ throw new FileNotFoundException();
+ }
+ }
+
+ @Override
+ public OutputStream startWrite() {
+ return mWriteStream;
+ }
+
+ @Override
+ public void finishWrite(OutputStream os, boolean success) {
+ mWasSuccessful = success;
+ try {
+ os.close();
+ } catch (IOException e) {
+ // This method can't throw IOException since the super implementation doesn't, so
+ // we just wrap it in a RuntimeException so we end up crashing the test all the
+ // same.
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void setReadStream(InputStream is) {
+ mReadStream = is;
+ }
+
+ public void setWriteStream(OutputStream os) {
+ mWriteStream = os;
+ }
+
+ public boolean wasWriteSuccessful() {
+ return mWasSuccessful;
+ }
+ }
+
+ private static void assertArrayEquals(float[] expected, float[] actual, String name) {
+ assertEquals("Expected " + name + " arrays to be the same length!",
+ expected.length, actual.length);
+ for (int i = 0; i < expected.length; i++) {
+ assertEquals("Expected " + name + " arrays to be equivalent when value " + i
+ + "differs", expected[i], actual[i], 0.01 /*tolerance*/);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
new file mode 100644
index 0000000..ac3abed
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.util.Arrays;
+
+import javax.crypto.SecretKey;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KeySyncUtilsTest {
+ private static final int RECOVERY_KEY_LENGTH_BITS = 256;
+ private static final int THM_KF_HASH_SIZE = 256;
+ private static final int KEY_CLAIMANT_LENGTH_BYTES = 16;
+ private static final String SHA_256_ALGORITHM = "SHA-256";
+
+ @Test
+ public void calculateThmKfHash_isShaOfLockScreenHashWithPrefix() throws Exception {
+ byte[] lockScreenHash = utf8Bytes("012345678910");
+
+ byte[] thmKfHash = KeySyncUtils.calculateThmKfHash(lockScreenHash);
+
+ assertArrayEquals(calculateSha256(utf8Bytes("THM_KF_hash012345678910")), thmKfHash);
+ }
+
+ @Test
+ public void calculateThmKfHash_is256BitsLong() throws Exception {
+ byte[] thmKfHash = KeySyncUtils.calculateThmKfHash(utf8Bytes("1234"));
+
+ assertEquals(THM_KF_HASH_SIZE / Byte.SIZE, thmKfHash.length);
+ }
+
+ @Test
+ public void generateRecoveryKey_returnsA256BitKey() throws Exception {
+ SecretKey key = KeySyncUtils.generateRecoveryKey();
+
+ assertEquals(RECOVERY_KEY_LENGTH_BITS / Byte.SIZE, key.getEncoded().length);
+ }
+
+ @Test
+ public void generateRecoveryKey_generatesANewKeyEachTime() throws Exception {
+ SecretKey a = KeySyncUtils.generateRecoveryKey();
+ SecretKey b = KeySyncUtils.generateRecoveryKey();
+
+ assertFalse(Arrays.equals(a.getEncoded(), b.getEncoded()));
+ }
+
+ @Test
+ public void generateKeyClaimant_returns16Bytes() throws Exception {
+ byte[] keyClaimant = KeySyncUtils.generateKeyClaimant();
+
+ assertEquals(KEY_CLAIMANT_LENGTH_BYTES, keyClaimant.length);
+ }
+
+ @Test
+ public void generateKeyClaimant_generatesANewClaimantEachTime() {
+ byte[] a = KeySyncUtils.generateKeyClaimant();
+ byte[] b = KeySyncUtils.generateKeyClaimant();
+
+ assertFalse(Arrays.equals(a, b));
+ }
+
+ @Test
+ public void concat_concatenatesArrays() {
+ assertArrayEquals(
+ utf8Bytes("hello, world!"),
+ KeySyncUtils.concat(
+ utf8Bytes("hello"),
+ utf8Bytes(", "),
+ utf8Bytes("world"),
+ utf8Bytes("!")));
+ }
+
+ private static byte[] utf8Bytes(String s) {
+ return s.getBytes(StandardCharsets.UTF_8);
+ }
+
+ private static byte[] calculateSha256(byte[] bytes) throws Exception {
+ MessageDigest messageDigest = MessageDigest.getInstance(SHA_256_ALGORITHM);
+ messageDigest.update(bytes);
+ return messageDigest.digest();
+ }
+}
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
new file mode 100644
index 0000000..e20f664
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
@@ -0,0 +1,281 @@
+/*
+ * 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;
+
+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 static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.security.KeyStore;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PlatformKeyManagerTest {
+
+ private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
+ private static final int USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS = 15;
+ private static final int USER_ID_FIXTURE = 42;
+
+ @Mock private Context mContext;
+ @Mock private KeyStoreProxy mKeyStoreProxy;
+ @Mock private KeyguardManager mKeyguardManager;
+
+ @Captor private ArgumentCaptor<KeyStore.ProtectionParameter> mProtectionParameterCaptor;
+ @Captor private ArgumentCaptor<KeyStore.Entry> mEntryArgumentCaptor;
+
+ private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
+ private File mDatabaseFile;
+
+ private PlatformKeyManager mPlatformKeyManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ Context context = InstrumentationRegistry.getTargetContext();
+ mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
+ mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
+ mPlatformKeyManager = new PlatformKeyManager(
+ USER_ID_FIXTURE, mContext, mKeyStoreProxy, mRecoverableKeyStoreDb);
+
+ when(mContext.getSystemService(anyString())).thenReturn(mKeyguardManager);
+ when(mContext.getSystemServiceName(any())).thenReturn("test");
+ when(mKeyguardManager.isDeviceSecure(USER_ID_FIXTURE)).thenReturn(true);
+ }
+
+ @After
+ public void tearDown() {
+ mRecoverableKeyStoreDb.close();
+ mDatabaseFile.delete();
+ }
+
+ @Test
+ public void init_createsEncryptKeyWithCorrectAlias() throws Exception {
+ mPlatformKeyManager.init();
+
+ verify(mKeyStoreProxy).setEntry(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
+ any(),
+ any());
+ }
+
+ @Test
+ public void init_createsEncryptKeyWithCorrectPurposes() throws Exception {
+ mPlatformKeyManager.init();
+
+ assertEquals(KeyProperties.PURPOSE_ENCRYPT, getEncryptKeyProtection().getPurposes());
+ }
+
+ @Test
+ public void init_createsEncryptKeyWithCorrectPaddings() throws Exception {
+ mPlatformKeyManager.init();
+
+ assertArrayEquals(
+ new String[] { KeyProperties.ENCRYPTION_PADDING_NONE },
+ getEncryptKeyProtection().getEncryptionPaddings());
+ }
+
+ @Test
+ public void init_createsEncryptKeyWithCorrectBlockModes() throws Exception {
+ mPlatformKeyManager.init();
+
+ assertArrayEquals(
+ new String[] { KeyProperties.BLOCK_MODE_GCM },
+ getEncryptKeyProtection().getBlockModes());
+ }
+
+ @Test
+ public void init_createsEncryptKeyWithoutAuthenticationRequired() throws Exception {
+ mPlatformKeyManager.init();
+
+ assertFalse(getEncryptKeyProtection().isUserAuthenticationRequired());
+ }
+
+ @Test
+ public void init_createsDecryptKeyWithCorrectAlias() throws Exception {
+ mPlatformKeyManager.init();
+
+ verify(mKeyStoreProxy).setEntry(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ any(),
+ any());
+ }
+
+ @Test
+ public void init_createsDecryptKeyWithCorrectPurposes() throws Exception {
+ mPlatformKeyManager.init();
+
+ assertEquals(KeyProperties.PURPOSE_DECRYPT, getDecryptKeyProtection().getPurposes());
+ }
+
+ @Test
+ public void init_createsDecryptKeyWithCorrectPaddings() throws Exception {
+ mPlatformKeyManager.init();
+
+ assertArrayEquals(
+ new String[] { KeyProperties.ENCRYPTION_PADDING_NONE },
+ getDecryptKeyProtection().getEncryptionPaddings());
+ }
+
+ @Test
+ public void init_createsDecryptKeyWithCorrectBlockModes() throws Exception {
+ mPlatformKeyManager.init();
+
+ assertArrayEquals(
+ new String[] { KeyProperties.BLOCK_MODE_GCM },
+ getDecryptKeyProtection().getBlockModes());
+ }
+
+ @Test
+ public void init_createsDecryptKeyWithAuthenticationRequired() throws Exception {
+ mPlatformKeyManager.init();
+
+ assertTrue(getDecryptKeyProtection().isUserAuthenticationRequired());
+ }
+
+ @Test
+ public void init_createsDecryptKeyWithAuthenticationValidFor15Seconds() throws Exception {
+ mPlatformKeyManager.init();
+
+ assertEquals(
+ USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS,
+ getDecryptKeyProtection().getUserAuthenticationValidityDurationSeconds());
+ }
+
+ @Test
+ public void init_createsDecryptKeyBoundToTheUsersAuthentication() throws Exception {
+ mPlatformKeyManager.init();
+
+ assertEquals(
+ USER_ID_FIXTURE,
+ getDecryptKeyProtection().getBoundToSpecificSecureUserId());
+ }
+
+ @Test
+ public void init_createsBothKeysWithSameMaterial() throws Exception {
+ mPlatformKeyManager.init();
+
+ verify(mKeyStoreProxy, times(2)).setEntry(any(), mEntryArgumentCaptor.capture(), any());
+ List<KeyStore.Entry> entries = mEntryArgumentCaptor.getAllValues();
+ assertArrayEquals(
+ ((KeyStore.SecretKeyEntry) entries.get(0)).getSecretKey().getEncoded(),
+ ((KeyStore.SecretKeyEntry) entries.get(1)).getSecretKey().getEncoded());
+ }
+
+ @Test
+ public void init_setsGenerationIdTo1() throws Exception {
+ mPlatformKeyManager.init();
+
+ assertEquals(1, mPlatformKeyManager.getGenerationId());
+ }
+
+ @Test
+ public void getDecryptKey_getsDecryptKeyWithCorrectAlias() throws Exception {
+ mPlatformKeyManager.getDecryptKey();
+
+ verify(mKeyStoreProxy).getKey(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ any());
+ }
+
+ @Test
+ public void getEncryptKey_getsDecryptKeyWithCorrectAlias() throws Exception {
+ mPlatformKeyManager.getEncryptKey();
+
+ verify(mKeyStoreProxy).getKey(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
+ any());
+ }
+
+ @Test
+ public void regenerate_incrementsTheGenerationId() throws Exception {
+ mPlatformKeyManager.init();
+
+ mPlatformKeyManager.regenerate();
+
+ assertEquals(2, mPlatformKeyManager.getGenerationId());
+ }
+
+ @Test
+ public void regenerate_generatesANewEncryptKeyWithTheCorrectAlias() throws Exception {
+ mPlatformKeyManager.init();
+
+ mPlatformKeyManager.regenerate();
+
+ verify(mKeyStoreProxy).setEntry(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"),
+ any(),
+ any());
+ }
+
+ @Test
+ public void regenerate_generatesANewDecryptKeyWithTheCorrectAlias() throws Exception {
+ mPlatformKeyManager.init();
+
+ mPlatformKeyManager.regenerate();
+
+ verify(mKeyStoreProxy).setEntry(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
+ any(),
+ any());
+ }
+
+ private KeyProtection getEncryptKeyProtection() throws Exception {
+ verify(mKeyStoreProxy).setEntry(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
+ any(),
+ mProtectionParameterCaptor.capture());
+ return (KeyProtection) mProtectionParameterCaptor.getValue();
+ }
+
+ private KeyProtection getDecryptKeyProtection() throws Exception {
+ verify(mKeyStoreProxy).setEntry(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ any(),
+ mProtectionParameterCaptor.capture());
+ return (KeyProtection) mProtectionParameterCaptor.getValue();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
new file mode 100644
index 0000000..3012931
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGeneratorTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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;
+
+import static junit.framework.Assert.fail;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.security.keystore.AndroidKeyStoreProvider;
+import android.security.keystore.AndroidKeyStoreSecretKey;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.KeyStore;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RecoverableKeyGeneratorTest {
+ private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
+ private static final int TEST_GENERATION_ID = 3;
+ private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
+ private static final String KEY_ALGORITHM = "AES";
+ private static final String SUPPORTED_CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+ private static final String UNSUPPORTED_CIPHER_ALGORITHM = "AES/CTR/NoPadding";
+ private static final String TEST_ALIAS = "karlin";
+ private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyGeneratorTestWrappingKey";
+ private static final int KEYSTORE_UID_SELF = -1;
+ private static final int GCM_TAG_LENGTH_BITS = 128;
+ private static final int GCM_NONCE_LENGTH_BYTES = 12;
+
+ private PlatformEncryptionKey mPlatformKey;
+ private PlatformDecryptionKey mDecryptKey;
+ private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
+ private File mDatabaseFile;
+ private RecoverableKeyGenerator mRecoverableKeyGenerator;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getTargetContext();
+ mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
+ mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
+
+ AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+ mPlatformKey = new PlatformEncryptionKey(TEST_GENERATION_ID, platformKey);
+ mDecryptKey = new PlatformDecryptionKey(TEST_GENERATION_ID, platformKey);
+ mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mRecoverableKeyStoreDb);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
+ keyStore.load(/*param=*/ null);
+ keyStore.deleteEntry(WRAPPING_KEY_ALIAS);
+
+ mRecoverableKeyStoreDb.close();
+ mDatabaseFile.delete();
+ }
+
+ @Test
+ public void generateAndStoreKey_setsKeyInKeyStore() throws Exception {
+ mRecoverableKeyGenerator.generateAndStoreKey(mPlatformKey, KEYSTORE_UID_SELF, TEST_ALIAS);
+
+ KeyStore keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(KEYSTORE_UID_SELF);
+ assertTrue(keyStore.containsAlias(TEST_ALIAS));
+ }
+
+ @Test
+ public void generateAndStoreKey_storesKeyEnabledForAesGcmNoPaddingEncryptDecrypt()
+ throws Exception {
+ mRecoverableKeyGenerator.generateAndStoreKey(mPlatformKey, KEYSTORE_UID_SELF, TEST_ALIAS);
+
+ KeyStore keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(KEYSTORE_UID_SELF);
+ SecretKey key = (SecretKey) keyStore.getKey(TEST_ALIAS, /*password=*/ null);
+ Cipher cipher = Cipher.getInstance(SUPPORTED_CIPHER_ALGORITHM);
+ cipher.init(Cipher.ENCRYPT_MODE, key);
+ byte[] nonce = new byte[GCM_NONCE_LENGTH_BYTES];
+ Arrays.fill(nonce, (byte) 0);
+ cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(GCM_TAG_LENGTH_BITS, nonce));
+ }
+
+ @Test
+ public void generateAndStoreKey_storesKeyDisabledForOtherModes() throws Exception {
+ mRecoverableKeyGenerator.generateAndStoreKey(mPlatformKey, KEYSTORE_UID_SELF, TEST_ALIAS);
+
+ KeyStore keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(KEYSTORE_UID_SELF);
+ SecretKey key = (SecretKey) keyStore.getKey(TEST_ALIAS, /*password=*/ null);
+ Cipher cipher = Cipher.getInstance(UNSUPPORTED_CIPHER_ALGORITHM);
+
+ try {
+ cipher.init(Cipher.ENCRYPT_MODE, key);
+ fail("Should not be able to use key for " + UNSUPPORTED_CIPHER_ALGORITHM);
+ } catch (InvalidKeyException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void generateAndStoreKey_storesWrappedKey() throws Exception {
+ mRecoverableKeyGenerator.generateAndStoreKey(mPlatformKey, KEYSTORE_UID_SELF, TEST_ALIAS);
+
+ KeyStore keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(KEYSTORE_UID_SELF);
+ SecretKey key = (SecretKey) keyStore.getKey(TEST_ALIAS, /*password=*/ null);
+ WrappedKey wrappedKey = mRecoverableKeyStoreDb.getKey(KEYSTORE_UID_SELF, TEST_ALIAS);
+ SecretKey unwrappedKey = WrappedKey
+ .unwrapKeys(mDecryptKey, ImmutableMap.of(TEST_ALIAS, wrappedKey))
+ .get(TEST_ALIAS);
+
+ // key and unwrappedKey should be equivalent. let's check!
+ byte[] plaintext = getUtf8Bytes("dtianpos");
+ Cipher cipher = Cipher.getInstance(SUPPORTED_CIPHER_ALGORITHM);
+ cipher.init(Cipher.ENCRYPT_MODE, key);
+ byte[] encrypted = cipher.doFinal(plaintext);
+ byte[] iv = cipher.getIV();
+ cipher.init(Cipher.DECRYPT_MODE, unwrappedKey, new GCMParameterSpec(128, iv));
+ byte[] decrypted = cipher.doFinal(encrypted);
+ assertArrayEquals(decrypted, plaintext);
+ }
+
+ private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(
+ KEY_ALGORITHM,
+ ANDROID_KEY_STORE_PROVIDER);
+ keyGenerator.init(new KeyGenParameterSpec.Builder(
+ WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+ return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+ }
+
+ private static byte[] getUtf8Bytes(String s) {
+ return s.getBytes(StandardCharsets.UTF_8);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/SecureBoxTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/SecureBoxTest.java
new file mode 100644
index 0000000..72b69f0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/SecureBoxTest.java
@@ -0,0 +1,365 @@
+/*
+ * 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;
+
+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.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.ECPrivateKeySpec;
+import javax.crypto.AEADBadTagException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SecureBoxTest {
+
+ private static final int EC_PUBLIC_KEY_LEN_BYTES = 65;
+ private static final int NUM_TEST_ITERATIONS = 100;
+ private static final int VERSION_LEN_BYTES = 2;
+
+ // The following fixtures were produced by the C implementation of SecureBox v2. We use these to
+ // cross-verify the two implementations.
+ private static final byte[] VAULT_PARAMS =
+ new byte[] {
+ (byte) 0x04, (byte) 0xb8, (byte) 0x00, (byte) 0x11, (byte) 0x18, (byte) 0x98,
+ (byte) 0x1d, (byte) 0xf0, (byte) 0x6e, (byte) 0xb4, (byte) 0x94, (byte) 0xfe,
+ (byte) 0x86, (byte) 0xda, (byte) 0x1c, (byte) 0x07, (byte) 0x8d, (byte) 0x01,
+ (byte) 0xb4, (byte) 0x3a, (byte) 0xf6, (byte) 0x8d, (byte) 0xdc, (byte) 0x61,
+ (byte) 0xd0, (byte) 0x46, (byte) 0x49, (byte) 0x95, (byte) 0x0f, (byte) 0x10,
+ (byte) 0x86, (byte) 0x93, (byte) 0x24, (byte) 0x66, (byte) 0xe0, (byte) 0x3f,
+ (byte) 0xd2, (byte) 0xdf, (byte) 0xf3, (byte) 0x79, (byte) 0x20, (byte) 0x1d,
+ (byte) 0x91, (byte) 0x55, (byte) 0xb0, (byte) 0xe5, (byte) 0xbd, (byte) 0x7a,
+ (byte) 0x8b, (byte) 0x32, (byte) 0x7d, (byte) 0x25, (byte) 0x53, (byte) 0xa2,
+ (byte) 0xfc, (byte) 0xa5, (byte) 0x65, (byte) 0xe1, (byte) 0xbd, (byte) 0x21,
+ (byte) 0x44, (byte) 0x7e, (byte) 0x78, (byte) 0x52, (byte) 0xfa, (byte) 0x31,
+ (byte) 0x32, (byte) 0x33, (byte) 0x34, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x78, (byte) 0x56, (byte) 0x34, (byte) 0x12, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0a, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00
+ };
+ private static final byte[] VAULT_CHALLENGE = getBytes("Not a real vault challenge");
+ private static final byte[] THM_KF_HASH = getBytes("12345678901234567890123456789012");
+ private static final byte[] ENCRYPTED_RECOVERY_KEY =
+ new byte[] {
+ (byte) 0x02, (byte) 0x00, (byte) 0x04, (byte) 0xe3, (byte) 0xa8, (byte) 0xd0,
+ (byte) 0x32, (byte) 0x3c, (byte) 0xc7, (byte) 0xe5, (byte) 0xe8, (byte) 0xc1,
+ (byte) 0x73, (byte) 0x4c, (byte) 0x75, (byte) 0x20, (byte) 0x2e, (byte) 0xb7,
+ (byte) 0xba, (byte) 0xef, (byte) 0x3e, (byte) 0x3e, (byte) 0xa6, (byte) 0x93,
+ (byte) 0xe9, (byte) 0xde, (byte) 0xa7, (byte) 0x00, (byte) 0x09, (byte) 0xba,
+ (byte) 0xa8, (byte) 0x9c, (byte) 0xac, (byte) 0x72, (byte) 0xff, (byte) 0xf6,
+ (byte) 0x84, (byte) 0x16, (byte) 0xb0, (byte) 0xff, (byte) 0x47, (byte) 0x98,
+ (byte) 0x53, (byte) 0xc4, (byte) 0xa3, (byte) 0x4a, (byte) 0x54, (byte) 0x21,
+ (byte) 0x8e, (byte) 0x00, (byte) 0x4b, (byte) 0xfa, (byte) 0xce, (byte) 0xe3,
+ (byte) 0x79, (byte) 0x8e, (byte) 0x20, (byte) 0x7c, (byte) 0x9b, (byte) 0xc4,
+ (byte) 0x7c, (byte) 0xd5, (byte) 0x33, (byte) 0x70, (byte) 0x96, (byte) 0xdc,
+ (byte) 0xa0, (byte) 0x1f, (byte) 0x6e, (byte) 0xbb, (byte) 0x5d, (byte) 0x0c,
+ (byte) 0x64, (byte) 0x5f, (byte) 0xed, (byte) 0xbf, (byte) 0x79, (byte) 0x8a,
+ (byte) 0x0e, (byte) 0xd6, (byte) 0x4b, (byte) 0x93, (byte) 0xc9, (byte) 0xcd,
+ (byte) 0x25, (byte) 0x06, (byte) 0x73, (byte) 0x5e, (byte) 0xdb, (byte) 0xac,
+ (byte) 0xa8, (byte) 0xeb, (byte) 0x6e, (byte) 0x26, (byte) 0x77, (byte) 0x56,
+ (byte) 0xd1, (byte) 0x23, (byte) 0x48, (byte) 0xb6, (byte) 0x6a, (byte) 0x15,
+ (byte) 0xd4, (byte) 0x3e, (byte) 0x38, (byte) 0x7d, (byte) 0x6f, (byte) 0x6f,
+ (byte) 0x7c, (byte) 0x0b, (byte) 0x93, (byte) 0x4e, (byte) 0xb3, (byte) 0x21,
+ (byte) 0x44, (byte) 0x86, (byte) 0xf3, (byte) 0x2e
+ };
+ private static final byte[] KEY_CLAIMANT = getBytes("asdfasdfasdfasdf");
+ private static final byte[] RECOVERY_CLAIM =
+ new byte[] {
+ (byte) 0x02, (byte) 0x00, (byte) 0x04, (byte) 0x16, (byte) 0x75, (byte) 0x5b,
+ (byte) 0xa2, (byte) 0xdc, (byte) 0x2b, (byte) 0x58, (byte) 0xb9, (byte) 0x66,
+ (byte) 0xcb, (byte) 0x6f, (byte) 0xb1, (byte) 0xc1, (byte) 0xb0, (byte) 0x1d,
+ (byte) 0x82, (byte) 0x29, (byte) 0x97, (byte) 0xec, (byte) 0x65, (byte) 0x5e,
+ (byte) 0xef, (byte) 0x14, (byte) 0xc7, (byte) 0xf0, (byte) 0xf1, (byte) 0x83,
+ (byte) 0x15, (byte) 0x0b, (byte) 0xcb, (byte) 0x33, (byte) 0x2d, (byte) 0x05,
+ (byte) 0x20, (byte) 0xdc, (byte) 0xc7, (byte) 0x0d, (byte) 0xc8, (byte) 0xc0,
+ (byte) 0xc9, (byte) 0xa8, (byte) 0x67, (byte) 0xc8, (byte) 0x16, (byte) 0xfe,
+ (byte) 0xfb, (byte) 0xb0, (byte) 0x28, (byte) 0x8e, (byte) 0x4f, (byte) 0xd5,
+ (byte) 0x31, (byte) 0xa7, (byte) 0x94, (byte) 0x33, (byte) 0x23, (byte) 0x15,
+ (byte) 0x04, (byte) 0xbf, (byte) 0x13, (byte) 0x6a, (byte) 0x28, (byte) 0x8f,
+ (byte) 0xa6, (byte) 0xfc, (byte) 0x01, (byte) 0xd5, (byte) 0x69, (byte) 0x3d,
+ (byte) 0x96, (byte) 0x0c, (byte) 0x37, (byte) 0xb4, (byte) 0x1e, (byte) 0x13,
+ (byte) 0x40, (byte) 0xcc, (byte) 0x44, (byte) 0x19, (byte) 0xf2, (byte) 0xdb,
+ (byte) 0x49, (byte) 0x80, (byte) 0x9f, (byte) 0xef, (byte) 0xee, (byte) 0x41,
+ (byte) 0xe6, (byte) 0x3f, (byte) 0xa8, (byte) 0xea, (byte) 0x89, (byte) 0xfe,
+ (byte) 0x56, (byte) 0x20, (byte) 0xba, (byte) 0x90, (byte) 0x9a, (byte) 0xba,
+ (byte) 0x0e, (byte) 0x30, (byte) 0xa7, (byte) 0x2b, (byte) 0x0a, (byte) 0x12,
+ (byte) 0x0b, (byte) 0x03, (byte) 0xd1, (byte) 0x0c, (byte) 0x8e, (byte) 0x82,
+ (byte) 0x03, (byte) 0xa1, (byte) 0x7f, (byte) 0xc8, (byte) 0xd0, (byte) 0xa9,
+ (byte) 0x86, (byte) 0x55, (byte) 0x63, (byte) 0xdc, (byte) 0x70, (byte) 0x34,
+ (byte) 0x21, (byte) 0x2a, (byte) 0x41, (byte) 0x3f, (byte) 0xbb, (byte) 0x82,
+ (byte) 0x82, (byte) 0xf9, (byte) 0x2b, (byte) 0xd2, (byte) 0x33, (byte) 0x03,
+ (byte) 0x50, (byte) 0xd2, (byte) 0x27, (byte) 0xeb, (byte) 0x1a
+ };
+
+ private static final byte[] TEST_SHARED_SECRET = getBytes("TEST_SHARED_SECRET");
+ private static final byte[] TEST_HEADER = getBytes("TEST_HEADER");
+ private static final byte[] TEST_PAYLOAD = getBytes("TEST_PAYLOAD");
+
+ private static final PublicKey THM_PUBLIC_KEY;
+ private static final PrivateKey THM_PRIVATE_KEY;
+
+ static {
+ try {
+ THM_PUBLIC_KEY =
+ SecureBox.decodePublicKey(
+ new byte[] {
+ (byte) 0x04, (byte) 0xb8, (byte) 0x00, (byte) 0x11, (byte) 0x18,
+ (byte) 0x98, (byte) 0x1d, (byte) 0xf0, (byte) 0x6e, (byte) 0xb4,
+ (byte) 0x94, (byte) 0xfe, (byte) 0x86, (byte) 0xda, (byte) 0x1c,
+ (byte) 0x07, (byte) 0x8d, (byte) 0x01, (byte) 0xb4, (byte) 0x3a,
+ (byte) 0xf6, (byte) 0x8d, (byte) 0xdc, (byte) 0x61, (byte) 0xd0,
+ (byte) 0x46, (byte) 0x49, (byte) 0x95, (byte) 0x0f, (byte) 0x10,
+ (byte) 0x86, (byte) 0x93, (byte) 0x24, (byte) 0x66, (byte) 0xe0,
+ (byte) 0x3f, (byte) 0xd2, (byte) 0xdf, (byte) 0xf3, (byte) 0x79,
+ (byte) 0x20, (byte) 0x1d, (byte) 0x91, (byte) 0x55, (byte) 0xb0,
+ (byte) 0xe5, (byte) 0xbd, (byte) 0x7a, (byte) 0x8b, (byte) 0x32,
+ (byte) 0x7d, (byte) 0x25, (byte) 0x53, (byte) 0xa2, (byte) 0xfc,
+ (byte) 0xa5, (byte) 0x65, (byte) 0xe1, (byte) 0xbd, (byte) 0x21,
+ (byte) 0x44, (byte) 0x7e, (byte) 0x78, (byte) 0x52, (byte) 0xfa
+ });
+ THM_PRIVATE_KEY =
+ decodePrivateKey(
+ new byte[] {
+ (byte) 0x70, (byte) 0x01, (byte) 0xc7, (byte) 0x87, (byte) 0x32,
+ (byte) 0x2f, (byte) 0x1c, (byte) 0x9a, (byte) 0x6e, (byte) 0xb1,
+ (byte) 0x91, (byte) 0xca, (byte) 0x4e, (byte) 0xb5, (byte) 0x44,
+ (byte) 0xba, (byte) 0xc8, (byte) 0x68, (byte) 0xc6, (byte) 0x0a,
+ (byte) 0x76, (byte) 0xcb, (byte) 0xd3, (byte) 0x63, (byte) 0x67,
+ (byte) 0x7c, (byte) 0xb0, (byte) 0x11, (byte) 0x82, (byte) 0x65,
+ (byte) 0x77, (byte) 0x01
+ });
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ @Test
+ public void genKeyPair_alwaysReturnsANewKeyPair() throws Exception {
+ KeyPair keyPair1 = SecureBox.genKeyPair();
+ KeyPair keyPair2 = SecureBox.genKeyPair();
+ assertThat(keyPair1).isNotEqualTo(keyPair2);
+ }
+
+ @Test
+ public void decryptRecoveryClaim() throws Exception {
+ byte[] claimContent =
+ SecureBox.decrypt(
+ THM_PRIVATE_KEY,
+ /*sharedSecret=*/ null,
+ SecureBox.concat(getBytes("V1 KF_claim"), VAULT_PARAMS, VAULT_CHALLENGE),
+ RECOVERY_CLAIM);
+ assertThat(claimContent).isEqualTo(SecureBox.concat(THM_KF_HASH, KEY_CLAIMANT));
+ }
+
+ @Test
+ public void decryptRecoveryKey_doesNotThrowForValidAuthenticationTag() throws Exception {
+ SecureBox.decrypt(
+ THM_PRIVATE_KEY,
+ THM_KF_HASH,
+ SecureBox.concat(getBytes("V1 THM_encrypted_recovery_key"), VAULT_PARAMS),
+ ENCRYPTED_RECOVERY_KEY);
+ }
+
+ @Test
+ public void encryptThenDecrypt() throws Exception {
+ byte[] state = TEST_PAYLOAD;
+ // Iterate multiple times to amplify any errors
+ for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
+ state = SecureBox.encrypt(THM_PUBLIC_KEY, TEST_SHARED_SECRET, TEST_HEADER, state);
+ }
+ for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
+ state = SecureBox.decrypt(THM_PRIVATE_KEY, TEST_SHARED_SECRET, TEST_HEADER, state);
+ }
+ assertThat(state).isEqualTo(TEST_PAYLOAD);
+ }
+
+ @Test
+ public void encryptThenDecrypt_nullPublicPrivateKeys() throws Exception {
+ byte[] encrypted =
+ SecureBox.encrypt(
+ /*theirPublicKey=*/ null, TEST_SHARED_SECRET, TEST_HEADER, TEST_PAYLOAD);
+ byte[] decrypted =
+ SecureBox.decrypt(
+ /*ourPrivateKey=*/ null, TEST_SHARED_SECRET, TEST_HEADER, encrypted);
+ assertThat(decrypted).isEqualTo(TEST_PAYLOAD);
+ }
+
+ @Test
+ public void encryptThenDecrypt_nullSharedSecret() throws Exception {
+ byte[] encrypted =
+ SecureBox.encrypt(
+ THM_PUBLIC_KEY, /*sharedSecret=*/ null, TEST_HEADER, TEST_PAYLOAD);
+ byte[] decrypted =
+ SecureBox.decrypt(THM_PRIVATE_KEY, /*sharedSecret=*/ null, TEST_HEADER, encrypted);
+ assertThat(decrypted).isEqualTo(TEST_PAYLOAD);
+ }
+
+ @Test
+ public void encryptThenDecrypt_nullHeader() throws Exception {
+ byte[] encrypted =
+ SecureBox.encrypt(
+ THM_PUBLIC_KEY, TEST_SHARED_SECRET, /*header=*/ null, TEST_PAYLOAD);
+ byte[] decrypted =
+ SecureBox.decrypt(THM_PRIVATE_KEY, TEST_SHARED_SECRET, /*header=*/ null, encrypted);
+ assertThat(decrypted).isEqualTo(TEST_PAYLOAD);
+ }
+
+ @Test
+ public void encryptThenDecrypt_nullPayload() throws Exception {
+ byte[] encrypted =
+ SecureBox.encrypt(
+ THM_PUBLIC_KEY, TEST_SHARED_SECRET, TEST_HEADER, /*payload=*/ null);
+ byte[] decrypted =
+ SecureBox.decrypt(
+ THM_PRIVATE_KEY,
+ TEST_SHARED_SECRET,
+ TEST_HEADER,
+ /*encryptedPayload=*/ encrypted);
+ assertThat(decrypted.length).isEqualTo(0);
+ }
+
+ @Test
+ public void encrypt_nullPublicKeyAndSharedSecret() throws Exception {
+ IllegalArgumentException expected =
+ expectThrows(
+ IllegalArgumentException.class,
+ () ->
+ SecureBox.encrypt(
+ /*theirPublicKey=*/ null,
+ /*sharedSecret=*/ null,
+ TEST_HEADER,
+ TEST_PAYLOAD));
+ assertThat(expected.getMessage()).contains("public key and shared secret");
+ }
+
+ @Test
+ public void decrypt_nullPrivateKeyAndSharedSecret() throws Exception {
+ IllegalArgumentException expected =
+ expectThrows(
+ IllegalArgumentException.class,
+ () ->
+ SecureBox.decrypt(
+ /*ourPrivateKey=*/ null,
+ /*sharedSecret=*/ null,
+ TEST_HEADER,
+ TEST_PAYLOAD));
+ assertThat(expected.getMessage()).contains("private key and shared secret");
+ }
+
+ @Test
+ public void decrypt_nullEncryptedPayload() throws Exception {
+ IllegalArgumentException expected =
+ expectThrows(
+ IllegalArgumentException.class,
+ () ->
+ SecureBox.decrypt(
+ THM_PRIVATE_KEY,
+ TEST_SHARED_SECRET,
+ TEST_HEADER,
+ /*encryptedPayload=*/ null));
+ assertThat(expected.getMessage()).contains("payload");
+ }
+
+ @Test
+ public void decrypt_badAuthenticationTag() throws Exception {
+ byte[] encrypted =
+ SecureBox.encrypt(THM_PUBLIC_KEY, TEST_SHARED_SECRET, TEST_HEADER, TEST_PAYLOAD);
+ encrypted[encrypted.length - 1] ^= (byte) 1;
+
+ assertThrows(
+ AEADBadTagException.class,
+ () ->
+ SecureBox.decrypt(
+ THM_PRIVATE_KEY, TEST_SHARED_SECRET, TEST_HEADER, encrypted));
+ }
+
+ @Test
+ public void encrypt_invalidPublicKey() throws Exception {
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+ keyGen.initialize(2048);
+ PublicKey publicKey = keyGen.genKeyPair().getPublic();
+
+ assertThrows(
+ InvalidKeyException.class,
+ () -> SecureBox.encrypt(publicKey, TEST_SHARED_SECRET, TEST_HEADER, TEST_PAYLOAD));
+ }
+
+ @Test
+ public void decrypt_invalidPrivateKey() throws Exception {
+ byte[] encrypted =
+ SecureBox.encrypt(THM_PUBLIC_KEY, TEST_SHARED_SECRET, TEST_HEADER, TEST_PAYLOAD);
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+ keyGen.initialize(2048);
+ PrivateKey privateKey = keyGen.genKeyPair().getPrivate();
+
+ assertThrows(
+ InvalidKeyException.class,
+ () -> SecureBox.decrypt(privateKey, TEST_SHARED_SECRET, TEST_HEADER, encrypted));
+ }
+
+ @Test
+ public void decrypt_publicKeyOutsideCurve() throws Exception {
+ byte[] encrypted =
+ SecureBox.encrypt(THM_PUBLIC_KEY, TEST_SHARED_SECRET, TEST_HEADER, TEST_PAYLOAD);
+ // Flip the least significant bit of the encoded public key
+ encrypted[VERSION_LEN_BYTES + EC_PUBLIC_KEY_LEN_BYTES - 1] ^= (byte) 1;
+
+ InvalidKeyException expected =
+ expectThrows(
+ InvalidKeyException.class,
+ () ->
+ SecureBox.decrypt(
+ THM_PRIVATE_KEY,
+ TEST_SHARED_SECRET,
+ TEST_HEADER,
+ encrypted));
+ assertThat(expected.getMessage()).contains("expected curve");
+ }
+
+ @Test
+ public void encodeThenDecodePublicKey() throws Exception {
+ for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
+ PublicKey originalKey = SecureBox.genKeyPair().getPublic();
+ byte[] encodedKey = SecureBox.encodePublicKey(originalKey);
+ PublicKey decodedKey = SecureBox.decodePublicKey(encodedKey);
+ assertThat(originalKey).isEqualTo(decodedKey);
+ }
+ }
+
+ private static byte[] getBytes(String str) {
+ return str.getBytes(StandardCharsets.UTF_8);
+ }
+
+ private static PrivateKey decodePrivateKey(byte[] keyBytes) throws Exception {
+ assertThat(keyBytes.length).isEqualTo(32);
+ BigInteger priv = new BigInteger(/*signum=*/ 1, keyBytes);
+ KeyFactory keyFactory = KeyFactory.getInstance("EC");
+ return keyFactory.generatePrivate(new ECPrivateKeySpec(priv, SecureBox.EC_PARAM_SPEC));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
new file mode 100644
index 0000000..56122a7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.security.keystore.AndroidKeyStoreSecretKey;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.security.KeyStore;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class WrappedKeyTest {
+ private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
+ private static final String KEY_ALGORITHM = "AES";
+ private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+ private static final String WRAPPING_KEY_ALIAS = "WrappedKeyTestWrappingKeyAlias";
+ private static final int GENERATION_ID = 1;
+ private static final int GCM_TAG_LENGTH_BYTES = 16;
+ private static final int BITS_PER_BYTE = 8;
+ private static final int GCM_TAG_LENGTH_BITS = GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE;
+
+ @After
+ public void tearDown() throws Exception {
+ KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
+ keyStore.load(/*param=*/ null);
+ keyStore.deleteEntry(WRAPPING_KEY_ALIAS);
+ }
+
+ @Test
+ public void fromSecretKey_createsWrappedKeyThatCanBeUnwrapped() throws Exception {
+ PlatformEncryptionKey wrappingKey = new PlatformEncryptionKey(
+ GENERATION_ID, generateAndroidKeyStoreKey());
+ SecretKey rawKey = generateKey();
+
+ WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey);
+
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+ cipher.init(
+ Cipher.UNWRAP_MODE,
+ wrappingKey.getKey(),
+ new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
+ SecretKey unwrappedKey = (SecretKey) cipher.unwrap(
+ wrappedKey.getKeyMaterial(), KEY_ALGORITHM, Cipher.SECRET_KEY);
+ assertEquals(rawKey, unwrappedKey);
+ }
+
+ @Test
+ public void fromSecretKey_returnsAKeyWithTheGenerationIdOfTheWrappingKey() throws Exception {
+ PlatformEncryptionKey wrappingKey = new PlatformEncryptionKey(
+ GENERATION_ID, generateAndroidKeyStoreKey());
+ SecretKey rawKey = generateKey();
+
+ WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey);
+
+ assertEquals(GENERATION_ID, wrappedKey.getPlatformKeyGenerationId());
+ }
+
+ @Test
+ public void decryptWrappedKeys_decryptsWrappedKeys() throws Exception {
+ String alias = "karlin";
+ AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+ SecretKey appKey = generateKey();
+ WrappedKey wrappedKey = WrappedKey.fromSecretKey(
+ new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey);
+ HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
+ keysByAlias.put(alias, wrappedKey);
+
+ Map<String, SecretKey> unwrappedKeys = WrappedKey.unwrapKeys(
+ new PlatformDecryptionKey(GENERATION_ID, platformKey), keysByAlias);
+
+ assertEquals(1, unwrappedKeys.size());
+ assertTrue(unwrappedKeys.containsKey(alias));
+ assertArrayEquals(appKey.getEncoded(), unwrappedKeys.get(alias).getEncoded());
+ }
+
+ @Test
+ public void decryptWrappedKeys_doesNotDieIfSomeKeysAreUnwrappable() throws Exception {
+ String alias = "karlin";
+ AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+ SecretKey appKey = generateKey();
+ WrappedKey wrappedKey = WrappedKey.fromSecretKey(
+ new PlatformEncryptionKey(GENERATION_ID, platformKey), appKey);
+ HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
+ keysByAlias.put(alias, wrappedKey);
+
+ Map<String, SecretKey> unwrappedKeys = WrappedKey.unwrapKeys(
+ new PlatformDecryptionKey(GENERATION_ID, generateAndroidKeyStoreKey()),
+ keysByAlias);
+
+ assertEquals(0, unwrappedKeys.size());
+ }
+
+ @Test
+ public void decryptWrappedKeys_throwsIfPlatformKeyGenerationIdDoesNotMatch() throws Exception {
+ AndroidKeyStoreSecretKey platformKey = generateAndroidKeyStoreKey();
+ WrappedKey wrappedKey = WrappedKey.fromSecretKey(
+ new PlatformEncryptionKey(GENERATION_ID, platformKey), generateKey());
+ HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
+ keysByAlias.put("benji", wrappedKey);
+
+ try {
+ WrappedKey.unwrapKeys(
+ new PlatformDecryptionKey(/*generationId=*/ 2, platformKey),
+ keysByAlias);
+ fail("Should have thrown.");
+ } catch (BadPlatformKeyException e) {
+ assertEquals(
+ "WrappedKey with alias 'benji' was wrapped with platform key 1,"
+ + " not platform key 2",
+ e.getMessage());
+ }
+ }
+
+ private SecretKey generateKey() throws Exception {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
+ keyGenerator.init(/*keySize=*/ 256);
+ return keyGenerator.generateKey();
+ }
+
+ private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(
+ KEY_ALGORITHM,
+ ANDROID_KEY_STORE_PROVIDER);
+ keyGenerator.init(new KeyGenParameterSpec.Builder(
+ WRAPPING_KEY_ALIAS,
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+ return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+ }
+
+ private PlatformDecryptionKey generatePlatformDecryptionKey() throws Exception {
+ return generatePlatformDecryptionKey(GENERATION_ID);
+ }
+
+ private PlatformDecryptionKey generatePlatformDecryptionKey(int generationId) throws Exception {
+ return new PlatformDecryptionKey(generationId, generateAndroidKeyStoreKey());
+ }
+}
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
new file mode 100644
index 0000000..d5ad959
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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 org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import com.android.server.locksettings.recoverablekeystore.WrappedKey;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RecoverableKeyStoreDbTest {
+ private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
+
+ private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
+ private File mDatabaseFile;
+
+ @Before
+ public void setUp() {
+ Context context = InstrumentationRegistry.getTargetContext();
+ mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
+ mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
+ }
+
+ @After
+ public void tearDown() {
+ mRecoverableKeyStoreDb.close();
+ mDatabaseFile.delete();
+ }
+
+ @Test
+ public void insertKey_replacesOldKey() {
+ int userId = 12;
+ String alias = "test";
+ WrappedKey oldWrappedKey = new WrappedKey(
+ getUtf8Bytes("nonce1"),
+ getUtf8Bytes("keymaterial1"),
+ /*platformKeyGenerationId=*/ 1);
+ mRecoverableKeyStoreDb.insertKey(
+ userId, alias, oldWrappedKey);
+ byte[] nonce = getUtf8Bytes("nonce2");
+ byte[] keyMaterial = getUtf8Bytes("keymaterial2");
+ WrappedKey newWrappedKey = new WrappedKey(
+ nonce, keyMaterial, /*platformKeyGenerationId=*/2);
+
+ mRecoverableKeyStoreDb.insertKey(
+ userId, alias, newWrappedKey);
+
+ WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(userId, alias);
+ assertArrayEquals(nonce, retrievedKey.getNonce());
+ assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial());
+ assertEquals(2, retrievedKey.getPlatformKeyGenerationId());
+ }
+
+ @Test
+ public void insertKey_allowsTwoUidsToHaveSameAlias() {
+ String alias = "pcoulton";
+ WrappedKey key1 = new WrappedKey(
+ getUtf8Bytes("nonce1"),
+ getUtf8Bytes("key1"),
+ /*platformKeyGenerationId=*/ 1);
+ WrappedKey key2 = new WrappedKey(
+ getUtf8Bytes("nonce2"),
+ getUtf8Bytes("key2"),
+ /*platformKeyGenerationId=*/ 1);
+
+ mRecoverableKeyStoreDb.insertKey(/*uid=*/ 1, alias, key1);
+ mRecoverableKeyStoreDb.insertKey(/*uid=*/ 2, alias, key2);
+
+ assertArrayEquals(
+ getUtf8Bytes("nonce1"),
+ mRecoverableKeyStoreDb.getKey(1, alias).getNonce());
+ assertArrayEquals(
+ getUtf8Bytes("nonce2"),
+ mRecoverableKeyStoreDb.getKey(2, alias).getNonce());
+ }
+
+ @Test
+ public void getKey_returnsNullIfNoKey() {
+ WrappedKey key = mRecoverableKeyStoreDb.getKey(
+ /*userId=*/ 1, /*alias=*/ "hello");
+
+ assertNull(key);
+ }
+
+ @Test
+ public void getKey_returnsInsertedKey() {
+ int userId = 12;
+ int generationId = 6;
+ String alias = "test";
+ byte[] nonce = getUtf8Bytes("nonce");
+ byte[] keyMaterial = getUtf8Bytes("keymaterial");
+ WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId);
+ mRecoverableKeyStoreDb.insertKey(userId, alias, wrappedKey);
+
+ WrappedKey retrievedKey = mRecoverableKeyStoreDb.getKey(userId, alias);
+
+ assertArrayEquals(nonce, retrievedKey.getNonce());
+ assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial());
+ assertEquals(generationId, retrievedKey.getPlatformKeyGenerationId());
+ }
+
+ @Test
+ public void getAllKeys_getsKeysWithUserIdAndGenerationId() {
+ int userId = 12;
+ int generationId = 6;
+ String alias = "test";
+ byte[] nonce = getUtf8Bytes("nonce");
+ byte[] keyMaterial = getUtf8Bytes("keymaterial");
+ WrappedKey wrappedKey = new WrappedKey(nonce, keyMaterial, generationId);
+ mRecoverableKeyStoreDb.insertKey(userId, alias, wrappedKey);
+
+ Map<String, WrappedKey> keys = mRecoverableKeyStoreDb.getAllKeys(userId, generationId);
+
+ assertEquals(1, keys.size());
+ assertTrue(keys.containsKey(alias));
+ WrappedKey retrievedKey = keys.get(alias);
+ assertArrayEquals(nonce, retrievedKey.getNonce());
+ assertArrayEquals(keyMaterial, retrievedKey.getKeyMaterial());
+ assertEquals(generationId, retrievedKey.getPlatformKeyGenerationId());
+ }
+
+ @Test
+ public void getAllKeys_doesNotReturnKeysWithBadGenerationId() {
+ int userId = 12;
+ WrappedKey wrappedKey = new WrappedKey(
+ getUtf8Bytes("nonce"),
+ getUtf8Bytes("keymaterial"),
+ /*platformKeyGenerationId=*/ 5);
+ mRecoverableKeyStoreDb.insertKey(
+ userId, /*alias=*/ "test", wrappedKey);
+
+ Map<String, WrappedKey> keys = mRecoverableKeyStoreDb.getAllKeys(
+ userId, /*generationId=*/ 7);
+
+ assertTrue(keys.isEmpty());
+ }
+
+ @Test
+ public void getAllKeys_doesNotReturnKeysWithBadUserId() {
+ int generationId = 12;
+ WrappedKey wrappedKey = new WrappedKey(
+ getUtf8Bytes("nonce"), getUtf8Bytes("keymaterial"), generationId);
+ mRecoverableKeyStoreDb.insertKey(
+ /*userId=*/ 1, /*alias=*/ "test", wrappedKey);
+
+ Map<String, WrappedKey> keys = mRecoverableKeyStoreDb.getAllKeys(
+ /*userId=*/ 2, generationId);
+
+ assertTrue(keys.isEmpty());
+ }
+
+ @Test
+ public void getPlatformKeyGenerationId_returnsGenerationId() {
+ int userId = 42;
+ int generationId = 24;
+ mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, generationId);
+
+ assertEquals(generationId, mRecoverableKeyStoreDb.getPlatformKeyGenerationId(userId));
+ }
+
+ @Test
+ public void getPlatformKeyGenerationId_returnsMinusOneIfNoEntry() {
+ assertEquals(-1, mRecoverableKeyStoreDb.getPlatformKeyGenerationId(42));
+ }
+
+ @Test
+ public void setPlatformKeyGenerationId_replacesOldEntry() {
+ int userId = 42;
+ mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, 1);
+ mRecoverableKeyStoreDb.setPlatformKeyGenerationId(userId, 2);
+
+ assertEquals(2, mRecoverableKeyStoreDb.getPlatformKeyGenerationId(userId));
+ }
+
+ private static byte[] getUtf8Bytes(String s) {
+ return s.getBytes(StandardCharsets.UTF_8);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java b/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java
index 7f48b8e..0c35fcb 100644
--- a/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java
@@ -19,7 +19,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import com.android.frameworks.servicestests.R;
import com.android.servicestests.aidl.ICmdReceiverService;
@@ -42,6 +41,7 @@
import android.util.Log;
import org.junit.AfterClass;
+import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -81,7 +81,7 @@
private static final long NETWORK_CHECK_TIMEOUT_MS = 4000; // 4 sec
- private static final long SCREEN_ON_DELAY_MS = 500; // 0.5 sec
+ private static final long SCREEN_ON_DELAY_MS = 1000; // 1 sec
private static final long BIND_SERVICE_TIMEOUT_SEC = 4;
@@ -348,13 +348,17 @@
}
private String executeCommand(String cmd) throws IOException {
- final String result = mUiDevice.executeShellCommand(cmd).trim();
+ final String result = executeSilentCommand(cmd);
Log.d(TAG, String.format("Result for '%s': %s", cmd, result));
return result;
}
+ private static String executeSilentCommand(String cmd) throws IOException {
+ return mUiDevice.executeShellCommand(cmd).trim();
+ }
+
private void assertDelayedCommandResult(String cmd, String expectedResult,
- int maxTries, int napTimeMs) throws IOException {
+ int maxTries, int napTimeMs) throws Exception {
String result = "";
for (int i = 1; i <= maxTries; ++i) {
result = executeCommand(cmd);
@@ -394,6 +398,23 @@
}
}
+ private static void fail(String msg) throws Exception {
+ dumpOnFailure();
+ Assert.fail(msg);
+ }
+
+ private static void dumpOnFailure() throws Exception {
+ Log.d(TAG, ">>> Begin network_management dump");
+ Log.printlns(Log.LOG_ID_MAIN, Log.DEBUG,
+ TAG, executeSilentCommand("dumpsys network_management"), null);
+ Log.d(TAG, "<<< End network_management dump");
+
+ Log.d(TAG, ">>> Begin netpolicy dump");
+ Log.printlns(Log.LOG_ID_MAIN, Log.DEBUG,
+ TAG, executeSilentCommand("dumpsys netpolicy"), null);
+ Log.d(TAG, "<<< End netpolicy dump");
+ }
+
private void finishActivity() throws Exception {
mCmdReceiverService.finishActivity();
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java
index 3fd1d55..b2eb572 100644
--- a/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/InstallerTest.java
@@ -16,19 +16,28 @@
package com.android.server.pm;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
import com.android.internal.util.ArrayUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.util.Arrays;
-public class InstallerTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class InstallerTest {
private static final String TAG = "InstallerTest";
private Installer mInstaller;
@@ -64,7 +73,7 @@
}
}
- @Override
+ @Before
public void setUp() throws Exception {
mInstaller = new Installer(getContext());
mInstaller.onStart();
@@ -72,13 +81,15 @@
mQuota.reset();
}
- @Override
+ @After
public void tearDown() throws Exception {
Log.i(TAG, mManual.toString());
Log.i(TAG, mQuota.toString());
mInstaller = null;
}
+ @Test
+ @Ignore("b/68819006")
public void testGetAppSize() throws Exception {
int[] appIds = null;
@@ -119,6 +130,8 @@
}
}
+ @Test
+ @Ignore("b/68819006")
public void testGetUserSize() throws Exception {
final int[] appIds = getAppIds(UserHandle.USER_SYSTEM);
@@ -138,6 +151,8 @@
checkEquals(Arrays.toString(appIds), stats, quotaStats);
}
+ @Test
+ @Ignore("b/68819006")
public void testGetExternalSize() throws Exception {
final int[] appIds = getAppIds(UserHandle.USER_SYSTEM);
@@ -167,6 +182,10 @@
return appIds;
}
+ private static Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
private static void checkEquals(String msg, PackageStats a, PackageStats b) {
checkEquals(msg + " codeSize", a.codeSize, b.codeSize);
checkEquals(msg + " dataSize", a.dataSize, b.dataSize);
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 0995f2e..b073ee5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -206,8 +206,8 @@
new File(InstrumentationRegistry.getContext().getFilesDir(), "com.android.bar-1");
private static final File UPDATED_CODE_PATH =
new File(InstrumentationRegistry.getContext().getFilesDir(), "com.android.bar-2");
- private static final int INITIAL_VERSION_CODE = 10023;
- private static final int UPDATED_VERSION_CODE = 10025;
+ private static final long INITIAL_VERSION_CODE = 10023L;
+ private static final long UPDATED_VERSION_CODE = 10025L;
@Test
public void testPackageStateCopy01() {
@@ -305,7 +305,6 @@
null /*usesStaticLibrariesVersions*/);
assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
- assertThat(testPkgSetting01.origPackage, is(nullValue()));
assertThat(testPkgSetting01.pkgFlags, is(0));
assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
final PackageUserState userState = testPkgSetting01.readUserState(0);
@@ -339,7 +338,6 @@
null /*usesStaticLibrariesVersions*/);
assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
- assertThat(testPkgSetting01.origPackage, is(nullValue()));
assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
final PackageUserState userState = testPkgSetting01.readUserState(0);
@@ -419,7 +417,6 @@
assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH));
assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
- assertSame(testPkgSetting01.origPackage, originalPkgSetting01);
// signatures object must be different
assertNotSame(testPkgSetting01.signatures, originalSignatures);
assertThat(testPkgSetting01.versionCode, is(UPDATED_VERSION_CODE));
@@ -457,7 +454,6 @@
assertThat(testPkgSetting01.appId, is(0));
assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH));
assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
- assertThat(testPkgSetting01.origPackage, is(nullValue()));
assertThat(testPkgSetting01.pkgFlags, is(0));
assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64"));
@@ -506,7 +502,6 @@
assertThat(testPkgSetting01.appId, is(10064));
assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH));
assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
- assertThat(testPkgSetting01.origPackage, is(nullValue()));
assertThat(testPkgSetting01.pkgFlags, is(0));
assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64"));
@@ -551,7 +546,6 @@
assertThat(testPkgSetting01.appId, is(10064));
assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH));
assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
- assertThat(testPkgSetting01.origPackage, is(nullValue()));
assertThat(testPkgSetting01.pkgFlags, is(0));
assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
@@ -658,8 +652,6 @@
// oldCodePaths is _not_ copied
// assertNotSame(origPkgSetting.oldCodePaths, testPkgSetting.oldCodePaths);
// assertThat(origPkgSetting.oldCodePaths, is(not(testPkgSetting.oldCodePaths)));
- assertSame(origPkgSetting.origPackage, testPkgSetting.origPackage);
- assertThat(origPkgSetting.origPackage, is(testPkgSetting.origPackage));
assertSame(origPkgSetting.parentPackageName, testPkgSetting.parentPackageName);
assertThat(origPkgSetting.parentPackageName, is(testPkgSetting.parentPackageName));
assertSame(origPkgSetting.pkg, testPkgSetting.pkg);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 36cc3a0..32b0b26 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -520,6 +520,10 @@
pkg.featureGroups = new ArrayList<>();
pkg.featureGroups.add(new FeatureGroupInfo());
+
+ pkg.mCompileSdkVersionCodename = "foo23";
+ pkg.mCompileSdkVersion = 100;
+ pkg.mVersionCodeMajor = 100;
}
private static void assertAllFieldsExist(PackageParser.Package pkg) throws Exception {
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 ee33e79..56d4b7e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -120,7 +120,6 @@
*/
@SmallTest
public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
-
/**
* Test for the first launch path, no settings file available.
*/
@@ -780,6 +779,7 @@
dumpsysOnLogcat();
+ mService.waitForBitmapSavesForTest();
// Check files and directories.
// Package 3 has no bitmaps, so we don't create a directory.
assertBitmapDirectories(USER_0, CALLING_PACKAGE_1, CALLING_PACKAGE_2);
@@ -835,6 +835,7 @@
makeFile(mService.getUserBitmapFilePath(USER_10), CALLING_PACKAGE_2, "3").createNewFile();
makeFile(mService.getUserBitmapFilePath(USER_10), CALLING_PACKAGE_2, "4").createNewFile();
+ mService.waitForBitmapSavesForTest();
assertBitmapDirectories(USER_0, CALLING_PACKAGE_1, CALLING_PACKAGE_2, CALLING_PACKAGE_3,
"a.b.c", "d.e.f");
@@ -849,6 +850,7 @@
// The below check is the same as above, except this time USER_0 use the CALLING_PACKAGE_3
// directory.
+ mService.waitForBitmapSavesForTest();
assertBitmapDirectories(USER_0, CALLING_PACKAGE_1, CALLING_PACKAGE_2, CALLING_PACKAGE_3);
assertBitmapDirectories(USER_10, CALLING_PACKAGE_1, CALLING_PACKAGE_2);
@@ -1127,13 +1129,13 @@
.setIcon(Icon.createWithResource(getTestContext(), R.drawable.black_32x32))
.build()
)));
-
+ mService.waitForBitmapSavesForTest();
assertWith(getCallerShortcuts())
.forShortcutWithId("s1", si -> {
assertTrue(si.hasIconResource());
assertEquals(R.drawable.black_32x32, si.getIconResourceId());
});
-
+ mService.waitForBitmapSavesForTest();
// Set bitmap icon
assertTrue(mManager.updateShortcuts(list(
new ShortcutInfo.Builder(mClientContext, "s1")
@@ -1141,7 +1143,7 @@
getTestContext().getResources(), R.drawable.black_64x64)))
.build()
)));
-
+ mService.waitForBitmapSavesForTest();
assertWith(getCallerShortcuts())
.forShortcutWithId("s1", si -> {
assertTrue(si.hasIconFile());
@@ -1161,7 +1163,7 @@
getTestContext().getResources(), R.drawable.black_64x64)))
.build()
)));
-
+ mService.waitForBitmapSavesForTest();
assertWith(getCallerShortcuts())
.forShortcutWithId("s1", si -> {
assertTrue(si.hasIconFile());
@@ -1173,7 +1175,7 @@
.setIcon(Icon.createWithResource(getTestContext(), R.drawable.black_32x32))
.build()
)));
-
+ mService.waitForBitmapSavesForTest();
assertWith(getCallerShortcuts())
.forShortcutWithId("s1", si -> {
assertTrue(si.hasIconResource());
diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java
index b57cac0..74013b7 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java
@@ -25,6 +25,7 @@
import android.support.test.filters.SmallTest;
import java.io.File;
+import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -33,6 +34,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
@SmallTest
public class PackageStatusStorageTest {
@@ -48,6 +50,7 @@
// Using the instrumentation context means the database is created in a test app-specific
// directory.
mPackageStatusStorage = new PackageStatusStorage(dataDir);
+ mPackageStatusStorage.initialize();
}
@After
@@ -56,6 +59,16 @@
}
@Test
+ public void initialize_fail() {
+ File readOnlyDir = new File("/system/does/not/exist");
+ PackageStatusStorage packageStatusStorage = new PackageStatusStorage(readOnlyDir);
+ try {
+ packageStatusStorage.initialize();
+ fail();
+ } catch (IOException expected) {}
+ }
+
+ @Test
public void getPackageStatus_initialState() {
assertNull(mPackageStatusStorage.getPackageStatus());
}
diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
index 5d739a3..9cf6392 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
@@ -30,6 +30,8 @@
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
+import java.io.File;
+import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Clock;
@@ -104,7 +106,7 @@
configureTrackingDisabled();
// Initialize the tracker.
- mPackageTracker.start();
+ assertFalse(mPackageTracker.start());
// Check the IntentHelper was not initialized.
mFakeIntentHelper.assertNotInitialized();
@@ -119,7 +121,7 @@
configureTrackingDisabled();
// Initialize the tracker.
- mPackageTracker.start();
+ assertFalse(mPackageTracker.start());
// Check reliability triggering state.
mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
@@ -141,7 +143,7 @@
configureTrackingDisabled();
// Initialize the tracker.
- mPackageTracker.start();
+ assertFalse(mPackageTracker.start());
// Check reliability triggering state.
mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
@@ -166,7 +168,7 @@
mPackageStatusStorage.generateCheckToken(INITIAL_APP_PACKAGE_VERSIONS);
// Initialize the tracker.
- mPackageTracker.start();
+ assertFalse(mPackageTracker.start());
// Check reliability triggering state.
mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
@@ -262,6 +264,35 @@
}
@Test
+ public void trackingEnabled_storageInitializationFails() throws Exception {
+ // Create a PackageStateStorage that will fail to initialize.
+ PackageStatusStorage packageStatusStorage =
+ new PackageStatusStorage(new File("/system/does/not/exist"));
+
+ // Create a new PackageTracker to use the bad storage.
+ mPackageTracker = new PackageTracker(
+ mFakeClock,
+ mMockConfigHelper,
+ mMockPackageManagerHelper,
+ packageStatusStorage,
+ mFakeIntentHelper);
+
+ // Set up device configuration.
+ configureTrackingEnabled();
+ configureReliabilityConfigSettingsOk();
+ configureValidApplications();
+
+ // Initialize the tracker.
+ assertFalse(mPackageTracker.start());
+
+ // Check the IntentHelper was not initialized.
+ mFakeIntentHelper.assertNotInitialized();
+
+ // Check reliability triggering state.
+ mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
+ }
+
+ @Test
public void trackingEnabled_packageUpdate_badUpdateAppManifestEntry() throws Exception {
// Set up device configuration.
configureTrackingEnabled();
@@ -269,7 +300,7 @@
configureValidApplications();
// Initialize the tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -306,7 +337,7 @@
configureValidApplications();
// Initialize the tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -351,7 +382,7 @@
configureValidApplications();
// Initialize the tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -399,7 +430,7 @@
configureValidApplications();
// Initialize the tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -438,7 +469,7 @@
configureValidApplications();
// Initialize the tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -484,7 +515,7 @@
configureValidApplications();
// Initialize the tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -533,7 +564,7 @@
configureValidApplications();
// Initialize the tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -593,7 +624,7 @@
configureValidApplications();
// Initialize the package tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -655,7 +686,7 @@
configureValidApplications();
// Initialize the package tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -711,7 +742,7 @@
configureValidApplications();
// Initialize the package tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -760,7 +791,7 @@
PackageVersions packageVersions = configureValidApplications();
// Initialize the package tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -797,7 +828,7 @@
PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions);
// Initialize the package tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -837,7 +868,7 @@
PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions);
// Initialize the package tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -883,7 +914,7 @@
PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions);
// Initialize the package tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -942,7 +973,7 @@
PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions);
// Initialize the package tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -1017,7 +1048,7 @@
PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions);
// Initialize the package tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -1083,7 +1114,7 @@
PackageStatus.CHECK_COMPLETED_SUCCESS, currentPackageVersions);
// Initialize the package tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
@@ -1138,7 +1169,7 @@
PackageStatus.CHECK_COMPLETED_SUCCESS, currentPackageVersions);
// Initialize the package tracker.
- mPackageTracker.start();
+ assertTrue(mPackageTracker.start());
// Check the intent helper is properly configured.
checkIntentHelperInitializedAndReliabilityTrackingEnabled();
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 86ce90c..b792d82 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -18,9 +18,14 @@
import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
+import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_FORCED;
import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
+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_FREQUENT;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
@@ -79,6 +84,7 @@
private static final long RARE_THRESHOLD = 48 * HOUR_MS;
private MyInjector mInjector;
+ private AppStandbyController mController;
static class MyContextWrapper extends ContextWrapper {
PackageManager mockPm = mock(PackageManager.class);
@@ -237,24 +243,23 @@
public void setUp() throws Exception {
MyContextWrapper myContext = new MyContextWrapper(InstrumentationRegistry.getContext());
mInjector = new MyInjector(myContext, Looper.getMainLooper());
+ mController = setupController();
}
@Test
public void testCharging() throws Exception {
- AppStandbyController controller = setupController();
-
- setChargingState(controller, true);
+ setChargingState(mController, true);
mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
- assertFalse(controller.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
+ assertFalse(mController.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
mInjector.mElapsedRealtime, false));
- setChargingState(controller, false);
+ setChargingState(mController, false);
mInjector.mElapsedRealtime = 2 * RARE_THRESHOLD + 2;
- controller.checkIdleStates(USER_ID);
- assertTrue(controller.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
+ mController.checkIdleStates(USER_ID);
+ assertTrue(mController.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
mInjector.mElapsedRealtime, false));
- setChargingState(controller, true);
- assertFalse(controller.isAppIdleFilteredOrParoled(PACKAGE_1,USER_ID,
+ setChargingState(mController, true);
+ assertFalse(mController.isAppIdleFilteredOrParoled(PACKAGE_1,USER_ID,
mInjector.mElapsedRealtime, false));
}
@@ -282,112 +287,142 @@
@Test
public void testBuckets() throws Exception {
- AppStandbyController controller = setupController();
+ assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
- assertTimeout(controller, 0, UsageStatsManager.STANDBY_BUCKET_NEVER);
-
- reportEvent(controller, USER_INTERACTION, 0);
+ reportEvent(mController, USER_INTERACTION, 0);
// ACTIVE bucket
- assertTimeout(controller, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
+ assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
// WORKING_SET bucket
- assertTimeout(controller, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
+ assertTimeout(mController, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
// WORKING_SET bucket
- assertTimeout(controller, FREQUENT_THRESHOLD - 1, STANDBY_BUCKET_WORKING_SET);
+ assertTimeout(mController, FREQUENT_THRESHOLD - 1, STANDBY_BUCKET_WORKING_SET);
// FREQUENT bucket
- assertTimeout(controller, FREQUENT_THRESHOLD + 1, STANDBY_BUCKET_FREQUENT);
+ assertTimeout(mController, FREQUENT_THRESHOLD + 1, STANDBY_BUCKET_FREQUENT);
// RARE bucket
- assertTimeout(controller, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE);
+ assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE);
- reportEvent(controller, USER_INTERACTION, RARE_THRESHOLD + 1);
+ reportEvent(mController, USER_INTERACTION, RARE_THRESHOLD + 1);
- assertTimeout(controller, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE);
+ assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE);
// RARE bucket
- assertTimeout(controller, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
+ assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
}
@Test
public void testScreenTimeAndBuckets() throws Exception {
- AppStandbyController controller = setupController();
mInjector.setDisplayOn(false);
- assertTimeout(controller, 0, UsageStatsManager.STANDBY_BUCKET_NEVER);
+ assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
- reportEvent(controller, USER_INTERACTION, 0);
+ reportEvent(mController, USER_INTERACTION, 0);
// ACTIVE bucket
- assertTimeout(controller, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
+ assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
// WORKING_SET bucket
- assertTimeout(controller, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
+ assertTimeout(mController, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
// RARE bucket, should fail because the screen wasn't ON.
mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
- controller.checkIdleStates(USER_ID);
- assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller));
+ mController.checkIdleStates(USER_ID);
+ assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
mInjector.setDisplayOn(true);
- assertTimeout(controller, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
+ assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
}
@Test
public void testForcedIdle() throws Exception {
- AppStandbyController controller = setupController();
- setChargingState(controller, false);
+ setChargingState(mController, false);
- controller.forceIdleState(PACKAGE_1, USER_ID, true);
- assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller));
- assertTrue(controller.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+ mController.forceIdleState(PACKAGE_1, USER_ID, true);
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
+ assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
- controller.forceIdleState(PACKAGE_1, USER_ID, false);
- assertEquals(STANDBY_BUCKET_ACTIVE, controller.getAppStandbyBucket(PACKAGE_1, USER_ID, 0,
+ mController.forceIdleState(PACKAGE_1, USER_ID, false);
+ assertEquals(STANDBY_BUCKET_ACTIVE, mController.getAppStandbyBucket(PACKAGE_1, USER_ID, 0,
true));
- assertFalse(controller.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+ assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
}
@Test
public void testNotificationEvent() throws Exception {
- AppStandbyController controller = setupController();
- setChargingState(controller, false);
+ setChargingState(mController, false);
- reportEvent(controller, USER_INTERACTION, 0);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller));
+ reportEvent(mController, USER_INTERACTION, 0);
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
mInjector.mElapsedRealtime = 1;
- reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller));
+ reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
- controller.forceIdleState(PACKAGE_1, USER_ID, true);
- reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
- assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller));
+ mController.forceIdleState(PACKAGE_1, USER_ID, true);
+ reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
+ assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController));
}
@Test
public void testPredictionTimedout() throws Exception {
- AppStandbyController controller = setupController();
- setChargingState(controller, false);
- controller.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_PREDICTED + "CTS", 1 * HOUR_MS);
+ setChargingState(mController, false);
+ // Set it to timeout or usage, so that prediction can override it
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_TIMEOUT, 1 * HOUR_MS);
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_PREDICTED + ":CTS", 1 * HOUR_MS);
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
// Fast forward 12 hours
mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD;
- controller.checkIdleStates(USER_ID);
+ mController.checkIdleStates(USER_ID);
// Should still be in predicted bucket, since prediction timeout is 1 day since prediction
- assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller));
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
// Fast forward two more hours
mInjector.mElapsedRealtime += 2 * HOUR_MS;
- controller.checkIdleStates(USER_ID);
+ mController.checkIdleStates(USER_ID);
// Should have now applied prediction timeout
- assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller));
+ assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController));
// Fast forward RARE bucket
mInjector.mElapsedRealtime += RARE_THRESHOLD;
- controller.checkIdleStates(USER_ID);
+ mController.checkIdleStates(USER_ID);
// Should continue to apply prediction timeout
- assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller));
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
+ }
+
+ @Test
+ public void testOverrides() throws Exception {
+ setChargingState(mController, false);
+ // Can force to NEVER
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
+ REASON_FORCED, 1 * HOUR_MS);
+ assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
+
+ // Prediction can't override FORCED reason
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+ REASON_FORCED, 1 * HOUR_MS);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_PREDICTED, 1 * HOUR_MS);
+ assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController));
+
+ // Prediction can't override NEVER
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
+ REASON_DEFAULT, 2 * HOUR_MS);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_PREDICTED, 2 * HOUR_MS);
+ assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
+
+ // Prediction can't set to NEVER
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_USAGE, 2 * HOUR_MS);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
+ REASON_PREDICTED, 2 * HOUR_MS);
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index 401f585..9ad41cd 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -39,9 +39,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
import org.mockito.Mockito;
import org.mockito.Matchers;
-import org.mockito.compat.ArgumentMatcher;
import java.lang.Integer;
import java.util.concurrent.CountDownLatch;
@@ -141,7 +141,7 @@
}
// For matching the package name of a PackageInfo
- private class IsPackageInfoWithName extends ArgumentMatcher<PackageInfo> {
+ private class IsPackageInfoWithName implements ArgumentMatcher<PackageInfo> {
private final String mPackageName;
IsPackageInfoWithName(String name) {
@@ -149,8 +149,8 @@
}
@Override
- public boolean matchesObject(Object p) {
- return ((PackageInfo) p).packageName.equals(mPackageName);
+ public boolean matches(PackageInfo p) {
+ return p.packageName.equals(mPackageName);
}
@Override
@@ -202,10 +202,10 @@
private static PackageInfo createPackageInfo(String packageName, boolean enabled, boolean valid,
boolean installed, Signature[] signatures, long updateTime, boolean hidden,
- int versionCode, boolean isSystemApp) {
+ long versionCode, boolean isSystemApp) {
PackageInfo p = createPackageInfo(packageName, enabled, valid, installed, signatures,
updateTime, hidden);
- p.versionCode = versionCode;
+ p.setLongVersionCode(versionCode);
p.applicationInfo.versionCode = versionCode;
if (isSystemApp) p.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
return p;
@@ -1495,7 +1495,7 @@
public void testGetCurrentWebViewPackage() {
PackageInfo firstPackage = createPackageInfo("first", true /* enabled */,
true /* valid */, true /* installed */);
- firstPackage.versionCode = 100;
+ firstPackage.setLongVersionCode(100);
firstPackage.versionName = "first package version";
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo(firstPackage.packageName, "", true, false, null)};
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 4dd51eb..70906df 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -53,12 +53,12 @@
}
@Override
- SurfaceControl getSurfaceControl() {
+ public SurfaceControl getSurfaceControl() {
return mControl;
}
@Override
- SurfaceControl.Transaction getPendingTransaction() {
+ public SurfaceControl.Transaction getPendingTransaction() {
return mTransaction;
}
}
@@ -93,12 +93,12 @@
}
@Override
- SurfaceControl getSurfaceControl() {
+ public SurfaceControl getSurfaceControl() {
return mHostControl;
}
@Override
- SurfaceControl.Transaction getPendingTransaction() {
+ public SurfaceControl.Transaction getPendingTransaction() {
return mHostTransaction;
}
}
@@ -110,8 +110,8 @@
@Before
public void setUp() throws Exception {
super.setUp();
-
mHost = new MockSurfaceBuildingContainer();
+
mTransaction = mock(SurfaceControl.Transaction.class);
mDimmer = new Dimmer(mHost);
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
new file mode 100644
index 0000000..ce76a22
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
@@ -0,0 +1,88 @@
+/*
+ * 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.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.InputChannel;
+import android.view.Surface;
+import android.view.SurfaceSession;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+/**
+ * Tests for the {@link DragDropController} class.
+ *
+ * atest com.android.server.wm.DragDropControllerTests
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class DragDropControllerTests extends WindowTestsBase {
+ private static final int TIMEOUT_MS = 1000;
+ private DragDropController mTarget;
+ private WindowState mWindow;
+ private IBinder mToken;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ assertNotNull(sWm.mDragDropController);
+ mTarget = sWm.mDragDropController;
+ mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
+ synchronized (sWm.mWindowMap) {
+ // Because sWm is a static object, the previous operation may remain.
+ assertFalse(mTarget.dragDropActiveLocked());
+ }
+ }
+
+ @After
+ public void tearDown() {
+ if (mToken != null) {
+ mTarget.cancelDragAndDrop(mToken);
+ }
+ }
+
+ @Test
+ public void testPrepareDrag() throws Exception {
+ final Surface surface = new Surface();
+ mToken = mTarget.prepareDrag(
+ new SurfaceSession(), 0, 0, mWindow.mClient, 0, 100, 100, surface);
+ assertNotNull(mToken);
+ }
+
+ @Test
+ public void testPrepareDrag_ZeroSizeSurface() throws Exception {
+ final Surface surface = new Surface();
+ mToken = mTarget.prepareDrag(
+ new SurfaceSession(), 0, 0, mWindow.mClient, 0, 0, 0, surface);
+ assertNull(mToken);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index 9ecf51e..c309611 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -24,9 +24,13 @@
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.animation.AnimationHandler;
import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
+import android.animation.ValueAnimator;
import android.graphics.Matrix;
import android.graphics.Point;
import android.platform.test.annotations.Presubmit;
@@ -40,6 +44,7 @@
import android.view.animation.TranslateAnimation;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
+import com.android.server.wm.SurfaceAnimationRunner.AnimatorFactory;
import org.junit.Before;
import org.junit.Rule;
@@ -54,7 +59,7 @@
/**
* Test class for {@link SurfaceAnimationRunner}.
*
- * runtest frameworks-services -c com.android.server.wm.SurfaceAnimationRunnerTest
+ * atest FrameworksServicesTests:com.android.server.wm.SurfaceAnimationRunnerTest
*/
@SmallTest
@Presubmit
@@ -63,6 +68,7 @@
@Mock SurfaceControl mMockSurface;
@Mock Transaction mMockTransaction;
+ @Mock AnimationSpec mMockAnimationSpec;
@Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
private SurfaceAnimationRunner mSurfaceAnimationRunner;
@@ -72,7 +78,7 @@
public void setUp() throws Exception {
super.setUp();
mFinishCallbackLatch = new CountDownLatch(1);
- mSurfaceAnimationRunner = new SurfaceAnimationRunner(null /* callbackProvider */,
+ mSurfaceAnimationRunner = new SurfaceAnimationRunner(null /* callbackProvider */, null,
mMockTransaction);
}
@@ -104,7 +110,8 @@
@Test
public void testCancel_notStarted() throws Exception {
- mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), mMockTransaction);
+ mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
+ mMockTransaction);
mSurfaceAnimationRunner
.startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction,
this::finishedCallback);
@@ -112,22 +119,47 @@
waitUntilHandlersIdle();
assertTrue(mSurfaceAnimationRunner.mPendingAnimations.isEmpty());
assertFinishCallbackNotCalled();
- //verify(mMockSurface).release();
}
@Test
public void testCancel_running() throws Exception {
- mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), mMockTransaction);
- mSurfaceAnimationRunner
- .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction,
- this::finishedCallback);
+ mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
+ mMockTransaction);
+ mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface,
+ mMockTransaction, this::finishedCallback);
waitUntilNextFrame();
assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface);
assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
waitUntilHandlersIdle();
assertFinishCallbackNotCalled();
- //verify(mMockSurface).release();
+ }
+
+ @Test
+ public void testCancel_sneakyCancelBeforeUpdate() throws Exception {
+ mSurfaceAnimationRunner = new SurfaceAnimationRunner(null, () -> new ValueAnimator() {
+ {
+ setFloatValues(0f, 1f);
+ }
+
+ @Override
+ public void addUpdateListener(AnimatorUpdateListener listener) {
+ super.addUpdateListener(animation -> {
+ // Sneaky test cancels animation just before applying frame to simulate
+ // interleaving of multiple threads. Muahahaha
+ if (animation.getCurrentPlayTime() > 0) {
+ mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface);
+ }
+ listener.onAnimationUpdate(animation);
+ });
+ }
+ }, mMockTransaction);
+ when(mMockAnimationSpec.getDuration()).thenReturn(200L);
+ mSurfaceAnimationRunner.startAnimation(mMockAnimationSpec, mMockSurface, mMockTransaction,
+ this::finishedCallback);
+ waitUntilNextFrame();
+ assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
+ verify(mMockAnimationSpec, atLeastOnce()).apply(any(), any(), eq(0L));
}
private void waitUntilNextFrame() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 9a52042..6f739ca 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -33,8 +33,6 @@
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
-import com.google.android.collect.Lists;
-
import com.android.server.wm.SurfaceAnimator.Animatable;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
@@ -50,27 +48,127 @@
/**
* Test class for {@link SurfaceAnimatorTest}.
*
- * runtest frameworks-services -c com.android.server.wm.SurfaceAnimatorTest
+ * atest FrameworksServicesTests:com.android.server.wm.SurfaceAnimatorTest
*/
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
public class SurfaceAnimatorTest extends WindowTestsBase {
- @Mock
- AnimationAdapter mSpec;
- @Mock
- AnimationAdapter mSpec2;
+ @Mock AnimationAdapter mSpec;
+ @Mock AnimationAdapter mSpec2;
@Mock Transaction mTransaction;
- private SurfaceAnimator mSurfaceAnimator;
- private SurfaceControl mParent;
- private SurfaceControl mSurface;
- private boolean mFinishedCallbackCalled;
- private SurfaceControl mLeash;
private SurfaceSession mSession = new SurfaceSession();
+ private MyAnimatable mAnimatable;
- private final Animatable mAnimatable = new Animatable() {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ mAnimatable = new MyAnimatable();
+ }
+
+ @Test
+ public void testRunAnimation() throws Exception {
+ mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
+ OnAnimationFinishedCallback.class);
+
+ assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
+ assertNotNull(mAnimatable.mSurfaceAnimator.getAnimation());
+ verify(mTransaction).reparent(eq(mAnimatable.mSurface), eq(mAnimatable.mLeash.getHandle()));
+ verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
+
+ callbackCaptor.getValue().onAnimationFinished(mSpec);
+ assertFalse(mAnimatable.mSurfaceAnimator.isAnimating());
+ assertNull(mAnimatable.mSurfaceAnimator.getAnimation());
+ assertTrue(mAnimatable.mFinishedCallbackCalled);
+ assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
+ // TODO: Verify reparenting once we use mPendingTransaction to reparent it back
+ }
+
+ @Test
+ public void testOverrideAnimation() throws Exception {
+ mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ final SurfaceControl firstLeash = mAnimatable.mLeash;
+ mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */);
+
+ assertTrue(mAnimatable.mPendingDestroySurfaces.contains(firstLeash));
+ assertFalse(mAnimatable.mFinishedCallbackCalled);
+
+ final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
+ OnAnimationFinishedCallback.class);
+ assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
+ assertNotNull(mAnimatable.mSurfaceAnimator.getAnimation());
+ verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
+
+ // First animation was finished, but this shouldn't cancel the second animation
+ callbackCaptor.getValue().onAnimationFinished(mSpec);
+ assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
+
+ // Second animation was finished
+ verify(mSpec2).startAnimation(any(), any(), callbackCaptor.capture());
+ callbackCaptor.getValue().onAnimationFinished(mSpec2);
+ assertFalse(mAnimatable.mSurfaceAnimator.isAnimating());
+ assertTrue(mAnimatable.mFinishedCallbackCalled);
+ }
+
+ @Test
+ public void testCancelAnimation() throws Exception {
+ mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
+ mAnimatable.mSurfaceAnimator.cancelAnimation();
+ assertFalse(mAnimatable.mSurfaceAnimator.isAnimating());
+ verify(mSpec).onAnimationCancelled(any());
+ assertTrue(mAnimatable.mFinishedCallbackCalled);
+ assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
+ }
+
+ @Test
+ public void testDelayingAnimationStart() throws Exception {
+ mAnimatable.mSurfaceAnimator.startDelayingAnimationStart();
+ mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ verifyZeroInteractions(mSpec);
+ assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
+ mAnimatable.mSurfaceAnimator.endDelayingAnimationStart();
+ verify(mSpec).startAnimation(any(), any(), any());
+ }
+
+ @Test
+ public void testDelayingAnimationStartAndCancelled() throws Exception {
+ mAnimatable.mSurfaceAnimator.startDelayingAnimationStart();
+ mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ mAnimatable.mSurfaceAnimator.cancelAnimation();
+ verifyZeroInteractions(mSpec);
+ assertFalse(mAnimatable.mSurfaceAnimator.isAnimating());
+ assertTrue(mAnimatable.mFinishedCallbackCalled);
+ assertTrue(mAnimatable.mPendingDestroySurfaces.contains(mAnimatable.mLeash));
+ }
+
+ private class MyAnimatable implements Animatable {
+
+ final SurfaceControl mParent;
+ final SurfaceControl mSurface;
+ final ArrayList<SurfaceControl> mPendingDestroySurfaces = new ArrayList<>();
+ final SurfaceAnimator mSurfaceAnimator;
+ SurfaceControl mLeash;
+ boolean mFinishedCallbackCalled;
+
+ MyAnimatable() {
+ mParent = sWm.makeSurfaceBuilder(mSession)
+ .setName("test surface parent")
+ .setSize(3000, 3000)
+ .build();
+ mSurface = sWm.makeSurfaceBuilder(mSession)
+ .setName("test surface")
+ .setSize(1, 1)
+ .build();
+ mFinishedCallbackCalled = false;
+ mLeash = null;
+ mSurfaceAnimator = new SurfaceAnimator(this, mFinishedCallback, sWm);
+ }
+
@Override
public Transaction getPendingTransaction() {
return mTransaction;
@@ -81,15 +179,20 @@
}
@Override
- public void onLeashCreated(Transaction t, SurfaceControl leash) {
+ public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
}
@Override
- public void onLeashDestroyed(Transaction t) {
+ public void onAnimationLeashDestroyed(Transaction t) {
}
@Override
- public Builder makeLeash() {
+ public void destroyAfterPendingTransaction(SurfaceControl surface) {
+ mPendingDestroySurfaces.add(surface);
+ }
+
+ @Override
+ public Builder makeAnimationLeash() {
return new SurfaceControl.Builder(mSession) {
@Override
@@ -101,12 +204,12 @@
}
@Override
- public SurfaceControl getSurface() {
+ public SurfaceControl getSurfaceControl() {
return mSurface;
}
@Override
- public SurfaceControl getParentSurface() {
+ public SurfaceControl getParentSurfaceControl() {
return mParent;
}
@@ -119,99 +222,9 @@
public int getSurfaceHeight() {
return 1;
}
- };
- private final Runnable mFinishedCallback = () -> {
- mFinishedCallbackCalled = true;
- };
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
- mParent = sWm.makeSurfaceBuilder(mSession)
- .setName("test surface parent")
- .setSize(3000, 3000)
- .build();
- mSurface = sWm.makeSurfaceBuilder(mSession)
- .setName("test surface")
- .setSize(1, 1)
- .build();
- mFinishedCallbackCalled = false;
- mLeash = null;
- mSurfaceAnimator = new SurfaceAnimator(mAnimatable, mFinishedCallback, sWm);
- }
-
- @Test
- public void testRunAnimation() throws Exception {
- mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
- final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
- OnAnimationFinishedCallback.class);
-
- assertTrue(mSurfaceAnimator.isAnimating());
- assertNotNull(mSurfaceAnimator.getAnimation());
- verify(mTransaction).reparent(eq(mSurface), eq(mLeash.getHandle()));
- verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
-
- callbackCaptor.getValue().onAnimationFinished(mSpec);
- assertFalse(mSurfaceAnimator.isAnimating());
- assertNull(mSurfaceAnimator.getAnimation());
- assertTrue(mFinishedCallbackCalled);
-
- // TODO: Verify reparenting once we use mPendingTransaction to reparent it back
- }
-
- @Test
- public void testOverrideAnimation() throws Exception {
- mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
- mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */);
-
- assertFalse(mFinishedCallbackCalled);
-
- final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
- OnAnimationFinishedCallback.class);
- assertTrue(mSurfaceAnimator.isAnimating());
- assertNotNull(mSurfaceAnimator.getAnimation());
- verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
-
- // First animation was finished, but this shouldn't cancel the second animation
- callbackCaptor.getValue().onAnimationFinished(mSpec);
- assertTrue(mSurfaceAnimator.isAnimating());
-
- // Second animation was finished
- verify(mSpec2).startAnimation(any(), any(), callbackCaptor.capture());
- callbackCaptor.getValue().onAnimationFinished(mSpec2);
- assertFalse(mSurfaceAnimator.isAnimating());
- assertTrue(mFinishedCallbackCalled);
- }
-
- @Test
- public void testCancelAnimation() throws Exception {
- mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
- assertTrue(mSurfaceAnimator.isAnimating());
- mSurfaceAnimator.cancelAnimation();
- assertFalse(mSurfaceAnimator.isAnimating());
- verify(mSpec).onAnimationCancelled(any());
- assertTrue(mFinishedCallbackCalled);
- }
-
- @Test
- public void testDelayingAnimationStart() throws Exception {
- mSurfaceAnimator.startDelayingAnimationStart();
- mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
- verifyZeroInteractions(mSpec);
- assertTrue(mSurfaceAnimator.isAnimating());
- mSurfaceAnimator.endDelayingAnimationStart();
- verify(mSpec).startAnimation(any(), any(), any());
- }
-
- @Test
- public void testDelayingAnimationStartAndCancelled() throws Exception {
- mSurfaceAnimator.startDelayingAnimationStart();
- mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
- mSurfaceAnimator.cancelAnimation();
- verifyZeroInteractions(mSpec);
- assertFalse(mSurfaceAnimator.isAnimating());
- assertTrue(mFinishedCallbackCalled);
+ private final Runnable mFinishedCallback = () -> {
+ mFinishedCallbackCalled = true;
+ };
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 89447a9..6070516 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -33,6 +33,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+
/**
* Tests for the {@link TaskPositioningController} class.
*
@@ -78,9 +79,9 @@
assertNotNull(mTarget.getDragWindowHandleLocked());
}
- assertTrue(sWm.mH.runWithScissors(() -> {
- mTarget.finishPositioning();
- }, TIMEOUT_MS));
+ mTarget.finishTaskPositioning();
+ // Wait until the looper processes finishTaskPositioning.
+ assertTrue(sWm.mH.runWithScissors(() -> {}, TIMEOUT_MS));
assertFalse(mTarget.isPositioningLocked());
assertNull(mTarget.getDragWindowHandleLocked());
@@ -99,15 +100,17 @@
assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow());
mTarget.handleTapOutsideTask(content, 0, 0);
+ // Wait until the looper processes finishTaskPositioning.
+ assertTrue(sWm.mH.runWithScissors(() -> {}, TIMEOUT_MS));
synchronized (sWm.mWindowMap) {
assertTrue(mTarget.isPositioningLocked());
assertNotNull(mTarget.getDragWindowHandleLocked());
}
- assertTrue(sWm.mH.runWithScissors(() -> {
- mTarget.finishPositioning();
- }, TIMEOUT_MS));
+ mTarget.finishTaskPositioning();
+ // Wait until the looper processes finishTaskPositioning.
+ assertTrue(sWm.mH.runWithScissors(() -> {}, TIMEOUT_MS));
assertFalse(mTarget.isPositioningLocked());
assertNull(mTarget.getDragWindowHandleLocked());
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 481c898..c735341 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -411,6 +411,11 @@
}
@Override
+ public boolean inKeyguardRestrictedKeyInputMode() {
+ return false;
+ }
+
+ @Override
public void dismissKeyguardLw(@Nullable IKeyguardDismissCallback callback) {
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index 5cb9467..307deb4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -29,8 +29,6 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -333,12 +331,19 @@
final TestWindowContainer child12 = child1.addChildWindow(builder.setIsAnimating(true));
final TestWindowContainer child21 = child2.addChildWindow();
- assertTrue(root.isAnimating());
+ assertFalse(root.isAnimating());
assertTrue(child1.isAnimating());
- assertFalse(child11.isAnimating());
+ assertTrue(child11.isAnimating());
assertTrue(child12.isAnimating());
assertFalse(child2.isAnimating());
assertFalse(child21.isAnimating());
+
+ assertTrue(root.isSelfOrChildAnimating());
+ assertTrue(child1.isSelfOrChildAnimating());
+ assertFalse(child11.isSelfOrChildAnimating());
+ assertTrue(child12.isSelfOrChildAnimating());
+ assertFalse(child2.isSelfOrChildAnimating());
+ assertFalse(child21.isSelfOrChildAnimating());
}
@Test
@@ -630,8 +635,8 @@
}
@Override
- boolean isAnimating() {
- return mIsAnimating || super.isAnimating();
+ boolean isSelfAnimating() {
+ return mIsAnimating;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 337fd50..0959df2 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -451,9 +451,9 @@
private DisplayCutout createDisplayCutoutFromRect(int left, int top, int right, int bottom) {
return DisplayCutout.fromBoundingPolygon(Arrays.asList(
new Point(left, top),
- new Point (left, bottom),
- new Point (right, bottom),
- new Point (left, bottom)
+ new Point(left, bottom),
+ new Point(right, bottom),
+ new Point(right, top)
));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index b2334e8..5f58744 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -189,7 +189,6 @@
public static class TestTask extends Task {
boolean mShouldDeferRemoval = false;
boolean mOnDisplayChangedCalled = false;
- private boolean mUseLocalIsAnimating = false;
private boolean mIsAnimating = false;
TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service,
@@ -214,12 +213,11 @@
}
@Override
- boolean isAnimating() {
- return mUseLocalIsAnimating ? mIsAnimating : super.isAnimating();
+ boolean isSelfAnimating() {
+ return mIsAnimating;
}
void setLocalIsAnimating(boolean isAnimating) {
- mUseLocalIsAnimating = true;
mIsAnimating = isAnimating;
}
}
diff --git a/services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/ConnTestActivity.java b/services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/ConnTestActivity.java
index b26cc7fd..f8d1d03 100644
--- a/services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/ConnTestActivity.java
+++ b/services/tests/servicestests/test-apps/ConnTestApp/src/com/android/servicestests/apps/conntestapp/ConnTestActivity.java
@@ -77,6 +77,7 @@
Log.i(TAG, "onStop called");
if (finishCommandReceiver != null) {
unregisterReceiver(finishCommandReceiver);
+ finishCommandReceiver = null;
}
super.onStop();
}
diff --git a/services/tests/shortcutmanagerutils/Android.mk b/services/tests/shortcutmanagerutils/Android.mk
index 0848fd5..019bcbd3 100644
--- a/services/tests/shortcutmanagerutils/Android.mk
+++ b/services/tests/shortcutmanagerutils/Android.mk
@@ -21,7 +21,6 @@
LOCAL_JAVA_LIBRARIES := \
mockito-target \
- legacy-android-test \
android.test.runner.stubs
LOCAL_MODULE_TAGS := optional
diff --git a/services/tests/uiservicestests/Android.mk b/services/tests/uiservicestests/Android.mk
index 40e7878..d8e14ec 100644
--- a/services/tests/uiservicestests/Android.mk
+++ b/services/tests/uiservicestests/Android.mk
@@ -10,7 +10,8 @@
# Include test java files and source from notifications package.
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
- $(call all-java-files-under, ../../core/java/com/android/server/notification)
+ $(call all-java-files-under, ../../core/java/com/android/server/notification) \
+ $(call all-java-files-under, ../../core/java/com/android/server/slice) \
LOCAL_STATIC_JAVA_LIBRARIES := \
frameworks-base-testutils \
diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
index 621b457..f022dcf 100644
--- a/services/tests/uiservicestests/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -28,6 +28,9 @@
<application>
<uses-library android:name="android.test.runner" />
+
+ <provider android:name=".DummyProvider"
+ android:authorities="com.android.services.uitests" />
</application>
<instrumentation
diff --git a/services/tests/uiservicestests/src/com/android/frameworks/tests/uiservices/DummyProvider.java b/services/tests/uiservicestests/src/com/android/frameworks/tests/uiservices/DummyProvider.java
new file mode 100644
index 0000000..574c226
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/frameworks/tests/uiservices/DummyProvider.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.frameworks.tests.uiservices;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+public class DummyProvider extends ContentProvider {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @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 Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
new file mode 100644
index 0000000..f534b5c
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
@@ -0,0 +1,38 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.testing.TestableContext;
+
+import org.junit.Before;
+import org.junit.Rule;
+
+
+public class UiServiceTestCase {
+ @Rule
+ public final TestableContext mContext =
+ new TestableContext(InstrumentationRegistry.getContext(), null);
+
+ protected TestableContext getContext() {
+ return mContext;
+ }
+
+ @Before
+ public void setup() {
+ // Share classloader to allow package access.
+ System.setProperty("dexmaker.share_classloader", "true");
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/AlertRateLimiterTest.java b/services/tests/uiservicestests/src/com/android/server/notification/AlertRateLimiterTest.java
index faf6a9b..d4c41e0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/AlertRateLimiterTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/AlertRateLimiterTest.java
@@ -22,13 +22,16 @@
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.UiServiceTestCase;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class AlertRateLimiterTest extends NotificationTestCase {
+public class AlertRateLimiterTest extends UiServiceTestCase {
private long mTestStartTime;
private
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
index 262516d..142041a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
@@ -27,13 +27,13 @@
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationChannel;
-import android.app.NotificationManager;
import android.os.UserHandle;
-import android.provider.Settings.Secure;
import android.service.notification.StatusBarNotification;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.server.UiServiceTestCase;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,7 +42,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class BadgeExtractorTest extends NotificationTestCase {
+public class BadgeExtractorTest extends UiServiceTestCase {
@Mock RankingConfig mConfig;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 0b4d61f..a92f7e7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -58,13 +58,13 @@
import android.service.notification.StatusBarNotification;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Slog;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
import com.android.internal.util.IntPair;
+import com.android.server.UiServiceTestCase;
import com.android.server.lights.Light;
import org.junit.Before;
@@ -74,12 +74,10 @@
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class BuzzBeepBlinkTest extends NotificationTestCase {
+public class BuzzBeepBlinkTest extends UiServiceTestCase {
@Mock AudioManager mAudioManager;
@Mock Vibrator mVibrator;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GlobalSortKeyComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
index f92bd84..97f2104 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
@@ -28,6 +28,8 @@
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.server.UiServiceTestCase;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,7 +39,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class GlobalSortKeyComparatorTest extends NotificationTestCase {
+public class GlobalSortKeyComparatorTest extends UiServiceTestCase {
private final String PKG = "PKG";
private final int UID = 1111111;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index f75c648..8d4c5b1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -38,6 +38,8 @@
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.server.UiServiceTestCase;
+
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
@@ -45,7 +47,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class GroupHelperTest extends NotificationTestCase {
+public class GroupHelperTest extends UiServiceTestCase {
private @Mock GroupHelper.Callback mCallback;
private GroupHelper mGroupHelper;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java
index d325e10..73d5961 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java
@@ -30,7 +30,6 @@
import android.test.suitebuilder.annotation.SmallTest;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import static org.mockito.Matchers.anyInt;
@@ -39,9 +38,11 @@
import static org.junit.Assert.assertEquals;
+import com.android.server.UiServiceTestCase;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class ImportanceExtractorTest extends NotificationTestCase {
+public class ImportanceExtractorTest extends UiServiceTestCase {
@Mock RankingConfig mConfig;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index a4b9b25..9ef0ec7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -49,6 +49,7 @@
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
+import com.android.server.UiServiceTestCase;
import com.google.android.collect.Lists;
@@ -68,7 +69,7 @@
import java.util.ArrayList;
import java.util.List;
-public class ManagedServicesTest extends NotificationTestCase {
+public class ManagedServicesTest extends UiServiceTestCase {
@Mock
private IPackageManager mIpm;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
index e527644..fd674f0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
@@ -31,12 +31,14 @@
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
+import com.android.server.UiServiceTestCase;
+
import org.junit.Test;
import java.util.ArrayList;
import java.util.Objects;
-public class NotificationAdjustmentExtractorTest extends NotificationTestCase {
+public class NotificationAdjustmentExtractorTest extends UiServiceTestCase {
@Test
public void testExtractsAdjustment() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
index d75213c..eb45960 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -16,7 +16,6 @@
package com.android.server.notification;
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
@@ -31,17 +30,17 @@
import android.app.Notification;
import android.app.NotificationChannel;
-import android.app.PendingIntent;
-import android.content.Intent;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
+import com.android.server.UiServiceTestCase;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-public class NotificationChannelExtractorTest extends NotificationTestCase {
+public class NotificationChannelExtractorTest extends UiServiceTestCase {
@Mock RankingConfig mConfig;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
index f457f6a..2241047 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
@@ -26,6 +26,7 @@
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.util.FastXmlSerializer;
+import com.android.server.UiServiceTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,7 +37,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class NotificationChannelTest extends NotificationTestCase {
+public class NotificationChannelTest extends UiServiceTestCase {
@Test
public void testWriteToParcel() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index 1e5f96f..3dcd5b9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -38,6 +38,8 @@
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.server.UiServiceTestCase;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -50,7 +52,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class NotificationComparatorTest extends NotificationTestCase {
+public class NotificationComparatorTest extends UiServiceTestCase {
@Mock Context mContext;
@Mock TelecomManager mTm;
@Mock RankingHandler handler;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
index 85852f9..00d93de 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
@@ -32,9 +32,11 @@
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
+import com.android.server.UiServiceTestCase;
+
import org.junit.Test;
-public class NotificationIntrusivenessExtractorTest extends NotificationTestCase {
+public class NotificationIntrusivenessExtractorTest extends UiServiceTestCase {
@Test
public void testNonIntrusive() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index d767ba2..f4313b8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -37,6 +37,8 @@
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.server.UiServiceTestCase;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,7 +47,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class NotificationListenerServiceTest extends NotificationTestCase {
+public class NotificationListenerServiceTest extends UiServiceTestCase {
private String[] mKeys = new String[] { "key", "key1", "key2", "key3"};
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 55ec133..ad3fecf 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -83,6 +83,7 @@
import android.util.AtomicFile;
import com.android.internal.statusbar.NotificationVisibility;
+import com.android.server.UiServiceTestCase;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
@@ -92,7 +93,9 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.*;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
import java.io.BufferedInputStream;
@@ -108,7 +111,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
-public class NotificationManagerServiceTest extends NotificationTestCase {
+public class NotificationManagerServiceTest extends UiServiceTestCase {
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
private final int mUid = Binder.getCallingUid();
private NotificationManagerService mService;
@@ -873,6 +876,77 @@
}
@Test
+ public void testCancelAllCancelNotificationsFromListener_ForegroundServiceFlag()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllCancelNotificationsFromListener_ForegroundServiceFlagWithParameter()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ String[] keys = {parent.sbn.getKey(), child.sbn.getKey(),
+ child2.sbn.getKey(), newGroup.sbn.getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testUserInitiatedCancelAllWithGroup_ForegroundServiceFlag() throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+ parent.getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
public void testFindGroupNotificationsLocked() throws Exception {
// make sure the same notification can be found in both lists and returned
final NotificationRecord group1 = generateNotificationRecord(
@@ -912,6 +986,157 @@
}
@Test
+ public void testCancelAllNotifications_CancelsNoClearFlagOnGoing() throws Exception {
+ final NotificationRecord notif = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ notif.getNotification().flags |= Notification.FLAG_NO_CLEAR;
+ mService.addNotification(notif);
+ mService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0,
+ Notification.FLAG_ONGOING_EVENT, true, notif.getUserId(), 0, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllCancelNotificationsFromListener_NoClearFlagWithParameter()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ String[] keys = {parent.sbn.getKey(), child.sbn.getKey(),
+ child2.sbn.getKey(), newGroup.sbn.getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testAppInitiatedCancelAllNotifications_CancelsOnGoingFlag() throws Exception {
+ final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(sbn.getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllNotifications_CancelsOnGoingFlag() throws Exception {
+ final NotificationRecord notif = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ notif.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ mService.addNotification(notif);
+ mService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, 0, true,
+ notif.getUserId(), 0, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testUserInitiatedCancelAllOnClearAll_OnGoingFlag() throws Exception {
+ final NotificationRecord notif = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ notif.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ mService.addNotification(notif);
+
+ mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+ notif.getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllCancelNotificationsFromListener_OnGoingFlag() throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testCancelAllCancelNotificationsFromListener_OnGoingFlagWithParameter()
+ throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ String[] keys = {parent.sbn.getKey(), child.sbn.getKey(),
+ child2.sbn.getKey(), newGroup.sbn.getKey()};
+ mService.getBinderService().cancelNotificationsFromListener(null, keys);
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ assertEquals(0, notifs.length);
+ }
+
+ @Test
+ public void testUserInitiatedCancelAllWithGroup_OnGoingFlag() throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false);
+ final NotificationRecord child2 = generateNotificationRecord(
+ mTestNotificationChannel, 3, "group", false);
+ child2.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ final NotificationRecord newGroup = generateNotificationRecord(
+ mTestNotificationChannel, 4, "group2", false);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+ parent.getUserId());
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
public void testTvExtenderChannelOverride_onTv() throws Exception {
mService.setIsTelevision(true);
mService.setRankingHelper(mRankingHelper);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index ef26705a..a5fa903 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -53,6 +53,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.server.UiServiceTestCase;
import org.junit.Before;
import org.junit.Test;
@@ -65,7 +66,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class NotificationRecordTest extends NotificationTestCase {
+public class NotificationRecordTest extends UiServiceTestCase {
private final Context mMockContext = Mockito.mock(Context.class);
@Mock PackageManager mPm;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
index fec2811..4f153ee 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
@@ -11,12 +11,14 @@
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.server.UiServiceTestCase;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class NotificationStatsTest extends NotificationTestCase {
+public class NotificationStatsTest extends UiServiceTestCase {
@Test
public void testConstructor() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
index 4165e9e..4bfb236 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
@@ -33,6 +33,8 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import com.android.server.UiServiceTestCase;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -41,7 +43,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class NotificationTest extends NotificationTestCase {
+public class NotificationTest extends UiServiceTestCase {
@Mock
ActivityManager mAm;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTestCase.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTestCase.java
deleted file mode 100644
index 1ee3412..0000000
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTestCase.java
+++ /dev/null
@@ -1,33 +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.notification;
-
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.testing.TestableContext;
-
-import org.junit.Rule;
-
-
-public class NotificationTestCase {
- @Rule
- public final TestableContext mContext =
- new TestableContext(InstrumentationRegistry.getContext(), null);
-
- protected TestableContext getContext() {
- return mContext;
- }
-}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 2d03f11..abfc54d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -65,6 +65,7 @@
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
+import com.android.server.UiServiceTestCase;
import org.json.JSONArray;
import org.json.JSONObject;
@@ -90,7 +91,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class RankingHelperTest extends NotificationTestCase {
+public class RankingHelperTest extends UiServiceTestCase {
private static final String PKG = "com.android.server.notification";
private static final int UID = 0;
private static final UserHandle USER = UserHandle.of(0);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
index e354267..5d8d48f1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
@@ -24,9 +24,11 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
+import com.android.server.UiServiceTestCase;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class RateEstimatorTest extends NotificationTestCase {
+public class RateEstimatorTest extends UiServiceTestCase {
private long mTestStartTime;
private RateEstimator mEstimator;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
index 5ebfd48..9564ab9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -27,6 +27,8 @@
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.server.UiServiceTestCase;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,7 +38,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class ScheduleCalendarTest extends NotificationTestCase {
+public class ScheduleCalendarTest extends UiServiceTestCase {
private ScheduleCalendar mScheduleCalendar;
private ZenModeConfig.ScheduleInfo mScheduleInfo;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
index 610592f..17fed83 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
@@ -13,6 +13,8 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import com.android.server.UiServiceTestCase;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -23,7 +25,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class ScheduleConditionProviderTest extends NotificationTestCase {
+public class ScheduleConditionProviderTest extends UiServiceTestCase {
ScheduleConditionProvider mService;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 07b21fb..88c6fcf 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -32,7 +32,6 @@
import android.service.notification.StatusBarNotification;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Slog;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -46,10 +45,12 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import com.android.server.UiServiceTestCase;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class SnoozeHelperTest extends NotificationTestCase {
+public class SnoozeHelperTest extends UiServiceTestCase {
private static final String TEST_CHANNEL_ID = "test_channel_id";
@Mock SnoozeHelper.Callback mCallback;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
index 4ac0c65..58f0ded 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
@@ -30,9 +30,11 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertEquals;
+import com.android.server.UiServiceTestCase;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class ValidateNotificationPeopleTest extends NotificationTestCase {
+public class ValidateNotificationPeopleTest extends UiServiceTestCase {
@Test
public void testNoExtra() throws Exception {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 8ac6481..0c7397a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -32,6 +32,8 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import com.android.server.UiServiceTestCase;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -41,7 +43,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class ZenModeHelperTest extends NotificationTestCase {
+public class ZenModeHelperTest extends UiServiceTestCase {
@Mock ConditionProviders mConditionProviders;
private TestableLooper mTestableLooper;
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
new file mode 100644
index 0000000..ce328c2
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -0,0 +1,214 @@
+package com.android.server.slice;
+
+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 static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.slice.ISliceListener;
+import android.app.slice.Slice;
+import android.app.slice.SliceProvider;
+import android.app.slice.SliceSpec;
+import android.content.ContentProvider;
+import android.content.IContentProvider;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class PinnedSliceStateTest extends UiServiceTestCase {
+
+ private static final String AUTH = "my.authority";
+ private static final Uri TEST_URI = Uri.parse("content://" + AUTH + "/path");
+
+ private static final SliceSpec[] FIRST_SPECS = new SliceSpec[]{
+ new SliceSpec("spec1", 3),
+ new SliceSpec("spec2", 3),
+ new SliceSpec("spec3", 2),
+ new SliceSpec("spec4", 1),
+ };
+
+ private static final SliceSpec[] SECOND_SPECS = new SliceSpec[]{
+ new SliceSpec("spec2", 1),
+ new SliceSpec("spec3", 2),
+ new SliceSpec("spec4", 3),
+ new SliceSpec("spec5", 4),
+ };
+
+ private SliceManagerService mSliceService;
+ private PinnedSliceState mPinnedSliceManager;
+ private IContentProvider mIContentProvider;
+ private ContentProvider mContentProvider;
+
+ @Before
+ public void setup() {
+ mSliceService = mock(SliceManagerService.class);
+ when(mSliceService.getLock()).thenReturn(new Object());
+ when(mSliceService.getContext()).thenReturn(mContext);
+ when(mSliceService.getHandler()).thenReturn(new Handler(TestableLooper.get(this).getLooper()));
+ mContentProvider = mock(ContentProvider.class);
+ mIContentProvider = mock(IContentProvider.class);
+ when(mContentProvider.getIContentProvider()).thenReturn(mIContentProvider);
+ mContext.getContentResolver().addProvider(AUTH, mContentProvider);
+ mPinnedSliceManager = new PinnedSliceState(mSliceService, TEST_URI);
+ }
+
+ @Test
+ public void testMergeSpecs() {
+ // No annotations to start.
+ assertNull(mPinnedSliceManager.getSpecs());
+
+ mPinnedSliceManager.mergeSpecs(FIRST_SPECS);
+ assertArrayEquals(FIRST_SPECS, mPinnedSliceManager.getSpecs());
+
+ mPinnedSliceManager.mergeSpecs(SECOND_SPECS);
+ assertArrayEquals(new SliceSpec[]{
+ // spec1 is gone because it's not in the second set.
+ new SliceSpec("spec2", 1), // spec2 is 1 because it's smaller in the second set.
+ new SliceSpec("spec3", 2), // spec3 is the same in both sets
+ new SliceSpec("spec4", 1), // spec4 is 1 because it's smaller in the first set.
+ // spec5 is gone because it's not in the first set.
+ }, mPinnedSliceManager.getSpecs());
+ }
+
+ @Test
+ public void testSendPinnedOnCreate() throws RemoteException {
+ // When created, a pinned message should be sent.
+ TestableLooper.get(this).processAllMessages();
+
+ verify(mIContentProvider).call(anyString(), eq(SliceProvider.METHOD_PIN), eq(null),
+ argThat(b -> {
+ assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
+ return true;
+ }));
+ }
+
+ @Test
+ public void testSendUnpinnedOnDestroy() throws RemoteException {
+ TestableLooper.get(this).processAllMessages();
+ clearInvocations(mIContentProvider);
+
+ mPinnedSliceManager.destroy();
+ TestableLooper.get(this).processAllMessages();
+
+ verify(mIContentProvider).call(anyString(), eq(SliceProvider.METHOD_UNPIN), eq(null),
+ argThat(b -> {
+ assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
+ return true;
+ }));
+ }
+
+ @Test
+ public void testPkgPin() {
+ assertFalse(mPinnedSliceManager.isPinned());
+
+ mPinnedSliceManager.pin("pkg", FIRST_SPECS);
+ assertTrue(mPinnedSliceManager.isPinned());
+
+ assertTrue(mPinnedSliceManager.unpin("pkg"));
+ assertFalse(mPinnedSliceManager.isPinned());
+ }
+
+ @Test
+ public void testMultiPkgPin() {
+ assertFalse(mPinnedSliceManager.isPinned());
+
+ mPinnedSliceManager.pin("pkg", FIRST_SPECS);
+ assertTrue(mPinnedSliceManager.isPinned());
+ mPinnedSliceManager.pin("pkg2", FIRST_SPECS);
+
+ assertFalse(mPinnedSliceManager.unpin("pkg"));
+ assertTrue(mPinnedSliceManager.unpin("pkg2"));
+ assertFalse(mPinnedSliceManager.isPinned());
+ }
+
+ @Test
+ public void testListenerPin() {
+ ISliceListener listener = mock(ISliceListener.class);
+ assertFalse(mPinnedSliceManager.isPinned());
+
+ mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS);
+ assertTrue(mPinnedSliceManager.isPinned());
+
+ assertTrue(mPinnedSliceManager.removeSliceListener(listener));
+ assertFalse(mPinnedSliceManager.isPinned());
+ }
+
+ @Test
+ public void testMultiListenerPin() {
+ ISliceListener listener = mock(ISliceListener.class);
+ ISliceListener listener2 = mock(ISliceListener.class);
+ assertFalse(mPinnedSliceManager.isPinned());
+
+ mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS);
+ assertTrue(mPinnedSliceManager.isPinned());
+ mPinnedSliceManager.addSliceListener(listener2, FIRST_SPECS);
+
+ assertFalse(mPinnedSliceManager.removeSliceListener(listener));
+ assertTrue(mPinnedSliceManager.removeSliceListener(listener2));
+ assertFalse(mPinnedSliceManager.isPinned());
+ }
+
+ @Test
+ public void testPkgListenerPin() {
+ ISliceListener listener = mock(ISliceListener.class);
+ assertFalse(mPinnedSliceManager.isPinned());
+
+ mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS);
+ assertTrue(mPinnedSliceManager.isPinned());
+ mPinnedSliceManager.pin("pkg", FIRST_SPECS);
+
+ assertFalse(mPinnedSliceManager.removeSliceListener(listener));
+ assertTrue(mPinnedSliceManager.unpin("pkg"));
+ assertFalse(mPinnedSliceManager.isPinned());
+ }
+
+ @Test
+ public void testBind() throws RemoteException {
+ TestableLooper.get(this).processAllMessages();
+ clearInvocations(mIContentProvider);
+
+ ISliceListener listener = mock(ISliceListener.class);
+ Slice s = new Slice.Builder(TEST_URI).build();
+ Bundle b = new Bundle();
+ b.putParcelable(SliceProvider.EXTRA_SLICE, s);
+ when(mIContentProvider.call(anyString(), eq(SliceProvider.METHOD_SLICE), eq(null),
+ any())).thenReturn(b);
+
+ assertFalse(mPinnedSliceManager.isPinned());
+
+ mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS);
+
+ mPinnedSliceManager.onChange();
+ TestableLooper.get(this).processAllMessages();
+
+ verify(mIContentProvider).call(anyString(), eq(SliceProvider.METHOD_SLICE), eq(null),
+ argThat(bundle -> {
+ assertEquals(TEST_URI, bundle.getParcelable(SliceProvider.EXTRA_BIND_URI));
+ return true;
+ }));
+ verify(listener).onSliceUpdated(eq(s));
+ }
+}
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
new file mode 100644
index 0000000..fe9ea7a
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.slice;
+
+import static android.content.ContentProvider.maybeAddUserId;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+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 static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.app.slice.ISliceListener;
+import android.app.slice.SliceSpec;
+import android.content.pm.PackageManagerInternal;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.server.LocalServices;
+import com.android.server.UiServiceTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class SliceManagerServiceTest extends UiServiceTestCase {
+
+ private static final String AUTH = "com.android.services.uitests";
+ private static final Uri TEST_URI = maybeAddUserId(Uri.parse("content://" + AUTH + "/path"), 0);
+
+ private static final SliceSpec[] EMPTY_SPECS = new SliceSpec[]{
+ };
+
+ private SliceManagerService mService;
+ private PinnedSliceState mCreatedSliceState;
+
+ @Before
+ public void setup() {
+ LocalServices.addService(PackageManagerInternal.class, mock(PackageManagerInternal.class));
+ mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
+ mContext.getTestablePermissions().setPermission(TEST_URI, PERMISSION_GRANTED);
+
+ mService = spy(new SliceManagerService(mContext, TestableLooper.get(this).getLooper()));
+ mCreatedSliceState = mock(PinnedSliceState.class);
+ doReturn(mCreatedSliceState).when(mService).createPinnedSlice(eq(TEST_URI));
+ }
+
+ @After
+ public void teardown() {
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ }
+
+ @Test
+ public void testAddListenerCreatesPinned() throws RemoteException {
+ mService.addSliceListener(TEST_URI, "pkg", mock(ISliceListener.class), EMPTY_SPECS);
+ verify(mService, times(1)).createPinnedSlice(eq(TEST_URI));
+ }
+
+ @Test
+ public void testAddListenerCreatesOnePinned() throws RemoteException {
+ mService.addSliceListener(TEST_URI, "pkg", mock(ISliceListener.class), EMPTY_SPECS);
+ mService.addSliceListener(TEST_URI, "pkg", mock(ISliceListener.class), EMPTY_SPECS);
+ verify(mService, times(1)).createPinnedSlice(eq(TEST_URI));
+ }
+
+ @Test
+ public void testRemoveListenerDestroysPinned() throws RemoteException {
+ ISliceListener listener = mock(ISliceListener.class);
+ mService.addSliceListener(TEST_URI, "pkg", listener, EMPTY_SPECS);
+
+ when(mCreatedSliceState.removeSliceListener(eq(listener))).thenReturn(false);
+ mService.removeSliceListener(TEST_URI, "pkg", listener);
+ verify(mCreatedSliceState, never()).destroy();
+
+ when(mCreatedSliceState.removeSliceListener(eq(listener))).thenReturn(true);
+ mService.removeSliceListener(TEST_URI, "pkg", listener);
+ verify(mCreatedSliceState).destroy();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testUnrecognizedThrows() throws RemoteException {
+ mService.removeSliceListener(TEST_URI, "pkg", mock(ISliceListener.class));
+ }
+
+ @Test
+ public void testAddPinCreatesPinned() throws RemoteException {
+ doReturn("pkg").when(mService).getDefaultHome(anyInt());
+
+ mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS);
+ mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS);
+ verify(mService, times(1)).createPinnedSlice(eq(TEST_URI));
+ }
+
+ @Test
+ public void testRemovePinDestroysPinned() throws RemoteException {
+ doReturn("pkg").when(mService).getDefaultHome(anyInt());
+
+ mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS);
+
+ when(mCreatedSliceState.unpin(eq("pkg"))).thenReturn(false);
+ mService.unpinSlice("pkg", TEST_URI);
+ verify(mCreatedSliceState, never()).destroy();
+
+ when(mCreatedSliceState.unpin(eq("pkg"))).thenReturn(true);
+ mService.unpinSlice("pkg", TEST_URI);
+ verify(mCreatedSliceState).destroy();
+ }
+
+}
\ No newline at end of file
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index 6ac4b36..a1f1810 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -16,6 +16,15 @@
package com.android.server.usage;
+import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
+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_NEVER;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+
import android.app.usage.UsageStatsManager;
import android.os.SystemClock;
import android.util.ArrayMap;
@@ -42,6 +51,8 @@
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
/**
* Keeps track of recent active state changes in apps.
@@ -195,14 +206,14 @@
+ (elapsedRealtime - mElapsedSnapshot);
appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
appUsageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
- if (appUsageHistory.currentBucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) {
- appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+ if (appUsageHistory.currentBucket > STANDBY_BUCKET_ACTIVE) {
+ appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
if (DEBUG) {
Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
+ ", reason=" + appUsageHistory.bucketingReason);
}
}
- appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE;
+ appUsageHistory.bucketingReason = REASON_USAGE;
return appUsageHistory.currentBucket;
}
@@ -211,15 +222,15 @@
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
elapsedRealtime, true);
- if (appUsageHistory.currentBucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) {
- appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+ if (appUsageHistory.currentBucket > STANDBY_BUCKET_WORKING_SET) {
+ appUsageHistory.currentBucket = STANDBY_BUCKET_WORKING_SET;
if (DEBUG) {
Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
+ ", reason=" + appUsageHistory.bucketingReason);
}
}
// TODO: Should this be a different reason for partial usage?
- appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE;
+ appUsageHistory.bucketingReason = REASON_USAGE;
return appUsageHistory.currentBucket;
}
@@ -277,8 +288,8 @@
appUsageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime);
appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
appUsageHistory.lastPredictedTime = getElapsedTime(0);
- appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_NEVER;
- appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT;
+ appUsageHistory.currentBucket = STANDBY_BUCKET_NEVER;
+ appUsageHistory.bucketingReason = REASON_DEFAULT;
appUsageHistory.lastInformedBucket = -1;
userHistory.put(packageName, appUsageHistory);
}
@@ -296,7 +307,7 @@
if (appUsageHistory == null) {
return false; // Default to not idle
} else {
- return appUsageHistory.currentBucket >= UsageStatsManager.STANDBY_BUCKET_RARE;
+ return appUsageHistory.currentBucket >= STANDBY_BUCKET_RARE;
// Whether or not it's passed will now be externally calculated and the
// bucket will be pushed to the history using setAppStandbyBucket()
//return hasPassedThresholds(appUsageHistory, elapsedRealtime);
@@ -318,7 +329,7 @@
getPackageHistory(userHistory, packageName, elapsedRealtime, true);
appUsageHistory.currentBucket = bucket;
appUsageHistory.bucketingReason = reason;
- if (reason.startsWith(UsageStatsManager.REASON_PREDICTED)) {
+ if (reason.startsWith(REASON_PREDICTED)) {
appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime);
}
if (DEBUG) {
@@ -334,6 +345,18 @@
return appUsageHistory.currentBucket;
}
+ public Map<String, Integer> getAppStandbyBuckets(int userId, long elapsedRealtime,
+ boolean appIdleEnabled) {
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ int size = userHistory.size();
+ HashMap<String, Integer> buckets = new HashMap<>(size);
+ for (int i = 0; i < size; i++) {
+ buckets.put(userHistory.keyAt(i),
+ appIdleEnabled ? userHistory.valueAt(i).currentBucket : STANDBY_BUCKET_ACTIVE);
+ }
+ return buckets;
+ }
+
public String getAppStandbyReason(String packageName, int userId, long elapsedRealtime) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
@@ -351,12 +374,12 @@
AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
elapsedRealtime, true);
if (idle) {
- appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_RARE;
- appUsageHistory.bucketingReason = UsageStatsManager.REASON_FORCED;
+ appUsageHistory.currentBucket = STANDBY_BUCKET_RARE;
+ appUsageHistory.bucketingReason = REASON_FORCED;
} else {
- appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+ appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
// This is to pretend that the app was just used, don't freeze the state anymore.
- appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE;
+ appUsageHistory.bucketingReason = REASON_USAGE;
}
return appUsageHistory.currentBucket;
}
@@ -459,12 +482,12 @@
String currentBucketString = parser.getAttributeValue(null,
ATTR_CURRENT_BUCKET);
appUsageHistory.currentBucket = currentBucketString == null
- ? UsageStatsManager.STANDBY_BUCKET_ACTIVE
+ ? STANDBY_BUCKET_ACTIVE
: Integer.parseInt(currentBucketString);
appUsageHistory.bucketingReason =
parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
if (appUsageHistory.bucketingReason == null) {
- appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT;
+ appUsageHistory.bucketingReason = REASON_DEFAULT;
}
appUsageHistory.lastInformedBucket = -1;
userHistory.put(packageName, appUsageHistory);
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index d8086bb..9b588fa 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -24,6 +24,7 @@
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;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
@@ -84,6 +85,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
/**
* Manages the standby state of an app, listening to various events.
@@ -132,7 +134,7 @@
@GuardedBy("mAppIdleLock")
private AppIdleHistory mAppIdleHistory;
- @GuardedBy("mAppIdleLock")
+ @GuardedBy("mPackageAccessListeners")
private ArrayList<AppIdleStateChangeListener>
mPackageAccessListeners = new ArrayList<>();
@@ -160,12 +162,14 @@
long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS;
long[] mAppStandbyElapsedThresholds = ELAPSED_TIME_THRESHOLDS;
- boolean mAppIdleEnabled;
+ volatile boolean mAppIdleEnabled;
boolean mAppIdleTempParoled;
boolean mCharging;
private long mLastAppIdleParoledTime;
private boolean mSystemServicesReady = false;
+ private final DeviceStateReceiver mDeviceStateReceiver;
+
private volatile boolean mPendingOneTimeCheckIdleStates;
private final AppStandbyHandler mHandler;
@@ -176,7 +180,7 @@
private AppWidgetManager mAppWidgetManager;
private PowerManager mPowerManager;
private PackageManager mPackageManager;
- private Injector mInjector;
+ Injector mInjector;
AppStandbyController(Context context, Looper looper) {
@@ -188,14 +192,13 @@
mContext = mInjector.getContext();
mHandler = new AppStandbyHandler(mInjector.getLooper());
mPackageManager = mContext.getPackageManager();
- mAppIdleEnabled = mInjector.isAppIdleEnabled();
+ mDeviceStateReceiver = new DeviceStateReceiver();
- if (mAppIdleEnabled) {
- IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
- deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
- deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
- mContext.registerReceiver(new DeviceStateReceiver(), deviceStates);
- }
+ IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
+ deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+ mContext.registerReceiver(mDeviceStateReceiver, deviceStates);
+
synchronized (mAppIdleLock) {
mAppIdleHistory = new AppIdleHistory(mInjector.getDataSystemDirectory(),
mInjector.elapsedRealtime());
@@ -211,9 +214,14 @@
null, mHandler);
}
+ void setAppIdleEnabled(boolean enabled) {
+ mAppIdleEnabled = enabled;
+ }
+
public void onBootPhase(int phase) {
mInjector.onBootPhase(phase);
if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ setAppIdleEnabled(mInjector.isAppIdleEnabled());
// Observe changes to the threshold
SettingsObserver settingsObserver = new SettingsObserver(mHandler);
settingsObserver.registerObserver();
@@ -238,6 +246,8 @@
}
void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
+ if (!mAppIdleEnabled) return;
+
// Get sync adapters for the authority
String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
authority, userId);
@@ -293,6 +303,7 @@
}
boolean isParoledOrCharging() {
+ if (!mAppIdleEnabled) return true;
synchronized (mAppIdleLock) {
return mAppIdleTempParoled || mCharging;
}
@@ -518,6 +529,7 @@
}
void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId) {
+ if (!mAppIdleEnabled) return;
synchronized (mAppIdleLock) {
// TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
// about apps that are on some kind of whitelist anyway.
@@ -557,6 +569,8 @@
* required.
*/
void forceIdleState(String packageName, int userId, boolean idle) {
+ if (!mAppIdleEnabled) return;
+
final int appId = getAppId(packageName);
if (appId < 0) return;
final long elapsedRealtime = mInjector.elapsedRealtime();
@@ -591,7 +605,7 @@
}
void addListener(AppIdleStateChangeListener listener) {
- synchronized (mAppIdleLock) {
+ synchronized (mPackageAccessListeners) {
if (!mPackageAccessListeners.contains(listener)) {
mPackageAccessListeners.add(listener);
}
@@ -599,7 +613,7 @@
}
void removeListener(AppIdleStateChangeListener listener) {
- synchronized (mAppIdleLock) {
+ synchronized (mPackageAccessListeners) {
mPackageAccessListeners.remove(listener);
}
}
@@ -761,7 +775,7 @@
}
void setAppIdleAsync(String packageName, boolean idle, int userId) {
- if (packageName == null) return;
+ if (packageName == null || !mAppIdleEnabled) return;
mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
.sendToTarget();
@@ -769,18 +783,41 @@
@StandbyBuckets public int getAppStandbyBucket(String packageName, int userId,
long elapsedRealtime, boolean shouldObfuscateInstantApps) {
- if (shouldObfuscateInstantApps &&
- mInjector.isPackageEphemeral(userId, packageName)) {
+ if (!mAppIdleEnabled || (shouldObfuscateInstantApps
+ && mInjector.isPackageEphemeral(userId, packageName))) {
return STANDBY_BUCKET_ACTIVE;
}
- return mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
+ synchronized (mAppIdleLock) {
+ return mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
+ }
+ }
+
+ public Map<String, Integer> getAppStandbyBuckets(int userId, long elapsedRealtime) {
+ synchronized (mAppIdleLock) {
+ return mAppIdleHistory.getAppStandbyBuckets(userId, elapsedRealtime, mAppIdleEnabled);
+ }
}
void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
String reason, long elapsedRealtime) {
- mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
- reason);
+ synchronized (mAppIdleLock) {
+ AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
+ userId, elapsedRealtime);
+ boolean predicted = reason != null && reason.startsWith(REASON_PREDICTED);
+ // Don't allow changing bucket if higher than ACTIVE
+ if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
+ // Don't allow prediction to change from or to NEVER
+ if ((app.currentBucket == STANDBY_BUCKET_NEVER
+ || newBucket == STANDBY_BUCKET_NEVER)
+ && predicted) {
+ return;
+ }
+ // If the bucket was forced, don't allow prediction to override
+ if (app.bucketingReason.equals(REASON_FORCED) && predicted) return;
+ mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
+ reason);
+ }
maybeInformListeners(packageName, userId, elapsedRealtime,
newBucket);
}
@@ -841,15 +878,19 @@
void informListeners(String packageName, int userId, int bucket) {
final boolean idle = bucket >= STANDBY_BUCKET_RARE;
- for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
- listener.onAppIdleStateChanged(packageName, userId, idle, bucket);
+ synchronized (mPackageAccessListeners) {
+ for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
+ listener.onAppIdleStateChanged(packageName, userId, idle, bucket);
+ }
}
}
void informParoleStateChanged() {
final boolean paroled = isParoledOrCharging();
- for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
- listener.onParoleStateChanged(paroled);
+ synchronized (mPackageAccessListeners) {
+ for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
+ listener.onParoleStateChanged(paroled);
+ }
}
}
@@ -1029,8 +1070,11 @@
}
boolean isAppIdleEnabled() {
- return mContext.getResources().getBoolean(
+ final boolean buildFlag = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableAutoPowerModes);
+ final boolean runtimeFlag = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.APP_STANDBY_ENABLED, 1) == 1;
+ return buildFlag && runtimeFlag;
}
boolean isCharging() {
@@ -1101,7 +1145,7 @@
break;
case MSG_CHECK_IDLE_STATES:
- if (checkIdleStates(msg.arg1)) {
+ if (checkIdleStates(msg.arg1) && mAppIdleEnabled) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(
MSG_CHECK_IDLE_STATES, msg.arg1, 0),
mCheckIdleIntervalMillis);
@@ -1200,6 +1244,8 @@
void registerObserver() {
mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
Settings.Global.APP_IDLE_CONSTANTS), false, this);
+ mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.APP_STANDBY_ENABLED), false, this);
}
@Override
@@ -1209,15 +1255,27 @@
}
void updateSettings() {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "appidle=" + Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.APP_STANDBY_ENABLED));
+ Slog.d(TAG, "appidleconstants=" + Settings.Global.getString(
+ mContext.getContentResolver(),
+ Settings.Global.APP_IDLE_CONSTANTS));
+ }
+ // Check if app_idle_enabled has changed
+ setAppIdleEnabled(mInjector.isAppIdleEnabled());
+
+ // Look at global settings for this.
+ // TODO: Maybe apply different thresholds for different users.
+ try {
+ mParser.setString(mInjector.getAppIdleSettings());
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
+ // fallthrough, mParser is empty and all defaults will be returned.
+ }
+
synchronized (mAppIdleLock) {
- // Look at global settings for this.
- // TODO: Maybe apply different thresholds for different users.
- try {
- mParser.setString(mInjector.getAppIdleSettings());
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
- // fallthrough, mParser is empty and all defaults will be returned.
- }
// Default: 24 hours between paroles
mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 21b11b0..82f8001 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -164,6 +164,14 @@
}
@Override
+ public boolean isReservedSupported(String volumeUuid, String callingPackage) {
+ enforcePermission(Binder.getCallingUid(), callingPackage);
+
+ // TODO: implement as part of b/62024591
+ return false;
+ }
+
+ @Override
public long getTotalBytes(String volumeUuid, String callingPackage) {
// NOTE: No permissions required
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 0572771..cdce448 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -68,7 +68,10 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
/**
* A service that collects, aggregates, and persists application usage data.
@@ -492,8 +495,8 @@
flushToDiskLocked();
pw.println("Flushed stats to disk");
return;
- } else {
- // Anything else is a pkg to filter
+ } else if (arg != null && !arg.startsWith("-")) {
+ // Anything else that doesn't start with '-' is a pkg to filter
pkg = arg;
break;
}
@@ -725,6 +728,10 @@
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
+ final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
+ final String reason = shellCaller
+ ? UsageStatsManager.REASON_FORCED
+ : UsageStatsManager.REASON_PREDICTED + ":" + callingUid;
final long token = Binder.clearCallingIdentity();
try {
// Caller cannot set their own standby state
@@ -732,8 +739,7 @@
PackageManager.MATCH_ANY_USER, userId) == callingUid) {
throw new IllegalArgumentException("Cannot set your own standby bucket");
}
- mAppStandby.setAppStandbyBucket(packageName, userId, bucket,
- UsageStatsManager.REASON_PREDICTED + ":" + callingUid,
+ mAppStandby.setAppStandbyBucket(packageName, userId, bucket, reason,
SystemClock.elapsedRealtime());
} finally {
Binder.restoreCallingIdentity(token);
@@ -741,6 +747,71 @@
}
@Override
+ public Map getAppStandbyBuckets(String callingPackageName, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ try {
+ userId = ActivityManager.getService().handleIncomingUser(
+ Binder.getCallingPid(), callingUid, userId, false, false,
+ "getAppStandbyBucket", null);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ if (!hasPermission(callingPackageName)) {
+ throw new SecurityException(
+ "Don't have permission to query app standby bucket");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mAppStandby.getAppStandbyBuckets(userId,
+ SystemClock.elapsedRealtime());
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void setAppStandbyBuckets(Map appBuckets, int userId) {
+ getContext().enforceCallingPermission(Manifest.permission.CHANGE_APP_IDLE_STATE,
+ "No permission to change app standby state");
+
+ final int callingUid = Binder.getCallingUid();
+ try {
+ userId = ActivityManager.getService().handleIncomingUser(
+ Binder.getCallingPid(), callingUid, userId, false, true,
+ "setAppStandbyBucket", null);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
+ final String reason = shellCaller
+ ? UsageStatsManager.REASON_FORCED
+ : UsageStatsManager.REASON_PREDICTED + ":" + callingUid;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ Map<String, Integer> buckets = (Map<String, Integer>) appBuckets;
+ for (Map.Entry<String, Integer> entry: buckets.entrySet()) {
+ String packageName = entry.getKey();
+ int bucket = entry.getValue();
+ if (bucket < UsageStatsManager.STANDBY_BUCKET_ACTIVE
+ || bucket > UsageStatsManager.STANDBY_BUCKET_NEVER) {
+ throw new IllegalArgumentException(
+ "Cannot set the standby bucket to " + bucket);
+ }
+ // Caller cannot set their own standby state
+ if (mPackageManagerInternal.getPackageUid(packageName,
+ PackageManager.MATCH_ANY_USER, userId) == callingUid) {
+ throw new IllegalArgumentException("Cannot set your own standby bucket");
+ }
+ mAppStandby.setAppStandbyBucket(packageName, userId, bucket, reason,
+ elapsedRealtime);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void whitelistAppTemporarily(String packageName, long duration, int userId)
throws RemoteException {
StringBuilder reason = new StringBuilder(32);
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index e76d211..7a352a4 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -28,6 +28,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.usb.descriptors.UsbDescriptor;
import com.android.server.usb.descriptors.UsbDescriptorParser;
import com.android.server.usb.descriptors.UsbDeviceDescriptor;
import com.android.server.usb.descriptors.report.TextReportCanvas;
@@ -84,7 +85,10 @@
String mDeviceAddress;
static final int CONNECT = 0;
- static final int DISCONNECT = 1;
+ static final int CONNECT_BADPARSE = 1;
+ static final int CONNECT_BADDEVICE = 2;
+ static final int DISCONNECT = -1;
+
final int mMode;
final byte[] mDescriptors;
@@ -100,8 +104,8 @@
}
void dumpShort(IndentingPrintWriter pw) {
- if (mMode == CONNECT) {
- pw.println(formatTime() + " Connect " + mDeviceAddress);
+ if (mMode != DISCONNECT) {
+ pw.println(formatTime() + " Connect " + mDeviceAddress + " mode:" + mMode);
UsbDescriptorParser parser = new UsbDescriptorParser(mDeviceAddress, mDescriptors);
UsbDeviceDescriptor deviceDescriptor = parser.getDeviceDescriptor();
@@ -115,9 +119,9 @@
}
}
- void dumpLong(IndentingPrintWriter pw) {
- if (mMode == CONNECT) {
- pw.println(formatTime() + " Connect " + mDeviceAddress);
+ void dumpTree(IndentingPrintWriter pw) {
+ if (mMode != DISCONNECT) {
+ pw.println(formatTime() + " Connect " + mDeviceAddress + " mode:" + mMode);
UsbDescriptorParser parser = new UsbDescriptorParser(mDeviceAddress, mDescriptors);
StringBuilder stringBuilder = new StringBuilder();
UsbDescriptorsTree descriptorTree = new UsbDescriptorsTree();
@@ -131,6 +135,55 @@
pw.println(formatTime() + " Disconnect " + mDeviceAddress);
}
}
+
+ void dumpList(IndentingPrintWriter pw) {
+ if (mMode != DISCONNECT) {
+ pw.println(formatTime() + " Connect " + mDeviceAddress + " mode:" + mMode);
+ UsbDescriptorParser parser = new UsbDescriptorParser(mDeviceAddress, mDescriptors);
+ StringBuilder stringBuilder = new StringBuilder();
+ TextReportCanvas canvas = new TextReportCanvas(parser, stringBuilder);
+ for (UsbDescriptor descriptor : parser.getDescriptors()) {
+ descriptor.report(canvas);
+ }
+ pw.println(stringBuilder.toString());
+
+ pw.println("isHeadset[in: " + parser.isInputHeadset()
+ + " , out: " + parser.isOutputHeadset() + "]");
+ } else {
+ pw.println(formatTime() + " Disconnect " + mDeviceAddress);
+ }
+ }
+
+ private static final int kDumpBytesPerLine = 16;
+
+ void dumpRaw(IndentingPrintWriter pw) {
+ if (mMode != DISCONNECT) {
+ pw.println(formatTime() + " Connect " + mDeviceAddress + " mode:" + mMode);
+ int length = mDescriptors.length;
+ pw.println("Raw Descriptors " + length + " bytes");
+ int dataOffset = 0;
+ for (int line = 0; line < length / kDumpBytesPerLine; line++) {
+ StringBuilder sb = new StringBuilder();
+ for (int offset = 0; offset < kDumpBytesPerLine; offset++) {
+ sb.append("0x")
+ .append(String.format("0x%02X", mDescriptors[dataOffset++]))
+ .append(" ");
+ }
+ pw.println(sb.toString());
+ }
+
+ // remainder
+ StringBuilder sb = new StringBuilder();
+ while (dataOffset < length) {
+ sb.append("0x")
+ .append(String.format("0x%02X", mDescriptors[dataOffset++]))
+ .append(" ");
+ }
+ pw.println(sb.toString());
+ } else {
+ pw.println(formatTime() + " Disconnect " + mDeviceAddress);
+ }
+ }
}
/*
@@ -205,7 +258,7 @@
ConnectionRecord rec =
new ConnectionRecord(deviceAddress, mode, rawDescriptors);
mConnections.add(rec);
- if (mode == ConnectionRecord.CONNECT) {
+ if (mode != ConnectionRecord.DISCONNECT) {
mLastConnect = rec;
}
}
@@ -241,31 +294,41 @@
if (parser.parseDescriptors(descriptors)) {
UsbDevice newDevice = parser.toAndroidUsbDevice();
- mDevices.put(deviceAddress, newDevice);
-
- // It is fine to call this only for the current user as all broadcasts are sent to
- // all profiles of the user and the dialogs should only show once.
- ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
- if (usbDeviceConnectionHandler == null) {
- getCurrentUserSettings().deviceAttached(newDevice);
+ if (newDevice == null) {
+ Slog.e(TAG, "Couldn't create UsbDevice object.");
+ // Tracking
+ addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADDEVICE,
+ parser.getRawDescriptors());
} else {
- getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice,
- usbDeviceConnectionHandler);
+ mDevices.put(deviceAddress, newDevice);
+
+ // It is fine to call this only for the current user as all broadcasts are
+ // sent to all profiles of the user and the dialogs should only show once.
+ ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
+ if (usbDeviceConnectionHandler == null) {
+ getCurrentUserSettings().deviceAttached(newDevice);
+ } else {
+ getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice,
+ usbDeviceConnectionHandler);
+ }
+
+ // Headset?
+ boolean isInputHeadset = parser.isInputHeadset();
+ boolean isOutputHeadset = parser.isOutputHeadset();
+ Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset
+ + " , out: " + isOutputHeadset + "]");
+
+ mUsbAlsaManager.usbDeviceAdded(newDevice, isInputHeadset, isOutputHeadset);
+
+ // Tracking
+ addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
+ parser.getRawDescriptors());
}
-
- // Headset?
- boolean isInputHeadset = parser.isInputHeadset();
- boolean isOutputHeadset = parser.isOutputHeadset();
- Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset
- + " , out: " + isOutputHeadset + "]");
-
- mUsbAlsaManager.usbDeviceAdded(newDevice, isInputHeadset, isOutputHeadset);
-
- // Tracking
- addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
- parser.getRawDescriptors());
} else {
Slog.e(TAG, "Error parsing USB device descriptors for " + deviceAddress);
+ // Tracking
+ addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADPARSE,
+ parser.getRawDescriptors());
return false;
}
}
@@ -330,7 +393,11 @@
}
}
- public void dump(IndentingPrintWriter pw) {
+ /**
+ * Dump out various information about the state of USB device connections.
+ *
+ */
+ public void dump(IndentingPrintWriter pw, String[] args) {
pw.println("USB Host State:");
synchronized (mHandlerLock) {
if (mUsbDeviceConnectionHandler != null) {
@@ -342,21 +409,38 @@
pw.println(" " + name + ": " + mDevices.get(name));
}
+ // Connections
pw.println("" + mNumConnects + " total connects/disconnects");
pw.println("Last " + mConnections.size() + " connections/disconnections");
for (ConnectionRecord rec : mConnections) {
rec.dumpShort(pw);
}
- if (mLastConnect != null) {
- pw.println("Last Connected USB Device:");
- mLastConnect.dumpLong(pw);
- }
}
mUsbAlsaManager.dump(pw);
}
+ /**
+ * Dump various descriptor data.
+ */
+ public void dumpDescriptors(IndentingPrintWriter pw, String[] args) {
+ if (mLastConnect != null) {
+ pw.println("Last Connected USB Device:");
+ if (args.length <= 1 || args[1].equals("-dump-short")) {
+ mLastConnect.dumpShort(pw);
+ } else if (args[1].equals("-dump-tree")) {
+ mLastConnect.dumpTree(pw);
+ } else if (args[1].equals("-dump-list")) {
+ mLastConnect.dumpList(pw);
+ } else if (args[1].equals("-dump-raw")) {
+ mLastConnect.dumpRaw(pw);
+ }
+ } else {
+ pw.println("No USB Devices have been connected.");
+ }
+ }
+
private native void monitorUsbHostBus();
private native ParcelFileDescriptor nativeOpenDevice(String deviceAddress);
}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 17de83f..8554cf7 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -496,7 +496,7 @@
mDeviceManager.dump(pw);
}
if (mHostManager != null) {
- mHostManager.dump(pw);
+ mHostManager.dump(pw, args);
}
if (mPortManager != null) {
mPortManager.dump(pw);
@@ -504,7 +504,7 @@
mAlsaManager.dump(pw);
mSettingsManager.dump(pw);
- } else if (args.length == 4 && "set-port-roles".equals(args[0])) {
+ } else if ("set-port-roles".equals(args[0]) && args.length == 4) {
final String portId = args[1];
final int powerRole;
switch (args[2]) {
@@ -546,7 +546,7 @@
pw.println();
mPortManager.dump(pw);
}
- } else if (args.length == 3 && "add-port".equals(args[0])) {
+ } else if ("add-port".equals(args[0]) && args.length == 3) {
final String portId = args[1];
final int supportedModes;
switch (args[2]) {
@@ -571,7 +571,7 @@
pw.println();
mPortManager.dump(pw);
}
- } else if (args.length == 5 && "connect-port".equals(args[0])) {
+ } else if ("connect-port".equals(args[0]) && args.length == 5) {
final String portId = args[1];
final int mode;
final boolean canChangeMode = args[2].endsWith("?");
@@ -618,30 +618,32 @@
pw.println();
mPortManager.dump(pw);
}
- } else if (args.length == 2 && "disconnect-port".equals(args[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);
}
- } else if (args.length == 2 && "remove-port".equals(args[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);
}
- } else if (args.length == 1 && "reset".equals(args[0])) {
+ } else if ("reset".equals(args[0]) && args.length == 1) {
if (mPortManager != null) {
mPortManager.resetSimulation(pw);
pw.println();
mPortManager.dump(pw);
}
- } else if (args.length == 1 && "ports".equals(args[0])) {
+ } else if ("ports".equals(args[0]) && args.length == 1) {
if (mPortManager != null) {
mPortManager.dump(pw);
}
+ } else if ("dump-descriptors".equals(args[0])) {
+ mHostManager.dumpDescriptors(pw, args);
} else {
pw.println("Dump current USB state or issue command:");
pw.println(" ports");
@@ -678,6 +680,12 @@
pw.println(" dumpsys usb add-port \"matrix\" ufp");
pw.println(" dumpsys usb connect-port \"matrix\" ufp sink device");
pw.println(" dumpsys usb reset");
+ pw.println();
+ pw.println("Example USB device descriptors:");
+ pw.println(" dumpsys usb dump-descriptors -dump-short");
+ pw.println(" dumpsys usb dump-descriptors -dump-tree");
+ pw.println(" dumpsys usb dump-descriptors -dump-list");
+ pw.println(" dumpsys usb dump-descriptors -dump-raw");
}
} finally {
Binder.restoreCallingIdentity(ident);
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
index 993778f..639aa4e 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
@@ -17,6 +17,7 @@
import android.hardware.usb.UsbConfiguration;
import android.hardware.usb.UsbInterface;
+import android.util.Log;
import com.android.server.usb.descriptors.report.ReportCanvas;
@@ -29,6 +30,7 @@
*/
public final class UsbConfigDescriptor extends UsbDescriptor {
private static final String TAG = "UsbConfigDescriptor";
+ private static final boolean DEBUG = false;
private int mTotalLength; // 2:2 Total length in bytes of data returned
private byte mNumInterfaces; // 4:1 Number of Interfaces
@@ -77,10 +79,16 @@
}
UsbConfiguration toAndroid(UsbDescriptorParser parser) {
+ if (DEBUG) {
+ Log.d(TAG, " toAndroid()");
+ }
String name = parser.getDescriptorString(mConfigIndex);
UsbConfiguration config = new
UsbConfiguration(mConfigValue, name, mAttribs, mMaxPower);
UsbInterface[] interfaces = new UsbInterface[mInterfaceDescriptors.size()];
+ if (DEBUG) {
+ Log.d(TAG, " " + mInterfaceDescriptors.size() + " interfaces.");
+ }
for (int index = 0; index < mInterfaceDescriptors.size(); index++) {
interfaces[index] = mInterfaceDescriptors.get(index).toAndroid(parser);
}
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 78c7fdc..7a1e9e2 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 = false;
+ private static final boolean DEBUG = true;
private final String mDeviceAddr;
@@ -289,10 +289,15 @@
*/
public UsbDevice toAndroidUsbDevice() {
if (mDeviceDescriptor == null) {
+ Log.e(TAG, "toAndroidUsbDevice() ERROR - No Device Descriptor");
return null;
}
- return mDeviceDescriptor.toAndroid(this);
+ UsbDevice device = mDeviceDescriptor.toAndroid(this);
+ if (device == null) {
+ Log.e(TAG, "toAndroidUsbDevice() ERROR Creating Device");
+ }
+ return device;
}
/**
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
index 8e7f0fd..e31e3a3 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
@@ -17,6 +17,7 @@
import android.hardware.usb.UsbConfiguration;
import android.hardware.usb.UsbDevice;
+import android.util.Log;
import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.UsbStrings;
@@ -30,6 +31,7 @@
*/
public final class UsbDeviceDescriptor extends UsbDescriptor {
private static final String TAG = "UsbDeviceDescriptor";
+ private static final boolean DEBUG = false;
public static final int USBSPEC_1_0 = 0x0100;
public static final int USBSPEC_1_1 = 0x0110;
@@ -113,19 +115,30 @@
* @hide
*/
public UsbDevice toAndroid(UsbDescriptorParser parser) {
+ if (DEBUG) {
+ Log.d(TAG, "toAndroid()");
+ }
+
String mfgName = parser.getDescriptorString(mMfgIndex);
String prodName = parser.getDescriptorString(mProductIndex);
+ if (DEBUG) {
+ Log.d(TAG, " mfgName:" + mfgName + " prodName:" + prodName);
+ }
// Create version string in "%.%" format
String versionString =
Integer.toString(mDeviceRelease >> 8) + "." + (mDeviceRelease & 0xFF);
String serialStr = parser.getDescriptorString(mSerialNum);
+ if (DEBUG) {
+ Log.d(TAG, " versionString:" + versionString + " serialStr:" + serialStr);
+ }
UsbDevice device = new UsbDevice(parser.getDeviceAddr(), mVendorID, mProductID,
mDevClass, mDevSubClass,
mProtocol, mfgName, prodName,
versionString, serialStr);
UsbConfiguration[] configs = new UsbConfiguration[mConfigDescriptors.size()];
+ Log.d(TAG, " " + configs.length + " configs");
for (int index = 0; index < mConfigDescriptors.size(); index++) {
configs[index] = mConfigDescriptors.get(index).toAndroid(parser);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
index 1130238..4da31ea 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
@@ -16,6 +16,7 @@
package com.android.server.usb.descriptors;
import android.hardware.usb.UsbEndpoint;
+import android.util.Log;
import com.android.server.usb.descriptors.report.ReportCanvas;
@@ -26,6 +27,7 @@
*/
public class UsbEndpointDescriptor extends UsbDescriptor {
private static final String TAG = "UsbEndpointDescriptor";
+ private static final boolean DEBUG = false;
public static final int MASK_ENDPOINT_ADDRESS = 0b000000000001111;
public static final int MASK_ENDPOINT_DIRECTION = (byte) 0b0000000010000000;
@@ -108,6 +110,12 @@
}
/* package */ UsbEndpoint toAndroid(UsbDescriptorParser parser) {
+ if (DEBUG) {
+ Log.d(TAG, "toAndroid() type:"
+ + Integer.toHexString(mAttributes & MASK_ATTRIBS_TRANSTYPE)
+ + " sync:" + Integer.toHexString(mAttributes & MASK_ATTRIBS_SYNCTYPE)
+ + " usage:" + Integer.toHexString(mAttributes & MASK_ATTRIBS_USEAGE));
+ }
return new UsbEndpoint(mEndpointAddress, mAttributes, mPacketSize, mInterval);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index d87b1af..632e3dc 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -17,6 +17,7 @@
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
+import android.util.Log;
import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.UsbStrings;
@@ -30,6 +31,7 @@
*/
public class UsbInterfaceDescriptor extends UsbDescriptor {
private static final String TAG = "UsbInterfaceDescriptor";
+ private static final boolean DEBUG = false;
protected int mInterfaceNumber; // 2:1 Number of Interface
protected byte mAlternateSetting; // 3:1 Value used to select alternative setting
@@ -93,6 +95,11 @@
}
UsbInterface toAndroid(UsbDescriptorParser parser) {
+ if (DEBUG) {
+ Log.d(TAG, "toAndroid() class:" + Integer.toHexString(mUsbClass)
+ + " subclass:" + Integer.toHexString(mUsbSubclass)
+ + " " + mEndpointDescriptors.size() + " endpoints.");
+ }
String name = parser.getDescriptorString(mDescrIndex);
UsbInterface ntrface = new UsbInterface(
mInterfaceNumber, mAlternateSetting, name, mUsbClass, mUsbSubclass, mProtocol);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 2d93da9..44f5551 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -67,6 +67,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
@@ -75,7 +76,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
-import java.util.TreeSet;
/**
* SystemService that publishes an IVoiceInteractionManagerService.
@@ -390,11 +390,13 @@
}
public void switchUser(int userHandle) {
- synchronized (this) {
- mCurUser = userHandle;
- mCurUserUnlocked = false;
- switchImplementationIfNeededLocked(false);
- }
+ FgThread.getHandler().post(() -> {
+ synchronized (this) {
+ mCurUser = userHandle;
+ mCurUserUnlocked = false;
+ switchImplementationIfNeededLocked(false);
+ }
+ });
}
void switchImplementationIfNeeded(boolean force) {
@@ -442,11 +444,11 @@
mImpl.shutdownLocked();
}
if (hasComponent) {
- mImpl = new VoiceInteractionManagerServiceImpl(mContext,
- UiThread.getHandler(), this, mCurUser, serviceComponent);
+ setImplLocked(new VoiceInteractionManagerServiceImpl(mContext,
+ UiThread.getHandler(), this, mCurUser, serviceComponent));
mImpl.startLocked();
} else {
- mImpl = null;
+ setImplLocked(null);
}
}
}
@@ -1177,6 +1179,12 @@
}
}
+ private void setImplLocked(VoiceInteractionManagerServiceImpl impl) {
+ mImpl = impl;
+ mAmInternal.notifyActiveVoiceInteractionServiceChanged(
+ getActiveServiceComponentName());
+ }
+
class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
super(handler);
@@ -1219,7 +1227,7 @@
unloadAllKeyphraseModels();
if (mImpl != null) {
mImpl.shutdownLocked();
- mImpl = null;
+ setImplLocked(null);
}
setCurInteractor(null, userHandle);
setCurRecognizer(null, userHandle);
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 7001615..dec7b76 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -21,6 +21,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -145,6 +146,7 @@
private static final String SESSION_RTT_UPGRADE_RESPONSE = "CS.rTRUR";
private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL";
private static final String SESSION_CONNECTION_SERVICE_FOCUS_GAINED = "CS.cSFG";
+ private static final String SESSION_HANDOVER_FAILED = "CS.haF";
private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;
private static final int MSG_CREATE_CONNECTION = 2;
@@ -176,6 +178,7 @@
private static final int MSG_CREATE_CONNECTION_COMPLETE = 29;
private static final int MSG_CONNECTION_SERVICE_FOCUS_LOST = 30;
private static final int MSG_CONNECTION_SERVICE_FOCUS_GAINED = 31;
+ private static final int MSG_HANDOVER_FAILED = 32;
private static Connection sNullConnection;
@@ -279,6 +282,22 @@
}
@Override
+ public void handoverFailed(String callId, ConnectionRequest request, int reason,
+ Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_HANDOVER_FAILED);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = request;
+ args.arg3 = Log.createSubsession();
+ args.arg4 = reason;
+ mHandler.obtainMessage(MSG_HANDOVER_FAILED, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void abort(String callId, Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_ABORT);
try {
@@ -747,6 +766,36 @@
}
break;
}
+ case MSG_HANDOVER_FAILED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3, SESSION_HANDLER +
+ SESSION_HANDOVER_FAILED);
+ try {
+ final String id = (String) args.arg1;
+ final ConnectionRequest request = (ConnectionRequest) args.arg2;
+ final int reason = (int) args.arg4;
+ if (!mAreAccountsInitialized) {
+ Log.d(this, "Enqueueing pre-init request %s", id);
+ mPreInitializationConnectionRequests.add(
+ new android.telecom.Logging.Runnable(
+ SESSION_HANDLER
+ + SESSION_HANDOVER_FAILED + ".pICR",
+ null /*lock*/) {
+ @Override
+ public void loggedRun() {
+ handoverFailed(id, request, reason);
+ }
+ }.prepare());
+ } else {
+ Log.i(this, "createConnectionFailed %s", id);
+ handoverFailed(id, request, reason);
+ }
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
case MSG_ABORT: {
SomeArgs args = (SomeArgs) msg.obj;
Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_ABORT);
@@ -1402,12 +1451,13 @@
isUnknown);
Connection connection = null;
- if (request.getExtras() != null && request.getExtras().getBoolean(
- TelecomManager.EXTRA_IS_HANDOVER,false)) {
+ if (getApplicationContext().getApplicationInfo().targetSdkVersion >
+ Build.VERSION_CODES.O_MR1 && request.getExtras() != null &&
+ request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER,false)) {
if (!isIncoming) {
connection = onCreateOutgoingHandoverConnection(callManagerAccount, request);
} else {
- // Todo: Call onCreateIncommingHandoverConnection()
+ connection = onCreateIncomingHandoverConnection(callManagerAccount, request);
}
} else {
connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
@@ -1482,6 +1532,13 @@
}
}
+ private void handoverFailed(final String callId, final ConnectionRequest request,
+ int reason) {
+
+ Log.i(this, "handoverFailed %s", callId);
+ onHandoverFailed(request, reason);
+ }
+
/**
* Called by Telecom when the creation of a new Connection has completed and it is now added
* to Telecom.
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 74b9465..fcfc593 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -964,6 +964,9 @@
if (hasCapabilities(CAPABILITY_SIM_SUBSCRIPTION)) {
sb.append("SimSub ");
}
+ if (hasCapabilities(CAPABILITY_RTT)) {
+ sb.append("Rtt");
+ }
return sb.toString();
}
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index a740566..3d04bfc 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -104,4 +104,7 @@
void connectionServiceFocusLost(in Session.Info sessionInfo);
void connectionServiceFocusGained(in Session.Info sessionInfo);
+
+ void handoverFailed(String callId, in ConnectionRequest request,
+ int error, in Session.Info sessionInfo);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index e9feb89..6a47d05 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -300,10 +300,10 @@
* If true all networks are considered as home network a.k.a non-roaming. When false,
* the 2 pairs of CMDA and GSM roaming/non-roaming arrays are consulted.
*
- * @see KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
- * @see KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
- * @see KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY
- * @see KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY
+ * @see #KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+ * @see #KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+ * @see #KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY
+ * @see #KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY
*/
public static final String
KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
@@ -735,12 +735,11 @@
public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
/**
- * Flag specifying whether signal strength is hidden in SIM Status screen,
- * default to false.
- * @hide
+ * Flag specifying whether the {@link android.telephony.SignalStrength} is shown in the SIM
+ * Status screen. The default value is true.
*/
- public static final String KEY_HIDE_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL =
- "hide_signal_strength_in_sim_status_bool";
+ public static final String KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL =
+ "show_signal_strength_in_sim_status_bool";
/**
* Flag specifying whether an additional (client initiated) intent needs to be sent on System
@@ -1310,6 +1309,19 @@
*/
public static final String KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL = "allow_hold_in_ims_call";
+
+ /**
+ * Flag indicating whether the carrier always wants to play an "on-hold" tone when a call has
+ * been remotely held.
+ * <p>
+ * When {@code true}, if the IMS stack indicates that the call session has been held, a signal
+ * will be sent from Telephony to play an audible "on-hold" tone played to the user.
+ * When {@code false}, a hold tone will only be played if the audio session becomes inactive.
+ * @hide
+ */
+ public static final String KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL =
+ "always_play_remote_hold_tone_bool";
+
/**
* When true, indicates that adding a call is disabled when there is an ongoing video call
* or when there is an ongoing call on wifi which was downgraded from video and VoWifi is
@@ -1648,6 +1660,11 @@
"show_ims_registration_status_bool";
/**
+ * Flag indicating whether the carrier supports RTT over IMS.
+ */
+ public static final String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool";
+
+ /**
* The flag to disable the popup dialog which warns the user of data charges.
* @hide
*/
@@ -1700,12 +1717,20 @@
public static final String KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL =
"spn_display_rule_use_roaming_from_service_state_bool";
+ /**
+ * Determines whether any carrier has been identified and its specific config has been applied,
+ * default to false.
+ * @hide
+ */
+ public static final String KEY_CARRIER_CONFIG_APPLIED_BOOL = "carrier_config_applied_bool";
+
/** 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_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);
@@ -1782,7 +1807,7 @@
sDefaults.putString(KEY_CARRIER_VVM_PACKAGE_NAME_STRING, "");
sDefaults.putStringArray(KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL, false);
- sDefaults.putBoolean(KEY_HIDE_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL, false);
+ sDefaults.putBoolean(KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL, true);
sDefaults.putBoolean(KEY_CI_ACTION_ON_SYS_UPDATE_BOOL, false);
sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING, "");
sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING, "");
@@ -1978,11 +2003,13 @@
sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null);
sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
+ sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false);
sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL, false);
sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
}
/**
@@ -2028,6 +2055,33 @@
}
/**
+ * Determines whether a configuration {@link PersistableBundle} obtained from
+ * {@link #getConfig()} or {@link #getConfigForSubId(int)} corresponds to an identified carrier.
+ * <p>
+ * When an app receives the {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED}
+ * broadcast which informs it that the carrier configuration has changed, it is possible
+ * that another reload of the carrier configuration has begun since the intent was sent.
+ * In this case, the carrier configuration the app fetches (e.g. via {@link #getConfig()})
+ * may not represent the configuration for the current carrier. It should be noted that it
+ * does not necessarily mean the configuration belongs to current carrier when this function
+ * return true because it may belong to another previous identified carrier. Users should
+ * always call {@link #getConfig()} or {@link #getConfigForSubId(int)} after receiving the
+ * broadcast {@link #ACTION_CARRIER_CONFIG_CHANGED}.
+ * </p>
+ * <p>
+ * After using {@link #getConfig()} or {@link #getConfigForSubId(int)} an app should always
+ * use this method to confirm whether any carrier specific configuration has been applied.
+ * </p>
+ *
+ * @param bundle the configuration bundle to be checked.
+ * @return boolean true if any carrier specific configuration bundle has been applied, false
+ * otherwise or the bundle is null.
+ */
+ public static boolean isConfigForIdentifiedCarrier(PersistableBundle bundle) {
+ return bundle != null && bundle.getBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL);
+ }
+
+ /**
* Calling this method triggers telephony services to fetch the current carrier configuration.
* <p>
* Normally this does not need to be called because the platform reloads config on its own.
@@ -2040,7 +2094,7 @@
* {@link android.service.carrier.CarrierService#onLoadConfig} will be called from an
* arbitrary thread.
* </p>
- * @see #hasCarrierPrivileges
+ * @see TelephonyManager#hasCarrierPrivileges
*/
public void notifyConfigChangedForSubId(int subId) {
try {
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index fdedf75..7ab75f5 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -390,112 +390,6 @@
}
/**
- * Send a text based SMS with messaging options.
- *
- * @param destinationAddress the address to send the message to
- * @param scAddress is the service center address or null to use
- * the current default SMSC
- * @param text the body of the message to send
- * @param sentIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is successfully sent, or failed.
- * The result code will be <code>Activity.RESULT_OK</code> for success,
- * or one of these errors:<br>
- * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
- * <code>RESULT_ERROR_RADIO_OFF</code><br>
- * <code>RESULT_ERROR_NULL_PDU</code><br>
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
- * The per-application based SMS control checks sentIntent. If sentIntent
- * is NULL the caller will be checked against all unknown applications,
- * which cause smaller number of SMS to be sent in checking period.
- * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is delivered to the recipient. The
- * raw pdu of the status report is in the extended data ("pdu").
- * @param priority Priority level of the message
- * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
- * ---------------------------------
- * PRIORITY | Level of Priority
- * ---------------------------------
- * '00' | Normal
- * '01' | Interactive
- * '10' | Urgent
- * '11' | Emergency
- * ----------------------------------
- * Any Other values included Negative considered as Invalid Priority Indicator of the message.
- * @param expectMore is a boolean to indicate the sending messages through same link or not.
- * @param validityPeriod Validity Period of the message in mins.
- * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
- * Validity Period(Minimum) -> 5 mins
- * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
- * Any Other values included Negative considered as Invalid Validity Period of the message.
- *
- * @throws IllegalArgumentException if destinationAddress or text are empty
- * {@hide}
- */
- public void sendTextMessage(
- String destinationAddress, String scAddress, String text,
- PendingIntent sentIntent, PendingIntent deliveryIntent,
- int priority, boolean expectMore, int validityPeriod) {
- sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
- true /* persistMessage*/, priority, expectMore, validityPeriod);
- }
-
- private void sendTextMessageInternal(
- String destinationAddress, String scAddress, String text,
- PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage,
- int priority, boolean expectMore, int validityPeriod) {
- if (TextUtils.isEmpty(destinationAddress)) {
- throw new IllegalArgumentException("Invalid destinationAddress");
- }
-
- if (TextUtils.isEmpty(text)) {
- throw new IllegalArgumentException("Invalid message body");
- }
-
- if (priority < 0x00 || priority > 0x03) {
- throw new IllegalArgumentException("Invalid priority");
- }
-
- if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
- throw new IllegalArgumentException("Invalid validity period");
- }
-
- try {
- ISms iccISms = getISmsServiceOrThrow();
- if (iccISms != null) {
- iccISms.sendTextForSubscriberWithOptions(getSubscriptionId(),
- ActivityThread.currentPackageName(), destinationAddress, scAddress, text,
- sentIntent, deliveryIntent, persistMessage, priority, expectMore,
- validityPeriod);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- }
-
- /**
- * Send a text based SMS without writing it into the SMS Provider.
- *
- * <p>Requires Permission:
- * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
- * privileges.
- * </p>
- *
- * @see #sendTextMessage(String, String, String, PendingIntent,
- * PendingIntent, int, boolean, int)
- * @hide
- */
- public void sendTextMessageWithoutPersisting(
- String destinationAddress, String scAddress, String text,
- PendingIntent sentIntent, PendingIntent deliveryIntent, int priority,
- boolean expectMore, int validityPeriod) {
- sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
- false /* persistMessage */, priority, expectMore, validityPeriod);
- }
-
- /**
- *
* Inject an SMS PDU into the android application framework.
*
* <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier
@@ -653,140 +547,6 @@
}
/**
- * Send a multi-part text based SMS with messaging options. The callee should have already
- * divided the message into correctly sized parts by calling
- * <code>divideMessage</code>.
- *
- * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
- * {@link android.Manifest.permission#SEND_SMS} permission.</p>
- *
- * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if
- * <em>and only if</em> an app is not selected as the default SMS app, the system automatically
- * writes messages sent using this method to the SMS Provider (the default SMS app is always
- * responsible for writing its sent messages to the SMS Provider). For information about
- * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
- *
- * @param destinationAddress the address to send the message to
- * @param scAddress is the service center address or null to use
- * the current default SMSC
- * @param parts an <code>ArrayList</code> of strings that, in order,
- * comprise the original message
- * @param sentIntents if not null, an <code>ArrayList</code> of
- * <code>PendingIntent</code>s (one for each message part) that is
- * broadcast when the corresponding message part has been sent.
- * The result code will be <code>Activity.RESULT_OK</code> for success,
- * or one of these errors:<br>
- * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
- * <code>RESULT_ERROR_RADIO_OFF</code><br>
- * <code>RESULT_ERROR_NULL_PDU</code><br>
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
- * The per-application based SMS control checks sentIntent. If sentIntent
- * is NULL the caller will be checked against all unknown applications,
- * which cause smaller number of SMS to be sent in checking period.
- * @param deliveryIntents if not null, an <code>ArrayList</code> of
- * <code>PendingIntent</code>s (one for each message part) that is
- * broadcast when the corresponding message part has been delivered
- * to the recipient. The raw pdu of the status report is in the
- * extended data ("pdu").
- * @param priority Priority level of the message
- * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
- * ---------------------------------
- * PRIORITY | Level of Priority
- * ---------------------------------
- * '00' | Normal
- * '01' | Interactive
- * '10' | Urgent
- * '11' | Emergency
- * ----------------------------------
- * Any Other values included Negative considered as Invalid Priority Indicator of the message.
- * @param expectMore is a boolean to indicate the sending messages through same link or not.
- * @param validityPeriod Validity Period of the message in mins.
- * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
- * Validity Period(Minimum) -> 5 mins
- * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
- * Any Other values included Negative considered as Invalid Validity Period of the message.
- *
- * @throws IllegalArgumentException if destinationAddress or data are empty
- * {@hide}
- */
- public void sendMultipartTextMessage(
- String destinationAddress, String scAddress, ArrayList<String> parts,
- ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents,
- int priority, boolean expectMore, int validityPeriod) {
- sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
- deliveryIntents, true /* persistMessage*/);
- }
-
- private void sendMultipartTextMessageInternal(
- String destinationAddress, String scAddress, List<String> parts,
- List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
- boolean persistMessage, int priority, boolean expectMore, int validityPeriod) {
- if (TextUtils.isEmpty(destinationAddress)) {
- throw new IllegalArgumentException("Invalid destinationAddress");
- }
- if (parts == null || parts.size() < 1) {
- throw new IllegalArgumentException("Invalid message body");
- }
-
- if (priority < 0x00 || priority > 0x03) {
- throw new IllegalArgumentException("Invalid priority");
- }
-
- if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
- throw new IllegalArgumentException("Invalid validity period");
- }
-
- if (parts.size() > 1) {
- try {
- ISms iccISms = getISmsServiceOrThrow();
- if (iccISms != null) {
- iccISms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(),
- ActivityThread.currentPackageName(), destinationAddress, scAddress,
- parts, sentIntents, deliveryIntents, persistMessage, priority,
- expectMore, validityPeriod);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- } else {
- PendingIntent sentIntent = null;
- PendingIntent deliveryIntent = null;
- if (sentIntents != null && sentIntents.size() > 0) {
- sentIntent = sentIntents.get(0);
- }
- if (deliveryIntents != null && deliveryIntents.size() > 0) {
- deliveryIntent = deliveryIntents.get(0);
- }
- sendTextMessageInternal(destinationAddress, scAddress, parts.get(0),
- sentIntent, deliveryIntent, persistMessage, priority, expectMore,
- validityPeriod);
- }
- }
-
- /**
- * Send a multi-part text based SMS without writing it into the SMS Provider.
- *
- * <p>Requires Permission:
- * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
- * privileges.
- * </p>
- *
- * @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList,
- * ArrayList, int, boolean, int)
- * @hide
- **/
- public void sendMultipartTextMessageWithoutPersisting(
- String destinationAddress, String scAddress, List<String> parts,
- List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
- int priority, boolean expectMore, int validityPeriod) {
- sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
- deliveryIntents, false /* persistMessage*/, priority, expectMore,
- validityPeriod);
- }
-
- /**
* Send a data based SMS to a specific application port.
*
* <p class="note"><strong>Note:</strong> Using this method requires that your app has the
@@ -1249,7 +1009,7 @@
* <code>getAllMessagesFromIcc</code>
* @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
*/
- private ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
+ private static ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
if (records != null) {
int count = records.size();
@@ -1257,8 +1017,7 @@
SmsRawData data = records.get(i);
// List contains all records, including "free" records (null)
if (data != null) {
- SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes(),
- getSubscriptionId());
+ SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes());
if (sms != null) {
messages.add(sms);
}
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index dd2a6df..a6dbc06 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -19,6 +19,7 @@
import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
import android.annotation.StringDef;
+import android.app.PendingIntent;
import android.content.res.Resources;
import android.os.Binder;
import android.text.TextUtils;
@@ -92,11 +93,13 @@
/**
* Indicates a 3GPP format SMS message.
+ * @see SmsManager#injectSmsPdu(byte[], String, PendingIntent)
*/
public static final String FORMAT_3GPP = "3gpp";
/**
* Indicates a 3GPP2 format SMS message.
+ * @see SmsManager#injectSmsPdu(byte[], String, PendingIntent)
*/
public static final String FORMAT_3GPP2 = "3gpp2";
@@ -274,31 +277,6 @@
}
/**
- * Create an SmsMessage from an SMS EF record.
- *
- * @param index Index of SMS record. This should be index in ArrayList
- * returned by SmsManager.getAllMessagesFromSim + 1.
- * @param data Record data.
- * @param subId Subscription Id of the SMS
- * @return An SmsMessage representing the record.
- *
- * @hide
- */
- public static SmsMessage createFromEfRecord(int index, byte[] data, int subId) {
- SmsMessageBase wrappedMessage;
-
- if (isCdmaVoice(subId)) {
- wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
- index, data);
- } else {
- wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
- index, data);
- }
-
- return wrappedMessage != null ? new SmsMessage(wrappedMessage) : null;
- }
-
- /**
* Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
* length in bytes (not hex chars) less the SMSC header
*
@@ -850,7 +828,6 @@
int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(subId);
return (PHONE_TYPE_CDMA == activePhone);
}
-
/**
* Decide if the carrier supports long SMS.
* {@hide}
diff --git a/telephony/java/android/telephony/Telephony.java b/telephony/java/android/telephony/Telephony.java
index d7b6142..942ea00 100644
--- a/telephony/java/android/telephony/Telephony.java
+++ b/telephony/java/android/telephony/Telephony.java
@@ -3342,6 +3342,12 @@
public static final String APN = "apn";
/**
+ * Prefix of Integrated Circuit Card Identifier.
+ * <P>Type: TEXT </P>
+ */
+ public static final String ICCID_PREFIX = "iccid_prefix";
+
+ /**
* User facing carrier name.
* <P>Type: TEXT </P>
*/
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 99fc9b3..9e77992 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -29,7 +29,6 @@
import android.annotation.WorkerThread;
import android.app.ActivityThread;
import android.app.PendingIntent;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
@@ -43,7 +42,6 @@
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.SystemProperties;
-import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.service.carrier.CarrierIdentifier;
import android.telecom.PhoneAccount;
@@ -979,6 +977,63 @@
*/
public static final int CDMA_ROAMING_MODE_ANY = 2;
+ /**
+ * An unknown carrier id. It could either be subscription unavailable or the subscription
+ * carrier cannot be recognized. Unrecognized carriers here means
+ * {@link #getSimOperator() MCC+MNC} cannot be identified.
+ */
+ public static final int UNKNOWN_CARRIER_ID = -1;
+
+ /**
+ * Broadcast Action: The subscription carrier identity has changed.
+ * This intent could be sent on the following events:
+ * <ul>
+ * <li>Subscription absent. Carrier identity could change from a valid id to
+ * {@link TelephonyManager#UNKNOWN_CARRIER_ID}.</li>
+ * <li>Subscription loaded. Carrier identity could change from
+ * {@link TelephonyManager#UNKNOWN_CARRIER_ID} to a valid id.</li>
+ * <li>The subscription carrier is recognized after a remote update.</li>
+ * </ul>
+ * The intent will have the following extra values:
+ * <ul>
+ * <li>{@link #EXTRA_CARRIER_ID} The up-to-date carrier id of the current subscription id.
+ * </li>
+ * <li>{@link #EXTRA_CARRIER_NAME} The up-to-date carrier name of the current subscription.
+ * </li>
+ * <li>{@link #EXTRA_SUBSCRIPTION_ID} The subscription id associated with the changed carrier
+ * identity.
+ * </li>
+ * </ul>
+ * <p class="note">This is a protected intent that can only be sent by the system.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED =
+ "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED";
+
+ /**
+ * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates
+ * the updated carrier id {@link TelephonyManager#getSubscriptionCarrierId()} of the current
+ * subscription.
+ * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
+ * the carrier cannot be identified.
+ */
+ public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
+
+ /**
+ * An string extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which
+ * indicates the updated carrier name of the current subscription.
+ * {@see TelephonyManager#getSubscriptionCarrierName()}
+ * <p>Carrier name is a user-facing name of the carrier id {@link #EXTRA_CARRIER_ID},
+ * usually the brand name of the subsidiary (e.g. T-Mobile).
+ */
+ public static final String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME";
+
+ /**
+ * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} to indicate the
+ * subscription which has changed.
+ */
+ public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
+
//
//
// Device Info
@@ -1121,12 +1176,14 @@
}
/**
- * Returns the NAI. Return null if NAI is not available.
- *
+ * Returns the Network Access Identifier (NAI). Return null if NAI is not available.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
- /** {@hide}*/
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public String getNai() {
- return getNai(getSlotIndex());
+ return getNaiBySubscriberId(getSubId());
}
/**
@@ -1137,11 +1194,18 @@
/** {@hide}*/
public String getNai(int slotIndex) {
int[] subId = SubscriptionManager.getSubId(slotIndex);
+ if (subId == null) {
+ return null;
+ }
+ return getNaiBySubscriberId(subId[0]);
+ }
+
+ private String getNaiBySubscriberId(int subId) {
try {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- String nai = info.getNaiForSubscriber(subId[0], mContext.getOpPackageName());
+ String nai = info.getNaiForSubscriber(subId, mContext.getOpPackageName());
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Rlog.v(TAG, "Nai = " + nai);
}
@@ -5692,39 +5756,38 @@
* @param enable Whether to enable mobile data.
*
* @see #hasCarrierPrivileges
+ * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setDataEnabled(boolean enable) {
- setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
+ setUserMobileDataEnabled(enable);
}
- /** @hide */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public void setDataEnabled(int subId, boolean enable) {
- try {
- Log.d(TAG, "setDataEnabled: enabled=" + enable);
- ITelephony telephony = getITelephony();
- if (telephony != null)
- telephony.setDataEnabled(subId, enable);
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#setDataEnabled", e);
- }
- }
-
-
/**
- * @deprecated use {@link #isDataEnabled()} instead.
+ * @hide
+ * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
+ */
+ @SystemApi
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setDataEnabled(int subId, boolean enable) {
+ setUserMobileDataEnabled(subId, enable);
+ }
+
+ /**
+ * @deprecated use {@link #isUserMobileDataEnabled()} instead.
* @hide
*/
@SystemApi
@Deprecated
public boolean getDataEnabled() {
- return isDataEnabled();
+ return isUserMobileDataEnabled();
}
/**
- * Returns whether mobile data is enabled or not.
+ * Returns whether mobile data is enabled or not per user setting. There are other factors
+ * that could disable mobile data, but they are not considered here.
*
* If this object has been created with {@link #createForSubscriptionId}, applies to the given
* subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
@@ -5741,28 +5804,21 @@
* @return true if mobile data is enabled.
*
* @see #hasCarrierPrivileges
+ * @deprecated use {@link #isUserMobileDataEnabled()} instead.
*/
- @SuppressWarnings("deprecation")
+ @Deprecated
public boolean isDataEnabled() {
- return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
+ return isUserMobileDataEnabled();
}
/**
- * @deprecated use {@link #isDataEnabled(int)} instead.
+ * @deprecated use {@link #isUserMobileDataEnabled()} instead.
* @hide
*/
+ @Deprecated
@SystemApi
public boolean getDataEnabled(int subId) {
- boolean retVal = false;
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- retVal = telephony.getDataEnabled(subId);
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#getDataEnabled", e);
- } catch (NullPointerException e) {
- }
- return retVal;
+ return isUserMobileDataEnabled(subId);
}
/** @hide */
@@ -6509,6 +6565,9 @@
* @param uri The URI for the ringtone to play when receiving a voicemail from a specific
* PhoneAccount.
* @see #hasCarrierPrivileges
+ *
+ * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
+ * instead.
*/
public void setVoicemailRingtoneUri(PhoneAccountHandle phoneAccountHandle, Uri uri) {
try {
@@ -6551,6 +6610,9 @@
* @param enabled Whether to enable or disable vibration for voicemail notifications from a
* specific PhoneAccount.
* @see #hasCarrierPrivileges
+ *
+ * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
+ * instead.
*/
public void setVoicemailVibrationEnabled(PhoneAccountHandle phoneAccountHandle,
boolean enabled) {
@@ -6566,6 +6628,55 @@
}
/**
+ * Returns carrier id of the current subscription.
+ * <p>To recognize a carrier (including MVNO) as a first class identity, assign each carrier
+ * with a canonical integer a.k.a carrier id.
+ *
+ * @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the
+ * subscription is unavailable or the carrier cannot be identified.
+ * @throws IllegalStateException if telephony service is unavailable.
+ */
+ public int getSubscriptionCarrierId() {
+ try {
+ ITelephony service = getITelephony();
+ return service.getSubscriptionCarrierId(getSubId());
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ ex.rethrowAsRuntimeException();
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing.
+ throw new IllegalStateException("Telephony service unavailable");
+ }
+ return UNKNOWN_CARRIER_ID;
+ }
+
+ /**
+ * Returns carrier name of the current subscription.
+ * <p>Carrier name is a user-facing name of carrier id {@link #getSubscriptionCarrierId()},
+ * usually the brand name of the subsidiary (e.g. T-Mobile). Each carrier could configure
+ * multiple {@link #getSimOperatorName() SPN} but should have a single carrier name.
+ * Carrier name is not a canonical identity, use {@link #getSubscriptionCarrierId()} instead.
+ * <p>The returned carrier name is unlocalized.
+ *
+ * @return Carrier name of the current subscription. Return {@code null} if the subscription is
+ * unavailable or the carrier cannot be identified.
+ * @throws IllegalStateException if telephony service is unavailable.
+ */
+ public String getSubscriptionCarrierName() {
+ try {
+ ITelephony service = getITelephony();
+ return service.getSubscriptionCarrierName(getSubId());
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ ex.rethrowAsRuntimeException();
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing.
+ throw new IllegalStateException("Telephony service unavailable");
+ }
+ return null;
+ }
+
+ /**
* Return the application ID for the app type like {@link APPTYPE_CSIM}.
*
* Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
@@ -6867,6 +6978,8 @@
* @return true if phone is in emergency callback mode
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean getEmergencyCallbackMode() {
return getEmergencyCallbackMode(getSubId());
}
@@ -6909,4 +7022,101 @@
}
return null;
}
+
+ /**
+ * Turns mobile data on or off.
+ * If the {@link TelephonyManager} object has been created with
+ * {@link #createForSubscriptionId}, this API applies to the given subId.
+ * Otherwise, it applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+ * calling app has carrier privileges.
+ *
+ * @param enable Whether to enable mobile data.
+ *
+ * @see #hasCarrierPrivileges
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setUserMobileDataEnabled(boolean enable) {
+ setUserMobileDataEnabled(
+ getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
+ }
+
+ /**
+ * Returns whether mobile data is enabled or not per user setting. There are other factors
+ * that could disable mobile data, but they are not considered here.
+ *
+ * If this object has been created with {@link #createForSubscriptionId}, applies to the given
+ * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ *
+ * <p>Requires one of the following permissions:
+ * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
+ * calling app has carrier privileges.
+ *
+ * <p>Note that this does not take into account any data restrictions that may be present on the
+ * calling app. Such restrictions may be inspected with
+ * {@link ConnectivityManager#getRestrictBackgroundStatus}.
+ *
+ * @return true if mobile data is enabled.
+ *
+ * @see #hasCarrierPrivileges
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.ACCESS_NETWORK_STATE,
+ android.Manifest.permission.MODIFY_PHONE_STATE
+ })
+ public boolean isUserMobileDataEnabled() {
+ return isUserMobileDataEnabled(
+ getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
+ }
+
+ /**
+ * @hide
+ * Unlike isUserMobileDataEnabled, this API also evaluates carrierDataEnabled,
+ * policyDataEnabled etc to give a final decision.
+ */
+ public boolean isMobileDataEnabled() {
+ boolean retVal = false;
+ try {
+ int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ retVal = telephony.isDataEnabled(subId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isDataEnabled", e);
+ } catch (NullPointerException e) {
+ }
+ return retVal;
+ }
+
+ /**
+ * Utility class of {@link #isUserMobileDataEnabled()};
+ */
+ private boolean isUserMobileDataEnabled(int subId) {
+ boolean retVal = false;
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ retVal = telephony.isUserDataEnabled(subId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isUserDataEnabled", e);
+ } catch (NullPointerException e) {
+ }
+ return retVal;
+ }
+
+ /** Utility method of {@link #setUserMobileDataEnabled(boolean)} */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ private void setUserMobileDataEnabled(int subId, boolean enable) {
+ try {
+ Log.d(TAG, "setUserMobileDataEnabled: enabled=" + enable);
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ telephony.setUserDataEnabled(subId, enable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
+ }
+ }
}
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 2507cfee..973df31 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -168,12 +168,10 @@
public static final String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon";
private final Context mContext;
- private final IEuiccController mController;
/** @hide */
public EuiccManager(Context context) {
mContext = context;
- mController = IEuiccController.Stub.asInterface(ServiceManager.getService("econtroller"));
}
/**
@@ -189,7 +187,7 @@
public boolean isEnabled() {
// In the future, this may reach out to IEuiccController (if non-null) to check any dynamic
// restrictions.
- return mController != null;
+ return getIEuiccController() != null;
}
/**
@@ -206,7 +204,7 @@
return null;
}
try {
- return mController.getEid();
+ return getIEuiccController().getEid();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -232,7 +230,7 @@
return;
}
try {
- mController.downloadSubscription(subscription, switchAfterDownload,
+ getIEuiccController().downloadSubscription(subscription, switchAfterDownload,
mContext.getOpPackageName(), callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -296,7 +294,7 @@
return;
}
try {
- mController.continueOperation(resolutionIntent, resolutionExtras);
+ getIEuiccController().continueOperation(resolutionIntent, resolutionExtras);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -328,7 +326,7 @@
return;
}
try {
- mController.getDownloadableSubscriptionMetadata(
+ getIEuiccController().getDownloadableSubscriptionMetadata(
subscription, mContext.getOpPackageName(), callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -358,7 +356,7 @@
return;
}
try {
- mController.getDefaultDownloadableSubscriptionList(
+ getIEuiccController().getDefaultDownloadableSubscriptionList(
mContext.getOpPackageName(), callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -377,7 +375,7 @@
return null;
}
try {
- return mController.getEuiccInfo();
+ return getIEuiccController().getEuiccInfo();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -402,7 +400,7 @@
return;
}
try {
- mController.deleteSubscription(
+ getIEuiccController().deleteSubscription(
subscriptionId, mContext.getOpPackageName(), callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -429,7 +427,7 @@
return;
}
try {
- mController.switchToSubscription(
+ getIEuiccController().switchToSubscription(
subscriptionId, mContext.getOpPackageName(), callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -455,7 +453,8 @@
return;
}
try {
- mController.updateSubscriptionNickname(subscriptionId, nickname, callbackIntent);
+ getIEuiccController().updateSubscriptionNickname(
+ subscriptionId, nickname, callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -477,7 +476,7 @@
return;
}
try {
- mController.eraseSubscriptions(callbackIntent);
+ getIEuiccController().eraseSubscriptions(callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -507,7 +506,7 @@
return;
}
try {
- mController.retainSubscriptionsForFactoryReset(callbackIntent);
+ getIEuiccController().retainSubscriptionsForFactoryReset(callbackIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -520,4 +519,8 @@
// Caller canceled the callback; do nothing.
}
}
+
+ private static IEuiccController getIEuiccController() {
+ return IEuiccController.Stub.asInterface(ServiceManager.getService("econtroller"));
+ }
}
diff --git a/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java
new file mode 100644
index 0000000..5d16dd5
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java
@@ -0,0 +1,364 @@
+/*
+ * 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.telephony.ims.internal;
+
+import android.os.RemoteException;
+import android.telephony.ims.internal.aidl.IImsCallSessionListener;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsConferenceState;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.ImsSuppServiceNotification;
+import com.android.ims.internal.ImsCallSession;
+
+/**
+ * Proxy class for interfacing with the framework's Call session for an ongoing IMS call.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsCallSessionListener maintained by other ImsServices.
+ *
+ * @hide
+ */
+public class ImsCallSessionListener {
+
+ private final IImsCallSessionListener mListener;
+
+ public ImsCallSessionListener(IImsCallSessionListener l) {
+ mListener = l;
+ }
+
+ /**
+ * Called when a request is sent out to initiate a new session
+ * and 1xx response is received from the network.
+ */
+ public void callSessionProgressing(ImsStreamMediaProfile profile)
+ throws RemoteException {
+ mListener.callSessionProgressing(profile);
+ }
+
+ /**
+ * Called when the session is initiated.
+ *
+ * @param profile the associated {@link ImsCallSession}.
+ */
+ public void callSessionInitiated(ImsCallProfile profile) throws RemoteException {
+ mListener.callSessionInitiated(profile);
+ }
+
+ /**
+ * Called when the session establishment has failed.
+ *
+ * @param reasonInfo detailed reason of the session establishment failure
+ */
+ public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) throws RemoteException {
+ mListener.callSessionInitiatedFailed(reasonInfo);
+ }
+
+ /**
+ * Called when the session is terminated.
+ *
+ * @param reasonInfo detailed reason of the session termination
+ */
+ public void callSessionTerminated(ImsReasonInfo reasonInfo) throws RemoteException {
+ mListener.callSessionTerminated(reasonInfo);
+ }
+
+ /**
+ * Called when the session is on hold.
+ */
+ public void callSessionHeld(ImsCallProfile profile) throws RemoteException {
+ mListener.callSessionHeld(profile);
+ }
+
+ /**
+ * Called when the session hold has failed.
+ *
+ * @param reasonInfo detailed reason of the session hold failure
+ */
+ public void callSessionHoldFailed(ImsReasonInfo reasonInfo) throws RemoteException {
+ mListener.callSessionHoldFailed(reasonInfo);
+ }
+
+ /**
+ * Called when the session hold is received from the remote user.
+ */
+ public void callSessionHoldReceived(ImsCallProfile profile) throws RemoteException {
+ mListener.callSessionHoldReceived(profile);
+ }
+
+ /**
+ * Called when the session resume is done.
+ */
+ public void callSessionResumed(ImsCallProfile profile) throws RemoteException {
+ mListener.callSessionResumed(profile);
+ }
+
+ /**
+ * Called when the session resume has failed.
+ *
+ * @param reasonInfo detailed reason of the session resume failure
+ */
+ public void callSessionResumeFailed(ImsReasonInfo reasonInfo) throws RemoteException {
+ mListener.callSessionResumeFailed(reasonInfo);
+ }
+
+ /**
+ * Called when the session resume is received from the remote user.
+ */
+ public void callSessionResumeReceived(ImsCallProfile profile) throws RemoteException {
+ mListener.callSessionResumeReceived(profile);
+ }
+
+ /**
+ * Called when the session merge has been started. At this point, the {@code newSession}
+ * represents the session which has been initiated to the IMS conference server for the
+ * new merged conference.
+ *
+ * @param newSession the session object that is merged with an active & hold session
+ */
+ public void callSessionMergeStarted(ImsCallSession newSession, ImsCallProfile profile)
+ throws RemoteException {
+ mListener.callSessionMergeStarted(newSession != null ? newSession.getSession() : null,
+ profile);
+ }
+
+ /**
+ * Called when the session merge is successful and the merged session is active.
+ *
+ * @param newSession the new session object that is used for the conference
+ */
+ public void callSessionMergeComplete(ImsCallSession newSession) throws RemoteException {
+ mListener.callSessionMergeComplete(newSession != null ? newSession.getSession() : null);
+ }
+
+ /**
+ * Called when the session merge has failed.
+ *
+ * @param reasonInfo detailed reason of the call merge failure
+ */
+ public void callSessionMergeFailed(ImsReasonInfo reasonInfo) throws RemoteException {
+ mListener.callSessionMergeFailed(reasonInfo);
+ }
+
+ /**
+ * Called when the session is updated (except for hold/unhold).
+ */
+ public void callSessionUpdated(ImsCallProfile profile) throws RemoteException {
+ mListener.callSessionUpdated(profile);
+ }
+
+ /**
+ * Called when the session update has failed.
+ *
+ * @param reasonInfo detailed reason of the session update failure
+ */
+ public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) throws RemoteException {
+ mListener.callSessionUpdateFailed(reasonInfo);
+ }
+
+ /**
+ * Called when the session update is received from the remote user.
+ */
+ public void callSessionUpdateReceived(ImsCallProfile profile) throws RemoteException {
+ mListener.callSessionUpdateReceived(profile);
+ }
+
+ /**
+ * Called when the session has been extended to a conference session.
+ *
+ * @param newSession the session object that is extended to the conference
+ * from the active session
+ */
+ public void callSessionConferenceExtended(ImsCallSession newSession, ImsCallProfile profile)
+ throws RemoteException {
+ mListener.callSessionConferenceExtended(newSession != null ? newSession.getSession() : null,
+ profile);
+ }
+
+ /**
+ * Called when the conference extension has failed.
+ *
+ * @param reasonInfo detailed reason of the conference extension failure
+ */
+ public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) throws RemoteException {
+ mListener.callSessionConferenceExtendFailed(reasonInfo);
+ }
+
+ /**
+ * Called when the conference extension is received from the remote user.
+ */
+ public void callSessionConferenceExtendReceived(ImsCallSession newSession,
+ ImsCallProfile profile) throws RemoteException {
+ mListener.callSessionConferenceExtendReceived(newSession != null
+ ? newSession.getSession() : null, profile);
+ }
+
+ /**
+ * Called when the invitation request of the participants is delivered to the conference
+ * server.
+ */
+ public void callSessionInviteParticipantsRequestDelivered() throws RemoteException {
+ mListener.callSessionInviteParticipantsRequestDelivered();
+ }
+
+ /**
+ * Called when the invitation request of the participants has failed.
+ *
+ * @param reasonInfo detailed reason of the conference invitation failure
+ */
+ public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo)
+ throws RemoteException {
+ mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
+ }
+
+ /**
+ * Called when the removal request of the participants is delivered to the conference
+ * server.
+ */
+ public void callSessionRemoveParticipantsRequestDelivered() throws RemoteException {
+ mListener.callSessionRemoveParticipantsRequestDelivered();
+ }
+
+ /**
+ * Called when the removal request of the participants has failed.
+ *
+ * @param reasonInfo detailed reason of the conference removal failure
+ */
+ public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo)
+ throws RemoteException {
+ mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
+ }
+
+ /**
+ * Notifies the framework of the updated Call session conference state.
+ *
+ * @param state the new {@link ImsConferenceState} associated with the conference.
+ */
+ public void callSessionConferenceStateUpdated(ImsConferenceState state) throws RemoteException {
+ mListener.callSessionConferenceStateUpdated(state);
+ }
+
+ /**
+ * Notifies the incoming USSD message.
+ */
+ public void callSessionUssdMessageReceived(int mode, String ussdMessage)
+ throws RemoteException {
+ mListener.callSessionUssdMessageReceived(mode, ussdMessage);
+ }
+
+ /**
+ * Notifies of a case where a {@link com.android.ims.internal.ImsCallSession} may potentially
+ * handover from one radio technology to another.
+ *
+ * @param srcAccessTech The source radio access technology; one of the access technology
+ * constants defined in {@link android.telephony.ServiceState}. For
+ * example
+ * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+ * @param targetAccessTech The target radio access technology; one of the access technology
+ * constants defined in {@link android.telephony.ServiceState}. For
+ * example
+ * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+ */
+ public void callSessionMayHandover(int srcAccessTech, int targetAccessTech)
+ throws RemoteException {
+ mListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
+ }
+
+ /**
+ * Called when session access technology changes.
+ *
+ * @param srcAccessTech original access technology
+ * @param targetAccessTech new access technology
+ * @param reasonInfo
+ */
+ public void callSessionHandover(int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) throws RemoteException {
+ mListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
+ }
+
+ /**
+ * Called when session access technology change fails.
+ *
+ * @param srcAccessTech original access technology
+ * @param targetAccessTech new access technology
+ * @param reasonInfo handover failure reason
+ */
+ public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) throws RemoteException {
+ mListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
+ }
+
+ /**
+ * Called when the TTY mode is changed by the remote party.
+ *
+ * @param mode one of the following: -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
+ */
+ public void callSessionTtyModeReceived(int mode) throws RemoteException {
+ mListener.callSessionTtyModeReceived(mode);
+ }
+
+ /**
+ * Called when the multiparty state is changed for this {@code ImsCallSession}.
+ *
+ * @param isMultiParty {@code true} if the session became multiparty,
+ * {@code false} otherwise.
+ */
+
+ public void callSessionMultipartyStateChanged(boolean isMultiParty) throws RemoteException {
+ mListener.callSessionMultipartyStateChanged(isMultiParty);
+ }
+
+ /**
+ * Called when the supplementary service information is received for the current session.
+ */
+ public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppSrvNotification)
+ throws RemoteException {
+ mListener.callSessionSuppServiceReceived(suppSrvNotification);
+ }
+
+ /**
+ * Received RTT modify request from the remote party.
+ *
+ * @param callProfile ImsCallProfile with updated attributes
+ */
+ public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile)
+ throws RemoteException {
+ mListener.callSessionRttModifyRequestReceived(callProfile);
+ }
+
+ /**
+ * @param status the received response for RTT modify request.
+ */
+ public void callSessionRttModifyResponseReceived(int status) throws RemoteException {
+ mListener.callSessionRttModifyResponseReceived(status);
+ }
+
+ /**
+ * Device received RTT message from Remote UE.
+ *
+ * @param rttMessage RTT message received
+ */
+ public void callSessionRttMessageReceived(String rttMessage) throws RemoteException {
+ mListener.callSessionRttMessageReceived(rttMessage);
+ }
+}
+
diff --git a/telephony/java/android/telephony/ims/internal/ImsService.java b/telephony/java/android/telephony/ims/internal/ImsService.java
new file mode 100644
index 0000000..b7c8ca0
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/ImsService.java
@@ -0,0 +1,339 @@
+/*
+ * 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.telephony.ims.internal;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ims.internal.aidl.IImsConfig;
+import android.telephony.ims.internal.aidl.IImsMmTelFeature;
+import android.telephony.ims.internal.aidl.IImsRcsFeature;
+import android.telephony.ims.internal.aidl.IImsRegistration;
+import android.telephony.ims.internal.aidl.IImsServiceController;
+import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
+import android.telephony.ims.internal.feature.ImsFeature;
+import android.telephony.ims.internal.feature.MmTelFeature;
+import android.telephony.ims.internal.feature.RcsFeature;
+import android.telephony.ims.internal.stub.ImsConfigImplBase;
+import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
+ * ImsService must register the service in their AndroidManifest to be detected by the framework.
+ * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
+ * permission. Then, the ImsService definition in the manifest must follow the following format:
+ *
+ * ...
+ * <service android:name=".EgImsService"
+ * android:permission="android.permission.BIND_IMS_SERVICE" >
+ * <!-- Apps must declare which features they support as metadata. The different categories are
+ * defined below. In this example, the RCS_FEATURE feature is supported. -->
+ * <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
+ * <intent-filter>
+ * <action android:name="android.telephony.ims.ImsService" />
+ * </intent-filter>
+ * </service>
+ * ...
+ *
+ * The telephony framework will then bind to the ImsService you have defined in your manifest
+ * if you are either:
+ * 1) Defined as the default ImsService for the device in the device overlay using
+ * "config_ims_package".
+ * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
+ * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
+ *
+ * The features that are currently supported in an ImsService are:
+ * - RCS_FEATURE: This ImsService implements the RcsFeature class.
+ * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class.
+ * @hide
+ */
+public class ImsService extends Service {
+
+ private static final String LOG_TAG = "ImsService";
+
+ /**
+ * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
+ * @hide
+ */
+ public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
+
+ // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
+ // slot.
+ // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
+ // call ImsFeature#onFeatureRemoved.
+ private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
+
+ private IImsServiceControllerListener mListener;
+
+
+ /**
+ * Listener that notifies the framework of ImsService changes.
+ */
+ public static class Listener extends IImsServiceControllerListener.Stub {
+ /**
+ * The IMS features that this ImsService supports has changed.
+ * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s
+ * that this ImsService supports. This may trigger the addition/removal of feature
+ * in this service.
+ */
+ public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
+ }
+ }
+
+ /**
+ * @hide
+ */
+ protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
+ @Override
+ public void setListener(IImsServiceControllerListener l) {
+ mListener = l;
+ }
+
+ @Override
+ public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
+ return createMmTelFeatureInternal(slotId, c);
+ }
+
+ @Override
+ public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
+ return createRcsFeatureInternal(slotId, c);
+ }
+
+ @Override
+ public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+ throws RemoteException {
+ ImsService.this.removeImsFeature(slotId, featureType, c);
+ }
+
+ @Override
+ public ImsFeatureConfiguration querySupportedImsFeatures() {
+ return ImsService.this.querySupportedImsFeatures();
+ }
+
+ @Override
+ public void notifyImsServiceReadyForFeatureCreation() {
+ ImsService.this.readyForFeatureCreation();
+ }
+
+ @Override
+ public void notifyImsFeatureReady(int slotId, int featureType)
+ throws RemoteException {
+ ImsService.this.notifyImsFeatureReady(slotId, featureType);
+ }
+
+ @Override
+ public IImsConfig getConfig(int slotId) throws RemoteException {
+ ImsConfigImplBase c = ImsService.this.getConfig(slotId);
+ return c != null ? c.getBinder() : null;
+ }
+
+ @Override
+ public IImsRegistration getRegistration(int slotId) throws RemoteException {
+ ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
+ return r != null ? r.getBinder() : null;
+ }
+ };
+
+ /**
+ * @hide
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ if(SERVICE_INTERFACE.equals(intent.getAction())) {
+ Log.i(LOG_TAG, "ImsService Bound.");
+ return mImsServiceController;
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public SparseArray<ImsFeature> getFeatures(int slotId) {
+ return mFeaturesBySlot.get(slotId);
+ }
+
+ private IImsMmTelFeature createMmTelFeatureInternal(int slotId,
+ IImsFeatureStatusCallback c) {
+ MmTelFeature f = createMmTelFeature(slotId);
+ if (f != null) {
+ setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c);
+ return f.getBinder();
+ } else {
+ Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
+ return null;
+ }
+ }
+
+ private IImsRcsFeature createRcsFeatureInternal(int slotId,
+ IImsFeatureStatusCallback c) {
+ RcsFeature f = createRcsFeature(slotId);
+ if (f != null) {
+ setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c);
+ return f.getBinder();
+ } else {
+ Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
+ return null;
+ }
+ }
+
+ private void setupFeature(ImsFeature f, int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ f.addImsFeatureStatusCallback(c);
+ f.initialize(this, slotId);
+ addImsFeature(slotId, featureType, f);
+ }
+
+ private void addImsFeature(int slotId, int featureType, ImsFeature f) {
+ synchronized (mFeaturesBySlot) {
+ // Get SparseArray for Features, by querying slot Id
+ SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+ if (features == null) {
+ // Populate new SparseArray of features if it doesn't exist for this slot yet.
+ features = new SparseArray<>();
+ mFeaturesBySlot.put(slotId, features);
+ }
+ features.put(featureType, f);
+ }
+ }
+
+ private void removeImsFeature(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ synchronized (mFeaturesBySlot) {
+ // get ImsFeature associated with the slot/feature
+ SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+ if (features == null) {
+ Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
+ + slotId);
+ return;
+ }
+ ImsFeature f = features.get(featureType);
+ if (f == null) {
+ Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
+ + featureType + " exists on slot " + slotId);
+ return;
+ }
+ f.removeImsFeatureStatusCallback(c);
+ f.onFeatureRemoved();
+ features.remove(featureType);
+ }
+ }
+
+ private void notifyImsFeatureReady(int slotId, int featureType) {
+ synchronized (mFeaturesBySlot) {
+ // get ImsFeature associated with the slot/feature
+ SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+ if (features == null) {
+ Log.w(LOG_TAG, "Can not notify ImsFeature ready. No ImsFeatures exist on " +
+ "slot " + slotId);
+ return;
+ }
+ ImsFeature f = features.get(featureType);
+ if (f == null) {
+ Log.w(LOG_TAG, "Can not notify ImsFeature ready. No feature with type "
+ + featureType + " exists on slot " + slotId);
+ return;
+ }
+ f.onFeatureReady();
+ }
+ }
+
+ /**
+ * When called, provide the {@link ImsFeatureConfiguration} that this ImsService currently
+ * supports. This will trigger the framework to set up the {@link ImsFeature}s that correspond
+ * to the {@link ImsFeature.FeatureType}s configured here.
+ * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports,
+ * defined in {@link ImsFeature.FeatureType}.
+ */
+ public ImsFeatureConfiguration querySupportedImsFeatures() {
+ // Return empty for base implementation
+ return new ImsFeatureConfiguration();
+ }
+
+ /**
+ * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated
+ * features, defined in {@link ImsFeature.FeatureType} that this ImsService supports. This may
+ * trigger the framework to add/remove new ImsFeatures, depending on the configuration.
+ */
+ public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)
+ throws RemoteException {
+ if (mListener == null) {
+ throw new IllegalStateException("Framework is not ready");
+ }
+ mListener.onUpdateSupportedImsFeatures(c);
+ }
+
+ /**
+ * The ImsService has been bound and is ready for ImsFeature creation based on the Features that
+ * the ImsService has registered for with the framework, either in the manifest or via
+ * The ImsService should use this signal instead of onCreate/onBind or similar to perform
+ * feature initialization because the framework may bind to this service multiple times to
+ * query the ImsService's {@link ImsFeatureConfiguration} via
+ * {@link #querySupportedImsFeatures()}before creating features.
+ */
+ public void readyForFeatureCreation() {
+ }
+
+ /**
+ * When called, the framework is requesting that a new MmTelFeature is created for the specified
+ * slot.
+ *
+ * @param slotId The slot ID that the MMTel Feature is being created for.
+ * @return The newly created MmTelFeature associated with the slot or null if the feature is not
+ * supported.
+ */
+ public MmTelFeature createMmTelFeature(int slotId) {
+ return null;
+ }
+
+ /**
+ * When called, the framework is requesting that a new RcsFeature is created for the specified
+ * slot
+ *
+ * @param slotId The slot ID that the RCS Feature is being created for.
+ * @return The newly created RcsFeature associated with the slot or null if the feature is not
+ * supported.
+ */
+ public RcsFeature createRcsFeature(int slotId) {
+ return null;
+ }
+
+ /**
+ * @param slotId The slot that the IMS configuration is associated with.
+ * @return ImsConfig implementation that is associated with the specified slot.
+ */
+ public ImsConfigImplBase getConfig(int slotId) {
+ return new ImsConfigImplBase();
+ }
+
+ /**
+ * @param slotId The slot that is associated with the IMS Registration.
+ * @return the ImsRegistration implementation associated with the slot.
+ */
+ public ImsRegistrationImplBase getRegistration(int slotId) {
+ return new ImsRegistrationImplBase();
+ }
+}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl
new file mode 100644
index 0000000..2fb6744
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2013 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.telephony.ims.internal.aidl;
+
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsConferenceState;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.ImsSuppServiceNotification;
+
+/**
+ * A listener type for receiving notification on IMS call session events.
+ * When an event is generated for an {@link IImsCallSession}, the application is notified
+ * by having one of the methods called on the {@link IImsCallSessionListener}.
+ * {@hide}
+ */
+oneway interface IImsCallSessionListener {
+ /**
+ * Notifies the result of the basic session operation (setup / terminate).
+ */
+ void callSessionProgressing(in ImsStreamMediaProfile profile);
+ void callSessionInitiated(in ImsCallProfile profile);
+ void callSessionInitiatedFailed(in ImsReasonInfo reasonInfo);
+ void callSessionTerminated(in ImsReasonInfo reasonInfo);
+
+ /**
+ * Notifies the result of the call hold/resume operation.
+ */
+ void callSessionHeld(in ImsCallProfile profile);
+ void callSessionHoldFailed(in ImsReasonInfo reasonInfo);
+ void callSessionHoldReceived(in ImsCallProfile profile);
+ void callSessionResumed(in ImsCallProfile profile);
+ void callSessionResumeFailed(in ImsReasonInfo reasonInfo);
+ void callSessionResumeReceived(in ImsCallProfile profile);
+
+ /**
+ * Notifies the result of call merge operation.
+ */
+ void callSessionMergeStarted(IImsCallSession newSession, in ImsCallProfile profile);
+ void callSessionMergeComplete(IImsCallSession session);
+ void callSessionMergeFailed(in ImsReasonInfo reasonInfo);
+
+ /**
+ * Notifies the result of call upgrade / downgrade or any other call updates.
+ */
+ void callSessionUpdated(in ImsCallProfile profile);
+ void callSessionUpdateFailed(in ImsReasonInfo reasonInfo);
+ void callSessionUpdateReceived(in ImsCallProfile profile);
+
+ /**
+ * Notifies the result of conference extension.
+ */
+ void callSessionConferenceExtended(IImsCallSession newSession, in ImsCallProfile profile);
+ void callSessionConferenceExtendFailed(in ImsReasonInfo reasonInfo);
+ void callSessionConferenceExtendReceived(IImsCallSession newSession,
+ in ImsCallProfile profile);
+
+ /**
+ * Notifies the result of the participant invitation / removal to/from the conference session.
+ */
+ void callSessionInviteParticipantsRequestDelivered();
+ void callSessionInviteParticipantsRequestFailed(in ImsReasonInfo reasonInfo);
+ void callSessionRemoveParticipantsRequestDelivered();
+ void callSessionRemoveParticipantsRequestFailed(in ImsReasonInfo reasonInfo);
+
+ /**
+ * Notifies the changes of the conference info. in the conference session.
+ */
+ void callSessionConferenceStateUpdated(in ImsConferenceState state);
+
+ /**
+ * Notifies the incoming USSD message.
+ */
+ void callSessionUssdMessageReceived(int mode, String ussdMessage);
+
+ /**
+ * Notifies of handover information for this call
+ */
+ void callSessionHandover(int srcAccessTech, int targetAccessTech,
+ in ImsReasonInfo reasonInfo);
+ void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+ in ImsReasonInfo reasonInfo);
+ void callSessionMayHandover(int srcAccessTech, int targetAccessTech);
+
+ /**
+ * Notifies the TTY mode change by remote party.
+ * @param mode one of the following:
+ * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF}
+ * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL}
+ * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO}
+ * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
+ */
+ void callSessionTtyModeReceived(int mode);
+
+ /**
+ * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
+ *
+ * @param session The call session.
+ * @param isMultiParty {@code true} if the session became multiparty, {@code false} otherwise.
+ */
+ void callSessionMultipartyStateChanged(boolean isMultiParty);
+
+ /**
+ * Notifies the supplementary service information for the current session.
+ */
+ void callSessionSuppServiceReceived(in ImsSuppServiceNotification suppSrvNotification);
+
+ /**
+ * Device received RTT modify request from Remote UE
+ * @param session ImsCallProfile with updated attribute
+ */
+ void callSessionRttModifyRequestReceived(in ImsCallProfile callProfile);
+
+ /* Device issued RTT modify request and inturn received response
+ * from Remote UE
+ * @param status Will be one of the following values from:
+ * - {@link Connection.RttModifyStatus}
+ */
+ void callSessionRttModifyResponseReceived(int status);
+
+ /*
+ * While in call, device received RTT message from Remote UE
+ * @param rttMessage Received RTT message
+ */
+ void callSessionRttMessageReceived(in String rttMessage);
+}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl
new file mode 100644
index 0000000..fd2eb24
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.telephony.ims.internal.aidl;
+
+/**
+ * See ImsFeature#CapabilityCallback for more information.
+ * {@hide}
+ */
+oneway interface IImsCapabilityCallback {
+ void onQueryCapabilityConfiguration(int capability, int radioTech, boolean enabled);
+ void onChangeCapabilityConfigurationError(int capability, int radioTech, int reason);
+ void onCapabilitiesStatusChanged(int config);
+}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl
new file mode 100644
index 0000000..3d424a3
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2013 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.telephony.ims.internal.aidl;
+
+import android.telephony.ims.internal.aidl.IImsConfigCallback;
+
+import com.android.ims.ImsConfigListener;
+
+/**
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include items provisioned by the operator.
+ *
+ * {@hide}
+ */
+interface IImsConfig {
+
+ void addImsConfigCallback(IImsConfigCallback c);
+ void removeImsConfigCallback(IImsConfigCallback c);
+ int getConfigInt(int item);
+ String getConfigString(int item);
+ // Return result code defined in ImsConfig#OperationStatusConstants
+ int setConfigInt(int item, int value);
+ // Return result code defined in ImsConfig#OperationStatusConstants
+ int setConfigString(int item, String value);
+}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl
new file mode 100644
index 0000000..52efd23
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2013 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.telephony.ims.internal.aidl;
+
+/**
+ * Provides callback interface for ImsConfig when a value has changed.
+ *
+ * {@hide}
+ */
+oneway interface IImsConfigCallback {
+ void onIntConfigChanged(int item, int value);
+ void onStringConfigChanged(int item, String value);
+}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl
new file mode 100644
index 0000000..7125781
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl
@@ -0,0 +1,52 @@
+/*
+ * 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.telephony.ims.internal.aidl;
+
+import android.os.Message;
+import android.telephony.ims.internal.aidl.IImsMmTelListener;
+import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
+import android.telephony.ims.internal.aidl.IImsCallSessionListener;
+import android.telephony.ims.internal.feature.CapabilityChangeRequest;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsUt;
+
+/**
+ * See MmTelFeature for more information.
+ * {@hide}
+ */
+interface IImsMmTelFeature {
+ void setListener(IImsMmTelListener l);
+ int getFeatureState();
+ ImsCallProfile createCallProfile(int callSessionType, int callType);
+ IImsCallSession createCallSession(in ImsCallProfile profile, IImsCallSessionListener listener);
+ IImsUt getUtInterface();
+ IImsEcbm getEcbmInterface();
+ void setUiTtyMode(int uiTtyMode, in Message onCompleteMessage);
+ IImsMultiEndpoint getMultiEndpointInterface();
+ int queryCapabilityStatus();
+ oneway void addCapabilityCallback(IImsCapabilityCallback c);
+ oneway void removeCapabilityCallback(IImsCapabilityCallback c);
+ oneway void changeCapabilitiesConfiguration(in CapabilityChangeRequest request,
+ IImsCapabilityCallback c);
+ oneway void queryCapabilityConfiguration(int capability, int radioTech,
+ IImsCapabilityCallback c);
+}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
new file mode 100644
index 0000000..8332bc0
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.telephony.ims.internal.aidl;
+
+import com.android.ims.internal.IImsCallSession;
+
+/**
+ * See MmTelFeature#Listener for more information.
+ * {@hide}
+ */
+oneway interface IImsMmTelListener {
+ void onIncomingCall(IImsCallSession c);
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl
new file mode 100644
index 0000000..f6005b6
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.telephony.ims.internal.aidl;
+
+/**
+ * See RcsFeature for more information.
+ * {@hide}
+ */
+interface IImsRcsFeature {
+ //Empty Default Implementation
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl
new file mode 100644
index 0000000..687b7ca
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013 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.telephony.ims.internal.aidl;
+
+import android.telephony.ims.internal.aidl.IImsRegistrationCallback;
+import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+
+/**
+ * See ImsRegistration for more information.
+ *
+ * {@hide}
+ */
+interface IImsRegistration {
+ int getRegistrationTechnology();
+ oneway void addRegistrationCallback(IImsRegistrationCallback c);
+ oneway void removeRegistrationCallback(IImsRegistrationCallback c);
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl
new file mode 100644
index 0000000..a50575b
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013 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.telephony.ims.internal.aidl;
+
+import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+
+import com.android.ims.ImsReasonInfo;
+
+/**
+ * See ImsRegistrationImplBase.Callback for more information.
+ *
+ * {@hide}
+ */
+oneway interface IImsRegistrationCallback {
+ void onRegistered(int imsRadioTech);
+ void onRegistering(int imsRadioTech);
+ void onDeregistered(in ImsReasonInfo info);
+ void onTechnologyChangeFailed(int imsRadioTech, in ImsReasonInfo info);
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
new file mode 100644
index 0000000..8afb955
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.telephony.ims.internal.aidl;
+
+import android.telephony.ims.internal.aidl.IImsMmTelFeature;
+import android.telephony.ims.internal.aidl.IImsRcsFeature;
+import android.telephony.ims.internal.aidl.IImsRegistration;
+import android.telephony.ims.internal.aidl.IImsConfig;
+import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
+import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+
+/**
+ * See ImsService and MmTelFeature for more information.
+ * {@hide}
+ */
+interface IImsServiceController {
+ void setListener(IImsServiceControllerListener l);
+ IImsMmTelFeature createMmTelFeature(int slotId, in IImsFeatureStatusCallback c);
+ IImsRcsFeature createRcsFeature(int slotId, in IImsFeatureStatusCallback c);
+ ImsFeatureConfiguration querySupportedImsFeatures();
+ // Synchronous call to ensure the ImsService is ready before continuing with feature creation.
+ void notifyImsServiceReadyForFeatureCreation();
+ // Synchronous call to ensure the new ImsFeature is ready before using the Feature.
+ void notifyImsFeatureReady(int slotId, int featureType);
+ void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c);
+ IImsConfig getConfig(int slotId);
+ IImsRegistration getRegistration(int slotId);
+}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl
new file mode 100644
index 0000000..01cca2db
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.telephony.ims.internal.aidl;
+
+import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+
+/**
+ * See ImsService#Listener for more information.
+ * {@hide}
+ */
+oneway interface IImsServiceControllerListener {
+ void onUpdateSupportedImsFeatures(in ImsFeatureConfiguration c);
+}
diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl
new file mode 100644
index 0000000..f4ec0eb
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.telephony.ims.internal.feature;
+
+parcelable CapabilityChangeRequest;
diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
new file mode 100644
index 0000000..4d18873
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
@@ -0,0 +1,197 @@
+/*
+ * 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.telephony.ims.internal.feature;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
+import android.util.ArraySet;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Request to send to IMS provider, which will try to enable/disable capabilities that are added to
+ * the request.
+ * {@hide}
+ */
+public class CapabilityChangeRequest implements Parcelable {
+
+ public static class CapabilityPair {
+ private final int mCapability;
+ private final int radioTech;
+
+ public CapabilityPair(int capability, int radioTech) {
+ this.mCapability = capability;
+ this.radioTech = radioTech;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof CapabilityPair)) return false;
+
+ CapabilityPair that = (CapabilityPair) o;
+
+ if (getCapability() != that.getCapability()) return false;
+ return getRadioTech() == that.getRadioTech();
+ }
+
+ @Override
+ public int hashCode() {
+ int result = getCapability();
+ result = 31 * result + getRadioTech();
+ return result;
+ }
+
+ public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() {
+ return mCapability;
+ }
+
+ public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() {
+ return radioTech;
+ }
+ }
+
+ // Pair contains <radio tech, mCapability>
+ private final Set<CapabilityPair> mCapabilitiesToEnable;
+ // Pair contains <radio tech, mCapability>
+ private final Set<CapabilityPair> mCapabilitiesToDisable;
+
+ public CapabilityChangeRequest() {
+ mCapabilitiesToEnable = new ArraySet<>();
+ mCapabilitiesToDisable = new ArraySet<>();
+ }
+
+ /**
+ * Add one or many capabilities to the request to be enabled.
+ *
+ * @param capabilities A bitfield of capabilities to enable, valid values are defined in
+ * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
+ * @param radioTech the radio tech that these capabilities should be enabled for, valid
+ * values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+ */
+ public void addCapabilitiesToEnableForTech(
+ @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+ addAllCapabilities(mCapabilitiesToEnable, capabilities, radioTech);
+ }
+
+ /**
+ * Add one or many capabilities to the request to be disabled.
+ * @param capabilities A bitfield of capabilities to diable, valid values are defined in
+ * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
+ * @param radioTech the radio tech that these capabilities should be disabled for, valid
+ * values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+ */
+ public void addCapabilitiesToDisableForTech(
+ @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+ addAllCapabilities(mCapabilitiesToDisable, capabilities, radioTech);
+ }
+
+ /**
+ * @return a {@link List} of {@link CapabilityPair}s that are requesting to be enabled.
+ */
+ public List<CapabilityPair> getCapabilitiesToEnable() {
+ return new ArrayList<>(mCapabilitiesToEnable);
+ }
+
+ /**
+ * @return a {@link List} of {@link CapabilityPair}s that are requesting to be disabled.
+ */
+ public List<CapabilityPair> getCapabilitiesToDisable() {
+ return new ArrayList<>(mCapabilitiesToDisable);
+ }
+
+ // Iterate through capabilities bitfield and add each one as a pair associated with the radio
+ // technology
+ private void addAllCapabilities(Set<CapabilityPair> set, int capabilities, int tech) {
+ long highestCapability = Long.highestOneBit(capabilities);
+ for (int i = 1; i <= highestCapability; i *= 2) {
+ if ((i & capabilities) > 0) {
+ set.add(new CapabilityPair(/*capability*/ i, /*radioTech*/ tech));
+ }
+ }
+ }
+
+ protected CapabilityChangeRequest(Parcel in) {
+ int enableSize = in.readInt();
+ mCapabilitiesToEnable = new ArraySet<>(enableSize);
+ for (int i = 0; i < enableSize; i++) {
+ mCapabilitiesToEnable.add(new CapabilityPair(/*capability*/ in.readInt(),
+ /*radioTech*/ in.readInt()));
+ }
+ int disableSize = in.readInt();
+ mCapabilitiesToDisable = new ArraySet<>(disableSize);
+ for (int i = 0; i < disableSize; i++) {
+ mCapabilitiesToDisable.add(new CapabilityPair(/*capability*/ in.readInt(),
+ /*radioTech*/ in.readInt()));
+ }
+ }
+
+ public static final Creator<CapabilityChangeRequest> CREATOR =
+ new Creator<CapabilityChangeRequest>() {
+ @Override
+ public CapabilityChangeRequest createFromParcel(Parcel in) {
+ return new CapabilityChangeRequest(in);
+ }
+
+ @Override
+ public CapabilityChangeRequest[] newArray(int size) {
+ return new CapabilityChangeRequest[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mCapabilitiesToEnable.size());
+ for (CapabilityPair pair : mCapabilitiesToEnable) {
+ dest.writeInt(pair.getCapability());
+ dest.writeInt(pair.getRadioTech());
+ }
+ dest.writeInt(mCapabilitiesToDisable.size());
+ for (CapabilityPair pair : mCapabilitiesToDisable) {
+ dest.writeInt(pair.getCapability());
+ dest.writeInt(pair.getRadioTech());
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof CapabilityChangeRequest)) return false;
+
+ CapabilityChangeRequest that = (CapabilityChangeRequest) o;
+
+ if (!mCapabilitiesToEnable.equals(that.mCapabilitiesToEnable)) return false;
+ return mCapabilitiesToDisable.equals(that.mCapabilitiesToDisable);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mCapabilitiesToEnable.hashCode();
+ result = 31 * result + mCapabilitiesToDisable.hashCode();
+ return result;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java b/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java
new file mode 100644
index 0000000..9f82ad2
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java
@@ -0,0 +1,462 @@
+/*
+ * 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.telephony.ims.internal.feature;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IInterface;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
+import android.util.Log;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ * Base class for all IMS features that are supported by the framework.
+ *
+ * @hide
+ */
+public abstract class ImsFeature {
+
+ private static final String LOG_TAG = "ImsFeature";
+
+ /**
+ * Action to broadcast when ImsService is up.
+ * Internal use only.
+ * Only defined here separately for compatibility purposes with the old ImsService.
+ *
+ * @hide
+ */
+ public static final String ACTION_IMS_SERVICE_UP =
+ "com.android.ims.IMS_SERVICE_UP";
+
+ /**
+ * Action to broadcast when ImsService is down.
+ * Internal use only.
+ * Only defined here separately for compatibility purposes with the old ImsService.
+ *
+ * @hide
+ */
+ public static final String ACTION_IMS_SERVICE_DOWN =
+ "com.android.ims.IMS_SERVICE_DOWN";
+
+ /**
+ * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
+ * A long value; the phone ID corresponding to the IMS service coming up or down.
+ * Only defined here separately for compatibility purposes with the old ImsService.
+ *
+ * @hide
+ */
+ public static final String EXTRA_PHONE_ID = "android:phone_id";
+
+ // Invalid feature value
+ public static final int FEATURE_INVALID = -1;
+ // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
+ // defined values in ImsServiceClass for compatibility purposes.
+ public static final int FEATURE_EMERGENCY_MMTEL = 0;
+ public static final int FEATURE_MMTEL = 1;
+ public static final int FEATURE_RCS = 2;
+ // Total number of features defined
+ public static final int FEATURE_MAX = 3;
+
+ // Integer values defining IMS features that are supported in ImsFeature.
+ @IntDef(flag = true,
+ value = {
+ FEATURE_EMERGENCY_MMTEL,
+ FEATURE_MMTEL,
+ FEATURE_RCS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FeatureType {}
+
+ // Integer values defining the state of the ImsFeature at any time.
+ @IntDef(flag = true,
+ value = {
+ STATE_UNAVAILABLE,
+ STATE_INITIALIZING,
+ STATE_READY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsState {}
+
+ public static final int STATE_UNAVAILABLE = 0;
+ public static final int STATE_INITIALIZING = 1;
+ public static final int STATE_READY = 2;
+
+ // Integer values defining the result codes that should be returned from
+ // {@link changeEnabledCapabilities} when the framework tries to set a feature's capability.
+ @IntDef(flag = true,
+ value = {
+ CAPABILITY_ERROR_GENERIC,
+ CAPABILITY_SUCCESS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsCapabilityError {}
+
+ public static final int CAPABILITY_ERROR_GENERIC = -1;
+ public static final int CAPABILITY_SUCCESS = 0;
+
+
+ /**
+ * The framework implements this callback in order to register for Feature Capability status
+ * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability
+ * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error
+ * callbacks when the ImsService can not change the capability as requested, via
+ * {@link #onChangeCapabilityConfigurationError}.
+ */
+ public static class CapabilityCallback extends IImsCapabilityCallback.Stub {
+
+ @Override
+ public final void onCapabilitiesStatusChanged(int config) throws RemoteException {
+ onCapabilitiesStatusChanged(new Capabilities(config));
+ }
+
+ /**
+ * Returns the result of a query for the capability configuration of a requested capability.
+ *
+ * @param capability The capability that was requested.
+ * @param radioTech The IMS radio technology associated with the capability.
+ * @param isEnabled true if the capability is enabled, false otherwise.
+ */
+ @Override
+ public void onQueryCapabilityConfiguration(int capability, int radioTech,
+ boolean isEnabled) {
+
+ }
+
+ /**
+ * Called when a change to the capability configuration has returned an error.
+ *
+ * @param capability The capability that was requested to be changed.
+ * @param radioTech The IMS radio technology associated with the capability.
+ * @param reason error associated with the failure to change configuration.
+ */
+ @Override
+ public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+ int reason) {
+ }
+
+ /**
+ * The status of the feature's capabilities has changed to either available or unavailable.
+ * If unavailable, the feature is not able to support the unavailable capability at this
+ * time.
+ *
+ * @param config The new availability of the capabilities.
+ */
+ public void onCapabilitiesStatusChanged(Capabilities config) {
+ }
+ }
+
+ /**
+ * Used by the ImsFeature to call back to the CapabilityCallback that the framework has
+ * provided.
+ */
+ protected static class CapabilityCallbackProxy {
+ private final IImsCapabilityCallback mCallback;
+
+ public CapabilityCallbackProxy(IImsCapabilityCallback c) {
+ mCallback = c;
+ }
+
+ /**
+ * This method notifies the provided framework callback that the request to change the
+ * indicated capability has failed and has not changed.
+ *
+ * @param capability The Capability that will be notified to the framework.
+ * @param radioTech The radio tech that this capability failed for.
+ * @param reason The reason this capability was unable to be changed.
+ */
+ public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+ @ImsCapabilityError int reason) {
+ try {
+ mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder.");
+ }
+ }
+
+ public void onQueryCapabilityConfiguration(int capability, int radioTech,
+ boolean isEnabled) {
+ try {
+ mCallback.onQueryCapabilityConfiguration(capability, radioTech, isEnabled);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "onQueryCapabilityConfiguration called on dead binder.");
+ }
+ }
+ }
+
+ /**
+ * Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask.
+ */
+ public static class Capabilities {
+ protected int mCapabilities = 0;
+
+ public Capabilities() {
+ }
+
+ protected Capabilities(int capabilities) {
+ mCapabilities = capabilities;
+ }
+
+ /**
+ * @param capabilities Capabilities to be added to the configuration in the form of a
+ * bit mask.
+ */
+ public void addCapabilities(int capabilities) {
+ mCapabilities |= capabilities;
+ }
+
+ /**
+ * @param capabilities Capabilities to be removed to the configuration in the form of a
+ * bit mask.
+ */
+ public void removeCapabilities(int capabilities) {
+ mCapabilities &= ~capabilities;
+ }
+
+ /**
+ * @return true if all of the capabilities specified are capable.
+ */
+ public boolean isCapable(int capabilities) {
+ return (mCapabilities & capabilities) == capabilities;
+ }
+
+ public Capabilities copy() {
+ return new Capabilities(mCapabilities);
+ }
+
+ /**
+ * @return a bitmask containing the capability flags directly.
+ */
+ public int getMask() {
+ return mCapabilities;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Capabilities)) return false;
+
+ Capabilities that = (Capabilities) o;
+
+ return mCapabilities == that.mCapabilities;
+ }
+
+ @Override
+ public int hashCode() {
+ return mCapabilities;
+ }
+ }
+
+ private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
+ new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
+ private @ImsState int mState = STATE_UNAVAILABLE;
+ private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+ private Context mContext;
+ private final Object mLock = new Object();
+ private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks
+ = new RemoteCallbackList<>();
+ private Capabilities mCapabilityStatus = new Capabilities();
+
+ public final void initialize(Context context, int slotId) {
+ mContext = context;
+ mSlotId = slotId;
+ }
+
+ public final int getFeatureState() {
+ synchronized (mLock) {
+ return mState;
+ }
+ }
+
+ protected final void setFeatureState(@ImsState int state) {
+ synchronized (mLock) {
+ if (mState != state) {
+ mState = state;
+ notifyFeatureState(state);
+ }
+ }
+ }
+
+ // Not final for testing, but shouldn't be extended!
+ @VisibleForTesting
+ public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
+ try {
+ // If we have just connected, send queued status.
+ c.notifyImsFeatureStatus(getFeatureState());
+ // Add the callback if the callback completes successfully without a RemoteException.
+ synchronized (mLock) {
+ mStatusCallbacks.add(c);
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+ }
+ }
+
+ @VisibleForTesting
+ // Not final for testing, but should not be extended!
+ public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
+ synchronized (mLock) {
+ mStatusCallbacks.remove(c);
+ }
+ }
+
+ /**
+ * Internal method called by ImsFeature when setFeatureState has changed.
+ */
+ private void notifyFeatureState(@ImsState int state) {
+ synchronized (mLock) {
+ for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
+ iter.hasNext(); ) {
+ IImsFeatureStatusCallback callback = iter.next();
+ try {
+ Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
+ callback.notifyImsFeatureStatus(state);
+ } catch (RemoteException e) {
+ // remove if the callback is no longer alive.
+ iter.remove();
+ Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+ }
+ }
+ }
+ sendImsServiceIntent(state);
+ }
+
+ /**
+ * Provide backwards compatibility using deprecated service UP/DOWN intents.
+ */
+ private void sendImsServiceIntent(@ImsState int state) {
+ if (mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ return;
+ }
+ Intent intent;
+ switch (state) {
+ case ImsFeature.STATE_UNAVAILABLE:
+ case ImsFeature.STATE_INITIALIZING:
+ intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+ break;
+ case ImsFeature.STATE_READY:
+ intent = new Intent(ACTION_IMS_SERVICE_UP);
+ break;
+ default:
+ intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+ }
+ intent.putExtra(EXTRA_PHONE_ID, mSlotId);
+ mContext.sendBroadcast(intent);
+ }
+
+ public final void addCapabilityCallback(IImsCapabilityCallback c) {
+ mCapabilityCallbacks.register(c);
+ }
+
+ public final void removeCapabilityCallback(IImsCapabilityCallback c) {
+ mCapabilityCallbacks.unregister(c);
+ }
+
+ /**
+ * @return the cached capabilities status for this feature.
+ */
+ @VisibleForTesting
+ public Capabilities queryCapabilityStatus() {
+ synchronized (mLock) {
+ return mCapabilityStatus.copy();
+ }
+ }
+
+ // Called internally to request the change of enabled capabilities.
+ @VisibleForTesting
+ public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request,
+ IImsCapabilityCallback c) throws RemoteException {
+ if (request == null) {
+ throw new IllegalArgumentException(
+ "ImsFeature#requestChangeEnabledCapabilities called with invalid params.");
+ }
+ changeEnabledCapabilities(request, new CapabilityCallbackProxy(c));
+ }
+
+ /**
+ * Called by the ImsFeature when the capabilities status has changed.
+ *
+ * @param c A {@link Capabilities} containing the new Capabilities status.
+ */
+ protected final void notifyCapabilitiesStatusChanged(Capabilities c) {
+ synchronized (mLock) {
+ mCapabilityStatus = c.copy();
+ }
+ int count = mCapabilityCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < count; i++) {
+ try {
+ mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged(
+ c.mCapabilities);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " +
+ "callback.");
+ }
+ }
+ } finally {
+ mCapabilityCallbacks.finishBroadcast();
+ }
+ }
+
+ /**
+ * Features should override this method to receive Capability preference change requests from
+ * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities
+ * in the {@link CapabilityChangeRequest} are not able to be completed due to an error,
+ * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for
+ * each failed capability.
+ *
+ * @param request A {@link CapabilityChangeRequest} containing requested capabilities to
+ * enable/disable.
+ * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework
+ * setting a subset of these capabilities fail, using
+ * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}.
+ */
+ public abstract void changeEnabledCapabilities(CapabilityChangeRequest request,
+ CapabilityCallbackProxy c);
+
+ /**
+ * Called when the framework is removing this feature and it needs to be cleaned up.
+ */
+ public abstract void onFeatureRemoved();
+
+ /**
+ * Called when the feature has been initialized and communication with the framework is set up.
+ * Any attempt by this feature to access the framework before this method is called will return
+ * with an {@link IllegalStateException}.
+ * The IMS provider should use this method to trigger registration for this feature on the IMS
+ * network, if needed.
+ */
+ public abstract void onFeatureReady();
+
+ /**
+ * @return Binder instance that the framework will use to communicate with this feature.
+ */
+ protected abstract IInterface getBinder();
+}
diff --git a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
new file mode 100644
index 0000000..f183a57
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
@@ -0,0 +1,422 @@
+/*
+ * 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.telephony.ims.internal.feature;
+
+import android.annotation.IntDef;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telecom.TelecomManager;
+import android.telephony.ims.internal.ImsCallSessionListener;
+import android.telephony.ims.internal.aidl.IImsCallSessionListener;
+import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
+import android.telephony.ims.internal.aidl.IImsMmTelFeature;
+import android.telephony.ims.internal.aidl.IImsMmTelListener;
+import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsEcbmImplBase;
+import android.telephony.ims.stub.ImsMultiEndpointImplBase;
+import android.telephony.ims.stub.ImsUtImplBase;
+import android.util.Log;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.ImsCallSession;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
+ *
+ * Any class wishing to use MmTelFeature should extend this class and implement all methods that the
+ * service supports.
+ * @hide
+ */
+
+public class MmTelFeature extends ImsFeature {
+
+ private static final String LOG_TAG = "MmTelFeature";
+
+ private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
+
+ @Override
+ public void setListener(IImsMmTelListener l) throws RemoteException {
+ synchronized (mLock) {
+ MmTelFeature.this.setListener(l);
+ }
+ }
+
+ @Override
+ public int getFeatureState() throws RemoteException {
+ synchronized (mLock) {
+ return MmTelFeature.this.getFeatureState();
+ }
+ }
+
+
+ @Override
+ public ImsCallProfile createCallProfile(int callSessionType, int callType)
+ throws RemoteException {
+ synchronized (mLock) {
+ return MmTelFeature.this.createCallProfile(callSessionType, callType);
+ }
+ }
+
+ @Override
+ public IImsCallSession createCallSession(ImsCallProfile profile,
+ IImsCallSessionListener listener) throws RemoteException {
+ synchronized (mLock) {
+ ImsCallSession s = MmTelFeature.this.createCallSession(profile,
+ new ImsCallSessionListener(listener));
+ return s != null ? s.getSession() : null;
+ }
+ }
+
+ @Override
+ public IImsUt getUtInterface() throws RemoteException {
+ synchronized (mLock) {
+ return MmTelFeature.this.getUt();
+ }
+ }
+
+ @Override
+ public IImsEcbm getEcbmInterface() throws RemoteException {
+ synchronized (mLock) {
+ return MmTelFeature.this.getEcbm();
+ }
+ }
+
+ @Override
+ public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
+ synchronized (mLock) {
+ MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
+ }
+ }
+
+ @Override
+ public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+ synchronized (mLock) {
+ return MmTelFeature.this.getMultiEndpoint();
+ }
+ }
+
+ @Override
+ public int queryCapabilityStatus() throws RemoteException {
+ return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
+ }
+
+ @Override
+ public void addCapabilityCallback(IImsCapabilityCallback c) {
+ MmTelFeature.this.addCapabilityCallback(c);
+ }
+
+ @Override
+ public void removeCapabilityCallback(IImsCapabilityCallback c) {
+ MmTelFeature.this.removeCapabilityCallback(c);
+ }
+
+ @Override
+ public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
+ IImsCapabilityCallback c) throws RemoteException {
+ MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
+ }
+
+ @Override
+ public void queryCapabilityConfiguration(int capability, int radioTech,
+ IImsCapabilityCallback c) {
+ queryCapabilityConfigurationInternal(capability, radioTech, c);
+ }
+ };
+
+ /**
+ * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask.
+ * The capabilities that are used in MmTelFeature are defined by {@link MmTelCapability}.
+ *
+ * The capabilities of this MmTelFeature will be set by the framework and can be queried with
+ * {@link #queryCapabilityStatus()}.
+ *
+ * This MmTelFeature can then return the status of each of these capabilities (enabled or not)
+ * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current
+ * status can also be queried using {@link #queryCapabilityStatus()}.
+ */
+ public static class MmTelCapabilities extends Capabilities {
+
+ @VisibleForTesting
+ public MmTelCapabilities() {
+ super();
+ }
+
+ public MmTelCapabilities(Capabilities c) {
+ mCapabilities = c.mCapabilities;
+ }
+
+ @IntDef(flag = true,
+ value = {
+ CAPABILITY_TYPE_VOICE,
+ CAPABILITY_TYPE_VIDEO,
+ CAPABILITY_TYPE_UT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MmTelCapability {}
+
+ /**
+ * This MmTelFeature supports Voice calling (IR.92)
+ */
+ public static final int CAPABILITY_TYPE_VOICE = 1 << 0;
+
+ /**
+ * This MmTelFeature supports Video (IR.94)
+ */
+ public static final int CAPABILITY_TYPE_VIDEO = 1 << 1;
+
+ /**
+ * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
+ */
+ public static final int CAPABILITY_TYPE_UT = 1 << 2;
+
+ @Override
+ public final void addCapabilities(@MmTelCapability int capabilities) {
+ super.addCapabilities(capabilities);
+ }
+
+ @Override
+ public final void removeCapabilities(@MmTelCapability int capability) {
+ super.removeCapabilities(capability);
+ }
+
+ @Override
+ public final boolean isCapable(@MmTelCapability int capabilities) {
+ return super.isCapable(capabilities);
+ }
+ }
+
+ /**
+ * Listener that the framework implements for communication from the MmTelFeature.
+ */
+ public static class Listener extends IImsMmTelListener.Stub {
+
+ @Override
+ public final void onIncomingCall(IImsCallSession c) {
+ onIncomingCall(new ImsCallSession(c));
+ }
+
+ /**
+ * Called when the IMS provider receives an incoming call.
+ * @param c The {@link ImsCallSession} associated with the new call.
+ */
+ public void onIncomingCall(ImsCallSession c) {
+ }
+ }
+
+ // Lock for feature synchronization
+ private final Object mLock = new Object();
+ private IImsMmTelListener mListener;
+
+ /**
+ * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
+ * notifies the framework.
+ */
+ private void setListener(IImsMmTelListener listener) {
+ synchronized (mLock) {
+ mListener = listener;
+ }
+ }
+
+ private void queryCapabilityConfigurationInternal(int capability, int radioTech,
+ IImsCapabilityCallback c) {
+ boolean enabled = queryCapabilityConfiguration(capability, radioTech);
+ try {
+ if (c != null) {
+ c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
+ }
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
+ }
+ }
+
+ /**
+ * The current capability status that this MmTelFeature has defined is available. This
+ * configuration will be used by the platform to figure out which capabilities are CURRENTLY
+ * available to be used.
+ *
+ * Should be a subset of the capabilities that are enabled by the framework in
+ * {@link #changeEnabledCapabilities}.
+ * @return A copy of the current MmTelFeature capability status.
+ */
+ @Override
+ public final MmTelCapabilities queryCapabilityStatus() {
+ return new MmTelCapabilities(super.queryCapabilityStatus());
+ }
+
+ /**
+ * Notify the framework that the status of the Capabilities has changed. Even though the
+ * MmTelFeature capability may be enabled by the framework, the status may be disabled due to
+ * the feature being unavailable from the network.
+ * @param c The current capability status of the MmTelFeature. If a capability is disabled, then
+ * the status of that capability is disabled. This can happen if the network does not currently
+ * support the capability that is enabled. A capability that is disabled by the framework (via
+ * {@link #changeEnabledCapabilities}) should also show the status as disabled.
+ */
+ protected final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) {
+ super.notifyCapabilitiesStatusChanged(c);
+ }
+
+ /**
+ * Notify the framework of an incoming call.
+ * @param c The {@link ImsCallSession} of the new incoming call.
+ *
+ * @throws RemoteException if the connection to the framework is not available. If this happens,
+ * the call should be no longer considered active and should be cleaned up.
+ * */
+ protected final void notifyIncomingCall(ImsCallSession c) throws RemoteException {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new IllegalStateException("Session is not available.");
+ }
+ mListener.onIncomingCall(c.getSession());
+ }
+ }
+
+ /**
+ * Provides the MmTelFeature with the ability to return the framework Capability Configuration
+ * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
+ * includes a capability A to enable or disable, this method should return the correct enabled
+ * status for capability A.
+ * @param capability The capability that we are querying the configuration for.
+ * @return true if the capability is enabled, false otherwise.
+ */
+ public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+ // Base implementation - Override to provide functionality
+ return false;
+ }
+
+ /**
+ * The MmTelFeature should override this method to handle the enabling/disabling of
+ * MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes
+ * the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities
+ * could not be set to their new values,
+ * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called
+ * individually for each capability whose processing resulted in an error.
+ *
+ * Enabling/Disabling a capability here indicates that the capability should be registered or
+ * deregistered (depending on the capability change) and become available or unavailable to
+ * the framework.
+ */
+ @Override
+ public void changeEnabledCapabilities(CapabilityChangeRequest request,
+ CapabilityCallbackProxy c) {
+ // Base implementation, no-op
+ }
+
+ /**
+ * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
+ *
+ * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#SERVICE_TYPE_NONE}
+ * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+ * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+ * @param callType a call type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE}
+ * {@link ImsCallProfile#CALL_TYPE_VT}
+ * {@link ImsCallProfile#CALL_TYPE_VT_TX}
+ * {@link ImsCallProfile#CALL_TYPE_VT_RX}
+ * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
+ * {@link ImsCallProfile#CALL_TYPE_VS}
+ * {@link ImsCallProfile#CALL_TYPE_VS_TX}
+ * {@link ImsCallProfile#CALL_TYPE_VS_RX}
+ * @return a {@link ImsCallProfile} object
+ */
+ public ImsCallProfile createCallProfile(int callSessionType, int callType) {
+ // Base Implementation - Should be overridden
+ return null;
+ }
+
+ /**
+ * Creates an {@link ImsCallSession} with the specified call profile.
+ * Use other methods, if applicable, instead of interacting with
+ * {@link ImsCallSession} directly.
+ *
+ * @param profile a call profile to make the call
+ * @param listener An implementation of IImsCallSessionListener.
+ */
+ public ImsCallSession createCallSession(ImsCallProfile profile,
+ ImsCallSessionListener listener) {
+ // Base Implementation - Should be overridden
+ return null;
+ }
+
+ /**
+ * @return The Ut interface for the supplementary service configuration.
+ */
+ public ImsUtImplBase getUt() {
+ // Base Implementation - Should be overridden
+ return null;
+ }
+
+ /**
+ * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
+ */
+ public ImsEcbmImplBase getEcbm() {
+ // Base Implementation - Should be overridden
+ return null;
+ }
+
+ /**
+ * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
+ */
+ public ImsMultiEndpointImplBase getMultiEndpoint() {
+ // Base Implementation - Should be overridden
+ return null;
+ }
+
+ /**
+ * Sets the current UI TTY mode for the MmTelFeature.
+ * @param mode An integer containing the new UI TTY Mode, can consist of
+ * {@link TelecomManager#TTY_MODE_OFF},
+ * {@link TelecomManager#TTY_MODE_FULL},
+ * {@link TelecomManager#TTY_MODE_HCO},
+ * {@link TelecomManager#TTY_MODE_VCO}
+ * @param onCompleteMessage A {@link Message} to be used when the mode has been set.
+ */
+ void setUiTtyMode(int mode, Message onCompleteMessage) {
+ // Base Implementation - Should be overridden
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ public void onFeatureRemoved() {
+ // Base Implementation - Should be overridden
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ public void onFeatureReady() {
+ // Base Implementation - Should be overridden
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public final IImsMmTelFeature getBinder() {
+ return mImsMMTelBinder;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java b/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java
new file mode 100644
index 0000000..8d1bd9d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java
@@ -0,0 +1,59 @@
+/*
+ * 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.telephony.ims.internal.feature;
+
+import android.telephony.ims.internal.aidl.IImsRcsFeature;
+
+/**
+ * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
+ * this class and provide implementations of the RcsFeature methods that they support.
+ * @hide
+ */
+
+public class RcsFeature extends ImsFeature {
+
+ private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() {
+ // Empty Default Implementation.
+ };
+
+
+ public RcsFeature() {
+ super();
+ }
+
+ @Override
+ public void changeEnabledCapabilities(CapabilityChangeRequest request,
+ CapabilityCallbackProxy c) {
+ // Do nothing for base implementation.
+ }
+
+ @Override
+ public void onFeatureRemoved() {
+
+ }
+
+ /**{@inheritDoc}*/
+ @Override
+ public void onFeatureReady() {
+
+ }
+
+ @Override
+ public final IImsRcsFeature getBinder() {
+ return mImsRcsBinder;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java
new file mode 100644
index 0000000..33aec5d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java
@@ -0,0 +1,173 @@
+/*
+ * 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.telephony.ims.internal.stub;
+
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.telephony.ims.internal.aidl.IImsConfig;
+import android.telephony.ims.internal.aidl.IImsConfigCallback;
+
+import com.android.ims.ImsConfig;
+
+/**
+ * Controls the modification of IMS specific configurations. For more information on the supported
+ * IMS configuration constants, see {@link ImsConfig}.
+ *
+ * @hide
+ */
+
+public class ImsConfigImplBase {
+
+ //TODO: Implement the Binder logic to call base APIs. Need to finish other ImsService Config
+ // work first.
+ private final IImsConfig mBinder = new IImsConfig.Stub() {
+
+ @Override
+ public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
+ ImsConfigImplBase.this.addImsConfigCallback(c);
+ }
+
+ @Override
+ public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
+ ImsConfigImplBase.this.removeImsConfigCallback(c);
+ }
+
+ @Override
+ public int getConfigInt(int item) throws RemoteException {
+ return Integer.MIN_VALUE;
+ }
+
+ @Override
+ public String getConfigString(int item) throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public int setConfigInt(int item, int value) throws RemoteException {
+ return Integer.MIN_VALUE;
+ }
+
+ @Override
+ public int setConfigString(int item, String value) throws RemoteException {
+ return Integer.MIN_VALUE;
+ }
+ };
+
+ public class Callback extends IImsConfigCallback.Stub {
+
+ @Override
+ public final void onIntConfigChanged(int item, int value) throws RemoteException {
+ onConfigChanged(item, value);
+ }
+
+ @Override
+ public final void onStringConfigChanged(int item, String value) throws RemoteException {
+ onConfigChanged(item, value);
+ }
+
+ /**
+ * Called when the IMS configuration has changed.
+ * @param item the IMS configuration key constant, as defined in ImsConfig.
+ * @param value the new integer value of the IMS configuration constant.
+ */
+ public void onConfigChanged(int item, int value) {
+ // Base Implementation
+ }
+
+ /**
+ * Called when the IMS configuration has changed.
+ * @param item the IMS configuration key constant, as defined in ImsConfig.
+ * @param value the new String value of the IMS configuration constant.
+ */
+ public void onConfigChanged(int item, String value) {
+ // Base Implementation
+ }
+ }
+
+ private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>();
+
+ /**
+ * Adds a {@link Callback} to the list of callbacks notified when a value in the configuration
+ * changes.
+ * @param c callback to add.
+ */
+ private void addImsConfigCallback(IImsConfigCallback c) {
+ mCallbacks.register(c);
+ }
+ /**
+ * Removes a {@link Callback} to the list of callbacks notified when a value in the
+ * configuration changes.
+ *
+ * @param c callback to remove.
+ */
+ private void removeImsConfigCallback(IImsConfigCallback c) {
+ mCallbacks.unregister(c);
+ }
+
+ public final IImsConfig getBinder() {
+ return mBinder;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by the operator device
+ * management entity. It sets the config item value in the provisioned storage
+ * from which the master value is derived.
+ *
+ * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in Integer format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ public int setConfig(int item, int value) {
+ // Base Implementation - To be overridden.
+ return ImsConfig.OperationStatusConstants.FAILED;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by the operator device
+ * management entity. It sets the config item value in the provisioned storage
+ * from which the master value is derived.
+ *
+ * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in String format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ public int setConfig(int item, String value) {
+ return ImsConfig.OperationStatusConstants.FAILED;
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters from the provisioned
+ * value storage.
+ *
+ * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in Integer format.
+ */
+ public int getConfigInt(int item) {
+ return ImsConfig.OperationStatusConstants.FAILED;
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters from the provisioned
+ * value storage.
+ *
+ * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in String format.
+ */
+ public String getConfigString(int item) {
+ return null;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl
new file mode 100644
index 0000000..e890cf8
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.telephony.ims.internal.stub;
+
+parcelable ImsFeatureConfiguration;
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java
new file mode 100644
index 0000000..244c957
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java
@@ -0,0 +1,147 @@
+/*
+ * 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.telephony.ims.internal.stub;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.internal.feature.ImsFeature;
+import android.util.ArraySet;
+
+import java.util.Arrays;
+import java.util.Set;
+
+/**
+ * Container class for IMS Feature configuration. This class contains the features that the
+ * ImsService supports, which are defined in {@link ImsFeature.FeatureType}.
+ * @hide
+ */
+public class ImsFeatureConfiguration implements Parcelable {
+ /**
+ * Features that this ImsService supports.
+ */
+ private final Set<Integer> mFeatures;
+
+ /**
+ * Creates an ImsFeatureConfiguration with the features
+ */
+ public static class Builder {
+ ImsFeatureConfiguration mConfig;
+ public Builder() {
+ mConfig = new ImsFeatureConfiguration();
+ }
+
+ /**
+ * @param feature A feature defined in {@link ImsFeature.FeatureType} that this service
+ * supports.
+ * @return a {@link Builder} to continue constructing the ImsFeatureConfiguration.
+ */
+ public Builder addFeature(@ImsFeature.FeatureType int feature) {
+ mConfig.addFeature(feature);
+ return this;
+ }
+
+ public ImsFeatureConfiguration build() {
+ return mConfig;
+ }
+ }
+
+ /**
+ * Creates with all registration features empty.
+ *
+ * Consider using the provided {@link Builder} to create this configuration instead.
+ */
+ public ImsFeatureConfiguration() {
+ mFeatures = new ArraySet<>();
+ }
+
+ /**
+ * Configuration of the ImsService, which describes which features the ImsService supports
+ * (for registration).
+ * @param features an array of feature integers defined in {@link ImsFeature} that describe
+ * which features this ImsService supports.
+ */
+ public ImsFeatureConfiguration(int[] features) {
+ mFeatures = new ArraySet<>();
+
+ if (features != null) {
+ for (int i : features) {
+ mFeatures.add(i);
+ }
+ }
+ }
+
+ /**
+ * @return an int[] containing the features that this ImsService supports.
+ */
+ public int[] getServiceFeatures() {
+ return mFeatures.stream().mapToInt(i->i).toArray();
+ }
+
+ void addFeature(int feature) {
+ mFeatures.add(feature);
+ }
+
+ protected ImsFeatureConfiguration(Parcel in) {
+ int[] features = in.createIntArray();
+ if (features != null) {
+ mFeatures = new ArraySet<>(features.length);
+ for(Integer i : features) {
+ mFeatures.add(i);
+ }
+ } else {
+ mFeatures = new ArraySet<>();
+ }
+ }
+
+ public static final Creator<ImsFeatureConfiguration> CREATOR
+ = new Creator<ImsFeatureConfiguration>() {
+ @Override
+ public ImsFeatureConfiguration createFromParcel(Parcel in) {
+ return new ImsFeatureConfiguration(in);
+ }
+
+ @Override
+ public ImsFeatureConfiguration[] newArray(int size) {
+ return new ImsFeatureConfiguration[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeIntArray(mFeatures.stream().mapToInt(i->i).toArray());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ImsFeatureConfiguration)) return false;
+
+ ImsFeatureConfiguration that = (ImsFeatureConfiguration) o;
+
+ return mFeatures.equals(that.mFeatures);
+ }
+
+ @Override
+ public int hashCode() {
+ return mFeatures.hashCode();
+ }
+}
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java
new file mode 100644
index 0000000..558b009
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java
@@ -0,0 +1,276 @@
+/*
+ * 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.telephony.ims.internal.stub;
+
+import android.annotation.IntDef;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.telephony.ims.internal.aidl.IImsRegistration;
+import android.telephony.ims.internal.aidl.IImsRegistrationCallback;
+import android.util.Log;
+
+import com.android.ims.ImsReasonInfo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Controls IMS registration for this ImsService and notifies the framework when the IMS
+ * registration for this ImsService has changed status.
+ * @hide
+ */
+
+public class ImsRegistrationImplBase {
+
+ private static final String LOG_TAG = "ImsRegistrationImplBase";
+
+ // Defines the underlying radio technology type that we have registered for IMS over.
+ @IntDef(flag = true,
+ value = {
+ REGISTRATION_TECH_NONE,
+ REGISTRATION_TECH_LTE,
+ REGISTRATION_TECH_IWLAN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsRegistrationTech {}
+ /**
+ * No registration technology specified, used when we are not registered.
+ */
+ public static final int REGISTRATION_TECH_NONE = -1;
+ /**
+ * IMS is registered to IMS via LTE.
+ */
+ public static final int REGISTRATION_TECH_LTE = 0;
+ /**
+ * IMS is registered to IMS via IWLAN.
+ */
+ public static final int REGISTRATION_TECH_IWLAN = 1;
+
+ // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current
+ // state.
+ private static final int REGISTRATION_STATE_NOT_REGISTERED = 0;
+ private static final int REGISTRATION_STATE_REGISTERING = 1;
+ private static final int REGISTRATION_STATE_REGISTERED = 2;
+
+
+ /**
+ * Callback class for receiving Registration callback events.
+ */
+ public static class Callback extends IImsRegistrationCallback.Stub {
+
+ /**
+ * Notifies the framework when the IMS Provider is connected to the IMS network.
+ *
+ * @param imsRadioTech the radio access technology. Valid values are defined in
+ * {@link ImsRegistrationTech}.
+ */
+ @Override
+ public void onRegistered(@ImsRegistrationTech int imsRadioTech) {
+ }
+
+ /**
+ * Notifies the framework when the IMS Provider is trying to connect the IMS network.
+ *
+ * @param imsRadioTech the radio access technology. Valid values are defined in
+ * {@link ImsRegistrationTech}.
+ */
+ @Override
+ public void onRegistering(@ImsRegistrationTech int imsRadioTech) {
+ }
+
+ /**
+ * Notifies the framework when the IMS Provider is disconnected from the IMS network.
+ *
+ * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+ */
+ @Override
+ public void onDeregistered(ImsReasonInfo info) {
+ }
+
+ /**
+ * A failure has occurred when trying to handover registration to another technology type,
+ * defined in {@link ImsRegistrationTech}
+ *
+ * @param imsRadioTech The {@link ImsRegistrationTech} type that has failed
+ * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
+ */
+ @Override
+ public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
+ ImsReasonInfo info) {
+ }
+ }
+
+ private final IImsRegistration mBinder = new IImsRegistration.Stub() {
+
+ @Override
+ public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException {
+ return getConnectionType();
+ }
+
+ @Override
+ public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
+ ImsRegistrationImplBase.this.addRegistrationCallback(c);
+ }
+
+ @Override
+ public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
+ ImsRegistrationImplBase.this.removeRegistrationCallback(c);
+ }
+ };
+
+ private final RemoteCallbackList<IImsRegistrationCallback> mCallbacks
+ = new RemoteCallbackList<>();
+ private final Object mLock = new Object();
+ // Locked on mLock
+ private @ImsRegistrationTech
+ int mConnectionType = REGISTRATION_TECH_NONE;
+ // Locked on mLock
+ private int mRegistrationState = REGISTRATION_STATE_NOT_REGISTERED;
+ // Locked on mLock
+ private ImsReasonInfo mLastDisconnectCause;
+
+ public final IImsRegistration getBinder() {
+ return mBinder;
+ }
+
+ private void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
+ mCallbacks.register(c);
+ updateNewCallbackWithState(c);
+ }
+
+ private void removeRegistrationCallback(IImsRegistrationCallback c) {
+ mCallbacks.unregister(c);
+ }
+
+ /**
+ * Notify the framework that the device is connected to the IMS network.
+ *
+ * @param imsRadioTech the radio access technology. Valid values are defined in
+ * {@link ImsRegistrationTech}.
+ */
+ public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
+ updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERED);
+ mCallbacks.broadcast((c) -> {
+ try {
+ c.onRegistered(imsRadioTech);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " " + "onRegistrationConnected() - Skipping " +
+ "callback.");
+ }
+ });
+ }
+
+ /**
+ * Notify the framework that the device is trying to connect the IMS network.
+ *
+ * @param imsRadioTech the radio access technology. Valid values are defined in
+ * {@link ImsRegistrationTech}.
+ */
+ public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
+ updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERING);
+ mCallbacks.broadcast((c) -> {
+ try {
+ c.onRegistering(imsRadioTech);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " " + "onRegistrationProcessing() - Skipping " +
+ "callback.");
+ }
+ });
+ }
+
+ /**
+ * Notify the framework that the device is disconnected from the IMS network.
+ *
+ * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+ */
+ public final void onDeregistered(ImsReasonInfo info) {
+ updateToDisconnectedState(info);
+ mCallbacks.broadcast((c) -> {
+ try {
+ c.onDeregistered(info);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " +
+ "callback.");
+ }
+ });
+ }
+
+ public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
+ ImsReasonInfo info) {
+ mCallbacks.broadcast((c) -> {
+ try {
+ c.onTechnologyChangeFailed(imsRadioTech, info);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - Skipping " +
+ "callback.");
+ }
+ });
+ }
+
+ private void updateToState(@ImsRegistrationTech int connType, int newState) {
+ synchronized (mLock) {
+ mConnectionType = connType;
+ mRegistrationState = newState;
+ mLastDisconnectCause = null;
+ }
+ }
+
+ private void updateToDisconnectedState(ImsReasonInfo info) {
+ synchronized (mLock) {
+ updateToState(REGISTRATION_TECH_NONE, REGISTRATION_STATE_NOT_REGISTERED);
+ if (info != null) {
+ mLastDisconnectCause = info;
+ } else {
+ Log.w(LOG_TAG, "updateToDisconnectedState: no ImsReasonInfo provided.");
+ mLastDisconnectCause = new ImsReasonInfo();
+ }
+ }
+ }
+
+ private @ImsRegistrationTech int getConnectionType() {
+ synchronized (mLock) {
+ return mConnectionType;
+ }
+ }
+
+ /**
+ * @param c the newly registered callback that will be updated with the current registration
+ * state.
+ */
+ private void updateNewCallbackWithState(IImsRegistrationCallback c) throws RemoteException {
+ int state;
+ ImsReasonInfo disconnectInfo;
+ synchronized (mLock) {
+ state = mRegistrationState;
+ disconnectInfo = mLastDisconnectCause;
+ }
+ switch (state) {
+ case REGISTRATION_STATE_NOT_REGISTERED: {
+ c.onDeregistered(disconnectInfo);
+ break;
+ }
+ case REGISTRATION_STATE_REGISTERING: {
+ c.onRegistering(getConnectionType());
+ break;
+ }
+ case REGISTRATION_STATE_REGISTERED: {
+ c.onRegistered(getConnectionType());
+ break;
+ }
+ }
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index a4eb424..fe37531 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -187,57 +187,6 @@
in PendingIntent deliveryIntent, in boolean persistMessage);
/**
- * Send an SMS with options using Subscription Id.
- *
- * @param subId the subId on which the SMS has to be sent.
- * @param destAddr the address to send the message to
- * @param scAddr the SMSC to send the message through, or NULL for the
- * default SMSC
- * @param text the body of the message to send
- * @param sentIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is sucessfully sent, or failed.
- * The result code will be <code>Activity.RESULT_OK<code> for success,
- * or one of these errors:<br>
- * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
- * <code>RESULT_ERROR_RADIO_OFF</code><br>
- * <code>RESULT_ERROR_NULL_PDU</code><br>
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
- * The per-application based SMS control checks sentIntent. If sentIntent
- * is NULL the caller will be checked against all unknown applications,
- * which cause smaller number of SMS to be sent in checking period.
- * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is delivered to the recipient. The
- * raw pdu of the status report is in the extended data ("pdu").
- * @param persistMessageForNonDefaultSmsApp whether the sent message should
- * be automatically persisted in the SMS db. It only affects messages sent
- * by a non-default SMS app. Currently only the carrier app can set this
- * parameter to false to skip auto message persistence.
- * @param priority Priority level of the message
- * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
- * ---------------------------------
- * PRIORITY | Level of Priority
- * ---------------------------------
- * '00' | Normal
- * '01' | Interactive
- * '10' | Urgent
- * '11' | Emergency
- * ----------------------------------
- * Any Other values included Negative considered as Invalid Priority Indicator of the message.
- * @param expectMore is a boolean to indicate the sending message is multi segmented or not.
- * @param validityPeriod Validity Period of the message in mins.
- * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
- * Validity Period(Minimum) -> 5 mins
- * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
- * Any Other values included Negative considered as Invalid Validity Period of the message.
- */
- void sendTextForSubscriberWithOptions(in int subId, String callingPkg, in String destAddr,
- in String scAddr, in String text, in PendingIntent sentIntent,
- in PendingIntent deliveryIntent, in boolean persistMessageForNonDefaultSmsApp,
- in int priority, in boolean expectMore, in int validityPeriod);
-
- /**
* Inject an SMS PDU into the android platform.
*
* @param subId the subId on which the SMS has to be injected.
@@ -285,56 +234,6 @@
in List<PendingIntent> deliveryIntents, in boolean persistMessageForNonDefaultSmsApp);
/**
- * Send a multi-part text based SMS with options using Subscription Id.
- *
- * @param subId the subId on which the SMS has to be sent.
- * @param destinationAddress the address to send the message to
- * @param scAddress is the service center address or null to use
- * the current default SMSC
- * @param parts an <code>ArrayList</code> of strings that, in order,
- * comprise the original message
- * @param sentIntents if not null, an <code>ArrayList</code> of
- * <code>PendingIntent</code>s (one for each message part) that is
- * broadcast when the corresponding message part has been sent.
- * The result code will be <code>Activity.RESULT_OK<code> for success,
- * or one of these errors:
- * <code>RESULT_ERROR_GENERIC_FAILURE</code>
- * <code>RESULT_ERROR_RADIO_OFF</code>
- * <code>RESULT_ERROR_NULL_PDU</code>.
- * @param deliveryIntents if not null, an <code>ArrayList</code> of
- * <code>PendingIntent</code>s (one for each message part) that is
- * broadcast when the corresponding message part has been delivered
- * to the recipient. The raw pdu of the status report is in the
- * extended data ("pdu").
- * @param persistMessageForNonDefaultSmsApp whether the sent message should
- * be automatically persisted in the SMS db. It only affects messages sent
- * by a non-default SMS app. Currently only the carrier app can set this
- * parameter to false to skip auto message persistence.
- * @param priority Priority level of the message
- * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
- * ---------------------------------
- * PRIORITY | Level of Priority
- * ---------------------------------
- * '00' | Normal
- * '01' | Interactive
- * '10' | Urgent
- * '11' | Emergency
- * ----------------------------------
- * Any Other values included Negative considered as Invalid Priority Indicator of the message.
- * @param expectMore is a boolean to indicate the sending message is multi segmented or not.
- * @param validityPeriod Validity Period of the message in mins.
- * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
- * Validity Period(Minimum) -> 5 mins
- * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
- * Any Other values included Negative considered as Invalid Validity Period of the message.
- */
- void sendMultipartTextForSubscriberWithOptions(in int subId, String callingPkg,
- in String destinationAddress, in String scAddress, in List<String> parts,
- in List<PendingIntent> sentIntents, in List<PendingIntent> deliveryIntents,
- in boolean persistMessageForNonDefaultSmsApp, in int priority, in boolean expectMore,
- in int validityPeriod);
-
- /**
* Enable reception of cell broadcast (SMS-CB) messages with the given
* message identifier and RAN type. The RAN type specify this message ID
* belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different clients
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index fd6091a..8ea53a5 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -870,14 +870,33 @@
*
* @param enable true to turn on, else false
*/
- void setDataEnabled(int subId, boolean enable);
+ void setUserDataEnabled(int subId, boolean enable);
+
+ /**
+ * Get the user enabled state of Mobile Data.
+ *
+ * TODO: remove and use isUserDataEnabled.
+ * This can't be removed now because some vendor codes
+ * calls through ITelephony directly while they should
+ * use TelephonyManager.
+ *
+ * @return true on enabled
+ */
+ boolean getDataEnabled(int subId);
/**
* Get the user enabled state of Mobile Data.
*
* @return true on enabled
*/
- boolean getDataEnabled(int subId);
+ boolean isUserDataEnabled(int subId);
+
+ /**
+ * Get the overall enabled state of Mobile Data.
+ *
+ * @return true on enabled
+ */
+ boolean isDataEnabled(int subId);
/**
* Get P-CSCF address from PCO after data connection is established or modified.
@@ -1298,6 +1317,34 @@
*/
List<CarrierIdentifier> getAllowedCarriers(int slotIndex);
+ /**
+ * Returns carrier id of the given subscription.
+ * <p>To recognize carrier as a first class identity, assign each carrier with a canonical
+ * integer a.k.a carrier id.
+ *
+ * @param subId The subscription id
+ * @return Carrier id of given subscription id. return {@link #UNKNOWN_CARRIER_ID} if
+ * subscription is unavailable or carrier cannot be identified.
+ * @throws IllegalStateException if telephony service is unavailable.
+ * @hide
+ */
+ int getSubscriptionCarrierId(int subId);
+
+ /**
+ * Returns carrier name of the given subscription.
+ * <p>Carrier name is a user-facing name of carrier id {@link #getSubscriptionCarrierId(int)},
+ * usually the brand name of the subsidiary (e.g. T-Mobile). Each carrier could configure
+ * multiple {@link #getSimOperatorName() SPN} but should have a single carrier name.
+ * Carrier name is not canonical identity, use {@link #getSubscriptionCarrierId(int)} instead.
+ * <p>Returned carrier name is unlocalized.
+ *
+ * @return Carrier name of given subscription id. return {@code null} if subscription is
+ * unavailable or carrier cannot be identified.
+ * @throws IllegalStateException if telephony service is unavailable.
+ * @hide
+ */
+ String getSubscriptionCarrierName(int subId);
+
/**
* Action set from carrier signalling broadcast receivers to enable/disable metered apns
* Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 14c5f4b..7a53ef6 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -99,15 +99,6 @@
private static final int RETURN_NO_ACK = 0;
private static final int RETURN_ACK = 1;
- /**
- * Supported priority modes for CDMA SMS messages
- * (See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1)
- */
- private static final int PRIORITY_NORMAL = 0x0;
- private static final int PRIORITY_INTERACTIVE = 0x1;
- private static final int PRIORITY_URGENT = 0x2;
- private static final int PRIORITY_EMERGENCY = 0x3;
-
private SmsEnvelope mEnvelope;
private BearerData mBearerData;
@@ -220,26 +211,6 @@
*/
public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message,
boolean statusReportRequested, SmsHeader smsHeader) {
- return getSubmitPdu(scAddr, destAddr, message, statusReportRequested, smsHeader, -1);
- }
-
- /**
- * Get an SMS-SUBMIT PDU for a destination address and a message
- *
- * @param scAddr Service Centre address. Null means use default.
- * @param destAddr Address of the recipient.
- * @param message String representation of the message payload.
- * @param statusReportRequested Indicates whether a report is requested for this message.
- * @param smsHeader Array containing the data for the User Data Header, preceded
- * by the Element Identifiers.
- * @param priority Priority level of the message
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
- * @hide
- */
- public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message,
- boolean statusReportRequested, SmsHeader smsHeader, int priority) {
/**
* TODO(cleanup): Do we really want silent failure like this?
@@ -253,7 +224,7 @@
UserData uData = new UserData();
uData.payloadStr = message;
uData.userDataHeader = smsHeader;
- return privateGetSubmitPdu(destAddr, statusReportRequested, uData, priority);
+ return privateGetSubmitPdu(destAddr, statusReportRequested, uData);
}
/**
@@ -311,22 +282,6 @@
}
/**
- * Get an SMS-SUBMIT PDU for a data message to a destination address & port
- *
- * @param destAddr the address of the destination for the message
- * @param userData the data for the message
- * @param statusReportRequested Indicates whether a report is requested for this message.
- * @param priority Priority level of the message
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
- */
- public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
- boolean statusReportRequested, int priority) {
- return privateGetSubmitPdu(destAddr, statusReportRequested, userData, priority);
- }
-
- /**
* Note: This function is a GSM specific functionality which is not supported in CDMA mode.
*/
@Override
@@ -809,15 +764,6 @@
*/
private static SubmitPdu privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested,
UserData userData) {
- return privateGetSubmitPdu(destAddrStr, statusReportRequested, userData, -1);
- }
-
- /**
- * Creates BearerData and Envelope from parameters for a Submit SMS.
- * @return byte stream for SubmitPdu.
- */
- private static SubmitPdu privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested,
- UserData userData, int priority) {
/**
* TODO(cleanup): give this function a more meaningful name.
@@ -846,10 +792,6 @@
bearerData.userAckReq = false;
bearerData.readAckReq = false;
bearerData.reportReq = false;
- if (priority >= PRIORITY_NORMAL && priority <= PRIORITY_EMERGENCY) {
- bearerData.priorityIndicatorSet = true;
- bearerData.priority = priority;
- }
bearerData.userData = userData;
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
index d267ad2..0dbc186 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -441,10 +441,11 @@
@Override
public String toString() {
- return "SmsCbHeader{GS=" + mGeographicalScope + ", serialNumber=0x" +
- Integer.toHexString(mSerialNumber) +
- ", messageIdentifier=0x" + Integer.toHexString(mMessageIdentifier) +
- ", DCS=0x" + Integer.toHexString(mDataCodingScheme) +
- ", page " + mPageIndex + " of " + mNrOfPages + '}';
+ return "SmsCbHeader{GS=" + mGeographicalScope + ", serialNumber=0x"
+ + Integer.toHexString(mSerialNumber)
+ + ", messageIdentifier=0x" + Integer.toHexString(mMessageIdentifier)
+ + ", format=" + mFormat
+ + ", DCS=0x" + Integer.toHexString(mDataCodingScheme)
+ + ", page " + mPageIndex + " of " + mNrOfPages + '}';
}
}
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 4f5bfa9..1ca19e0 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -89,18 +89,6 @@
private int mVoiceMailCount = 0;
- private static final int VALIDITY_PERIOD_FORMAT_NONE = 0x00;
- private static final int VALIDITY_PERIOD_FORMAT_ENHANCED = 0x01;
- private static final int VALIDITY_PERIOD_FORMAT_RELATIVE = 0x02;
- private static final int VALIDITY_PERIOD_FORMAT_ABSOLUTE = 0x03;
-
- //Validity Period min - 5 mins
- private static final int VALIDITY_PERIOD_MIN = 5;
- //Validity Period max - 63 weeks
- private static final int VALIDITY_PERIOD_MAX = 635040;
-
- private static final int INVALID_VALIDITY_PERIOD = -1;
-
public static class SubmitPdu extends SubmitPduBase {
}
@@ -214,45 +202,6 @@
}
/**
- * Get Encoded Relative Validty Period Value from Validity period in mins.
- *
- * @param validityPeriod Validity period in mins.
- *
- * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
- * ||relValidityPeriod (TP-VP) || || validityPeriod ||
- *
- * 0 to 143 ---> (TP-VP + 1) x 5 minutes
- *
- * 144 to 167 ---> 12 hours + ((TP-VP -143) x 30 minutes)
- *
- * 168 to 196 ---> (TP-VP - 166) x 1 day
- *
- * 197 to 255 ---> (TP-VP - 192) x 1 week
- *
- * @return relValidityPeriod Encoded Relative Validity Period Value.
- * @hide
- */
- public static int getRelativeValidityPeriod(int validityPeriod) {
- int relValidityPeriod = INVALID_VALIDITY_PERIOD;
-
- if (validityPeriod < VALIDITY_PERIOD_MIN || validityPeriod > VALIDITY_PERIOD_MAX) {
- Rlog.e(LOG_TAG,"Invalid Validity Period" + validityPeriod);
- return relValidityPeriod;
- }
-
- if (validityPeriod <= 720) {
- relValidityPeriod = (validityPeriod / 5) - 1;
- } else if (validityPeriod <= 1440) {
- relValidityPeriod = ((validityPeriod - 720) / 30) + 143;
- } else if (validityPeriod <= 43200) {
- relValidityPeriod = (validityPeriod / 1440) + 166;
- } else if (validityPeriod <= 635040) {
- relValidityPeriod = (validityPeriod / 10080) + 192;
- }
- return relValidityPeriod;
- }
-
- /**
* Get an SMS-SUBMIT PDU for a destination address and a message
*
* @param scAddress Service Centre address. Null means use default.
@@ -287,29 +236,6 @@
String destinationAddress, String message,
boolean statusReportRequested, byte[] header, int encoding,
int languageTable, int languageShiftTable) {
- return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested,
- header, encoding, languageTable, languageShiftTable, -1);
- }
-
- /**
- * Get an SMS-SUBMIT PDU for a destination address and a message using the
- * specified encoding.
- *
- * @param scAddress Service Centre address. Null means use default.
- * @param encoding Encoding defined by constants in
- * com.android.internal.telephony.SmsConstants.ENCODING_*
- * @param languageTable
- * @param languageShiftTable
- * @param validityPeriod Validity Period of the message in Minutes.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
- * @hide
- */
- public static SubmitPdu getSubmitPdu(String scAddress,
- String destinationAddress, String message,
- boolean statusReportRequested, byte[] header, int encoding,
- int languageTable, int languageShiftTable, int validityPeriod) {
// Perform null parameter checks.
if (message == null || destinationAddress == null) {
@@ -346,19 +272,8 @@
}
SubmitPdu ret = new SubmitPdu();
-
- int validityPeriodFormat = VALIDITY_PERIOD_FORMAT_NONE;
- int relativeValidityPeriod = INVALID_VALIDITY_PERIOD;
-
- // TP-Validity-Period-Format (TP-VPF) in 3GPP TS 23.040 V6.8.1 section 9.2.3.3
- //bit 4:3 = 10 - TP-VP field present - relative format
- if((relativeValidityPeriod = getRelativeValidityPeriod(validityPeriod)) >= 0) {
- validityPeriodFormat = VALIDITY_PERIOD_FORMAT_RELATIVE;
- }
-
- byte mtiByte = (byte)(0x01 | (validityPeriodFormat << 0x03) |
- (header != null ? 0x40 : 0x00));
-
+ // MTI = SMS-SUBMIT, UDHI = header != null
+ byte mtiByte = (byte)(0x01 | (header != null ? 0x40 : 0x00));
ByteArrayOutputStream bo = getSubmitPduHead(
scAddress, destinationAddress, mtiByte,
statusReportRequested, ret);
@@ -423,11 +338,7 @@
bo.write(0x08);
}
- if (validityPeriodFormat == VALIDITY_PERIOD_FORMAT_RELATIVE) {
- // ( TP-Validity-Period - relative format)
- bo.write(relativeValidityPeriod);
- }
-
+ // (no TP-Validity-Period)
bo.write(userData, 0, userData.length);
ret.encodedMessage = bo.toByteArray();
return ret;
@@ -477,24 +388,6 @@
}
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message
- *
- * @param scAddress Service Centre address. Null means use default.
- * @param destinationAddress the address of the destination for the message
- * @param statusReportRequested staus report of the message Requested
- * @param validityPeriod Validity Period of the message in Minutes.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
- */
- public static SubmitPdu getSubmitPdu(String scAddress,
- String destinationAddress, String message,
- boolean statusReportRequested, int validityPeriod) {
- return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested,
- null, ENCODING_UNKNOWN, 0, 0, validityPeriod);
- }
-
- /**
* Get an SMS-SUBMIT PDU for a data message to a destination address & port
*
* @param scAddress Service Centre address. null == use default
diff --git a/test-base/Android.bp b/test-base/Android.bp
index a3fd345..30c9af1 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -25,6 +25,7 @@
srcs: ["src/**/*.java"],
no_framework_libs: true,
+ hostdex: true,
libs: [
"framework",
],
diff --git a/test-mock/Android.mk b/test-mock/Android.mk
index a761a07..7926a77 100644
--- a/test-mock/Android.mk
+++ b/test-mock/Android.mk
@@ -16,7 +16,12 @@
LOCAL_PATH:= $(call my-dir)
-android_test_mock_source_files := $(call all-java-files-under, src/android/test/mock)
+# Includes the main framework source to ensure that doclava has access to the
+# visibility information for the base classes of the mock classes. Without it
+# otherwise hidden methods could be visible.
+android_test_mock_source_files := \
+ $(call all-java-files-under, src/android/test/mock) \
+ $(call all-java-files-under, ../core/java) \
# For unbundled build we'll use the prebuilt jar from prebuilts/sdk.
ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
@@ -67,6 +72,8 @@
LOCAL_ADDITIONAL_DEPENDENCIES := $(android_test_mock_gen_stamp)
android_test_mock_gen_stamp :=
+LOCAL_SDK_VERSION := current
+
include $(BUILD_STATIC_JAVA_LIBRARY)
# Archive a copy of the classes.jar in SDK build.
@@ -104,4 +111,91 @@
@echo Copying removed.txt
$(hide) $(ACP) $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_MOCK_REMOVED_API_FILE)
+# Generate the stub source files for android.test.mock.stubs-system
+# =================================================================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(android_test_mock_source_files)
+
+LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src/android/test/mock
+
+ANDROID_TEST_MOCK_SYSTEM_OUTPUT_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs-system_intermediates/api.txt
+ANDROID_TEST_MOCK_SYSTEM_OUTPUT_REMOVED_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs-system_intermediates/removed.txt
+
+ANDROID_TEST_MOCK_SYSTEM_API_FILE := $(LOCAL_PATH)/api/android-test-mock-system-current.txt
+ANDROID_TEST_MOCK_SYSTEM_REMOVED_API_FILE := $(LOCAL_PATH)/api/android-test-mock-system-removed.txt
+
+LOCAL_DROIDDOC_OPTIONS:= \
+ -stubpackages android.test.mock \
+ -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs-system_intermediates/src \
+ -nodocs \
+ -showAnnotation android.annotation.SystemApi \
+ -api $(ANDROID_TEST_MOCK_SYSTEM_OUTPUT_API_FILE) \
+ -removedApi $(ANDROID_TEST_MOCK_SYSTEM_OUTPUT_REMOVED_API_FILE) \
+
+LOCAL_UNINSTALLABLE_MODULE := true
+LOCAL_MODULE := android-test-mock-system-api-stubs-gen
+
+include $(BUILD_DROIDDOC)
+
+# Remember the target that will trigger the code generation.
+android_test_mock_system_gen_stamp := $(full_target)
+
+# Add some additional dependencies
+$(ANDROID_TEST_MOCK_SYSTEM_OUTPUT_API_FILE): $(full_target)
+$(ANDROID_TEST_MOCK_SYSTEM_OUTPUT_REMOVED_API_FILE): $(full_target)
+
+# Build the android.test.mock.stubs-system library
+# ================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := android.test.mock.stubs-system
+
+LOCAL_SOURCE_FILES_ALL_GENERATED := true
+
+# Make sure to run droiddoc first to generate the stub source files.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(android_test_mock_system_gen_stamp)
+android_test_mock_system_gen_stamp :=
+
+LOCAL_SDK_VERSION := system_current
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Archive a copy of the classes.jar in SDK build.
+$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):android.test.mock.stubs_system.jar)
+
+# Check that the android.test.mock.stubs-system library has not changed
+# =====================================================================
+
+# Check that the API we're building hasn't changed from the not-yet-released
+# SDK version.
+$(eval $(call check-api, \
+ check-android-test-mock-system-api-current, \
+ $(ANDROID_TEST_MOCK_SYSTEM_API_FILE), \
+ $(ANDROID_TEST_MOCK_SYSTEM_OUTPUT_API_FILE), \
+ $(ANDROID_TEST_MOCK_SYSTEM_REMOVED_API_FILE), \
+ $(ANDROID_TEST_MOCK_SYSTEM_OUTPUT_REMOVED_API_FILE), \
+ -error 2 -error 3 -error 4 -error 5 -error 6 \
+ -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
+ -error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 \
+ -error 25 -error 26 -error 27, \
+ cat $(LOCAL_PATH)/api/apicheck_msg_android_test_mock-system.txt, \
+ check-android-test-mock-system-api, \
+ $(call doc-timestamp-for,android-test-mock-system-api-stubs-gen) \
+ ))
+
+.PHONY: check-android-test-mock-system-api
+checkapi: check-android-test-mock-system-api
+
+.PHONY: update-android-test-mock-system-api
+update-api: update-android-test-mock-system-api
+
+update-android-test-mock-system-api: $(ANDROID_TEST_MOCK_SYSTEM_OUTPUT_API_FILE) | $(ACP)
+ @echo Copying current.txt
+ $(hide) $(ACP) $(ANDROID_TEST_MOCK_SYSTEM_OUTPUT_API_FILE) $(ANDROID_TEST_MOCK_SYSTEM_API_FILE)
+ @echo Copying removed.txt
+ $(hide) $(ACP) $(ANDROID_TEST_MOCK_SYSTEM_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_MOCK_SYSTEM_REMOVED_API_FILE)
+
endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
+
diff --git a/test-mock/api/android-test-mock-current.txt b/test-mock/api/android-test-mock-current.txt
index 3aa350b..48a1f80 100644
--- a/test-mock/api/android-test-mock-current.txt
+++ b/test-mock/api/android-test-mock-current.txt
@@ -10,7 +10,6 @@
ctor public MockContentProvider(android.content.Context, java.lang.String, java.lang.String, android.content.pm.PathPermission[]);
method public android.content.ContentProviderResult[] applyBatch(java.util.ArrayList<android.content.ContentProviderOperation>);
method public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
- method public final android.content.IContentProvider getIContentProvider();
method public java.lang.String getType(android.net.Uri);
method public android.net.Uri insert(android.net.Uri, android.content.ContentValues);
method public boolean onCreate();
@@ -22,37 +21,26 @@
public class MockContentResolver extends android.content.ContentResolver {
ctor public MockContentResolver();
ctor public MockContentResolver(android.content.Context);
- method protected android.content.IContentProvider acquireProvider(android.content.Context, java.lang.String);
- method protected android.content.IContentProvider acquireUnstableProvider(android.content.Context, java.lang.String);
method public void addProvider(java.lang.String, android.content.ContentProvider);
- method public boolean releaseProvider(android.content.IContentProvider);
- method public boolean releaseUnstableProvider(android.content.IContentProvider);
- method public void unstableProviderDied(android.content.IContentProvider);
}
public class MockContext extends android.content.Context {
ctor public MockContext();
method public boolean bindService(android.content.Intent, android.content.ServiceConnection, int);
- method public boolean canLoadUnsafeResources();
method public int checkCallingOrSelfPermission(java.lang.String);
method public int checkCallingOrSelfUriPermission(android.net.Uri, int);
method public int checkCallingPermission(java.lang.String);
method public int checkCallingUriPermission(android.net.Uri, int);
method public int checkPermission(java.lang.String, int, int);
- method public int checkPermission(java.lang.String, int, int, android.os.IBinder);
method public int checkSelfPermission(java.lang.String);
method public int checkUriPermission(android.net.Uri, int, int, int);
- method public int checkUriPermission(android.net.Uri, int, int, int, android.os.IBinder);
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper();
- method public android.content.Context createApplicationContext(android.content.pm.ApplicationInfo, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
method public android.content.Context createContextForSplit(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
- method public android.content.Context createCredentialProtectedStorageContext();
method public android.content.Context createDeviceProtectedStorageContext();
method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public android.content.Context createPackageContextAsUser(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
method public boolean deleteDatabase(java.lang.String);
method public boolean deleteFile(java.lang.String);
@@ -68,7 +56,6 @@
method public android.content.Context getApplicationContext();
method public android.content.pm.ApplicationInfo getApplicationInfo();
method public android.content.res.AssetManager getAssets();
- method public java.lang.String getBasePackageName();
method public java.io.File getCacheDir();
method public java.lang.ClassLoader getClassLoader();
method public java.io.File getCodeCacheDir();
@@ -76,8 +63,6 @@
method public java.io.File getDataDir();
method public java.io.File getDatabasePath(java.lang.String);
method public java.io.File getDir(java.lang.String, int);
- method public android.view.Display getDisplay();
- method public android.view.DisplayAdjustments getDisplayAdjustments(int);
method public java.io.File getExternalCacheDir();
method public java.io.File[] getExternalCacheDirs();
method public java.io.File getExternalFilesDir(java.lang.String);
@@ -89,25 +74,19 @@
method public java.io.File getNoBackupFilesDir();
method public java.io.File getObbDir();
method public java.io.File[] getObbDirs();
- method public java.lang.String getOpPackageName();
method public java.lang.String getPackageCodePath();
method public android.content.pm.PackageManager getPackageManager();
method public java.lang.String getPackageName();
method public java.lang.String getPackageResourcePath();
- method public java.io.File getPreloadsFileCache();
method public android.content.res.Resources getResources();
method public android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
- method public android.content.SharedPreferences getSharedPreferences(java.io.File, int);
- method public java.io.File getSharedPreferencesPath(java.lang.String);
method public java.lang.Object getSystemService(java.lang.String);
method public java.lang.String getSystemServiceName(java.lang.Class<?>);
method public android.content.res.Resources.Theme getTheme();
- method public int getUserId();
method public android.graphics.drawable.Drawable getWallpaper();
method public int getWallpaperDesiredMinimumHeight();
method public int getWallpaperDesiredMinimumWidth();
method public void grantUriPermission(java.lang.String, android.net.Uri, int);
- method public boolean isCredentialProtectedStorage();
method public boolean isDeviceProtectedStorage();
method public boolean moveDatabaseFrom(android.content.Context, java.lang.String);
method public boolean moveSharedPreferencesFrom(android.content.Context, java.lang.String);
@@ -120,31 +99,19 @@
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, int);
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler);
method public android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, java.lang.String, android.os.Handler, int);
- method public android.content.Intent registerReceiverAsUser(android.content.BroadcastReceiver, android.os.UserHandle, android.content.IntentFilter, java.lang.String, android.os.Handler);
- method public void reloadSharedPreferences();
method public void removeStickyBroadcast(android.content.Intent);
method public void removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void revokeUriPermission(android.net.Uri, int);
method public void revokeUriPermission(java.lang.String, android.net.Uri, int);
method public void sendBroadcast(android.content.Intent);
method public void sendBroadcast(android.content.Intent, java.lang.String);
- method public void sendBroadcast(android.content.Intent, java.lang.String, android.os.Bundle);
- method public void sendBroadcast(android.content.Intent, java.lang.String, int);
method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String);
- method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.os.Bundle);
- method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, int);
- method public void sendBroadcastMultiplePermissions(android.content.Intent, java.lang.String[]);
method public void sendOrderedBroadcast(android.content.Intent, java.lang.String);
method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, int, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, int, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
- method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, int, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void sendStickyBroadcast(android.content.Intent);
method public void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
- method public void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.os.Bundle);
method public void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
method public void setTheme(int);
@@ -155,17 +122,13 @@
method public void startActivity(android.content.Intent);
method public void startActivity(android.content.Intent, android.os.Bundle);
method public android.content.ComponentName startForegroundService(android.content.Intent);
- method public android.content.ComponentName startForegroundServiceAsUser(android.content.Intent, android.os.UserHandle);
method public boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle);
method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
method public android.content.ComponentName startService(android.content.Intent);
- method public android.content.ComponentName startServiceAsUser(android.content.Intent, android.os.UserHandle);
method public boolean stopService(android.content.Intent);
- method public boolean stopServiceAsUser(android.content.Intent, android.os.UserHandle);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
- method public void updateDisplay(int);
}
public deprecated class MockCursor implements android.database.Cursor {
@@ -221,8 +184,6 @@
public deprecated class MockPackageManager extends android.content.pm.PackageManager {
ctor public MockPackageManager();
- method public void addCrossProfileIntentFilter(android.content.IntentFilter, int, int, int);
- method public void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
method public void addPackageToPreferred(java.lang.String);
method public boolean addPermission(android.content.pm.PermissionInfo);
method public boolean addPermissionAsync(android.content.pm.PermissionInfo);
@@ -232,19 +193,10 @@
method public int checkPermission(java.lang.String, java.lang.String);
method public int checkSignatures(java.lang.String, java.lang.String);
method public int checkSignatures(int, int);
- method public void clearApplicationUserData(java.lang.String, android.content.pm.IPackageDataObserver);
- method public void clearCrossProfileIntentFilters(int);
method public void clearInstantAppCookie();
method public void clearPackagePreferredActivities(java.lang.String);
method public java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]);
- method public void deleteApplicationCacheFiles(java.lang.String, android.content.pm.IPackageDataObserver);
- method public void deleteApplicationCacheFilesAsUser(java.lang.String, int, android.content.pm.IPackageDataObserver);
- method public void deletePackage(java.lang.String, android.content.pm.IPackageDeleteObserver, int);
- method public void deletePackageAsUser(java.lang.String, android.content.pm.IPackageDeleteObserver, int, int);
method public void extendVerificationTimeout(int, int, long);
- method public void flushPackageRestrictionsAsUser(int);
- method public void freeStorage(java.lang.String, long, android.content.IntentSender);
- method public void freeStorageAndNotify(java.lang.String, long, android.content.pm.IPackageDataObserver);
method public android.graphics.drawable.Drawable getActivityBanner(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.graphics.drawable.Drawable getActivityBanner(android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.graphics.drawable.Drawable getActivityIcon(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -257,148 +209,75 @@
method public android.graphics.drawable.Drawable getApplicationBanner(android.content.pm.ApplicationInfo);
method public android.graphics.drawable.Drawable getApplicationBanner(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public int getApplicationEnabledSetting(java.lang.String);
- method public boolean getApplicationHiddenSettingAsUser(java.lang.String, android.os.UserHandle);
method public android.graphics.drawable.Drawable getApplicationIcon(android.content.pm.ApplicationInfo);
method public android.graphics.drawable.Drawable getApplicationIcon(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public android.content.pm.ApplicationInfo getApplicationInfoAsUser(java.lang.String, int, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.CharSequence getApplicationLabel(android.content.pm.ApplicationInfo);
method public android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo);
method public android.graphics.drawable.Drawable getApplicationLogo(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.ChangedPackages getChangedPackages(int);
method public int getComponentEnabledSetting(android.content.ComponentName);
method public android.graphics.drawable.Drawable getDefaultActivityIcon();
- method public java.lang.String getDefaultBrowserPackageNameAsUser(int);
method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
- method public android.content.ComponentName getHomeActivities(java.util.List<android.content.pm.ResolveInfo>);
- method public int getInstallReason(java.lang.String, android.os.UserHandle);
method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
- method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
- method public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
method public java.lang.String getInstallerPackageName(java.lang.String);
- method public java.lang.String getInstantAppAndroidId(java.lang.String, android.os.UserHandle);
method public byte[] getInstantAppCookie();
method public int getInstantAppCookieMaxBytes();
- method public int getInstantAppCookieMaxSize();
- method public android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String);
- method public android.content.ComponentName getInstantAppInstallerComponent();
- method public android.content.ComponentName getInstantAppResolverSettingsComponent();
- method public java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
- method public int getIntentVerificationStatusAsUser(java.lang.String, int);
- method public android.content.pm.KeySet getKeySetByAlias(java.lang.String, java.lang.String);
method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
- method public int getMoveStatus(int);
method public java.lang.String getNameForUid(int);
- method public java.lang.String[] getNamesForUids(int[]);
- method public java.util.List<android.os.storage.VolumeInfo> getPackageCandidateVolumes(android.content.pm.ApplicationInfo);
- method public android.os.storage.VolumeInfo getPackageCurrentVolume(android.content.pm.ApplicationInfo);
method public int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.PackageInfo getPackageInfo(android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public android.content.pm.PackageInfo getPackageInfoAsUser(java.lang.String, int, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.PackageInstaller getPackageInstaller();
- method public void getPackageSizeInfoAsUser(java.lang.String, int, android.content.pm.IPackageStatsObserver);
method public int getPackageUid(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public int getPackageUidAsUser(java.lang.String, int, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public int getPackageUidAsUser(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] getPackagesForUid(int);
method public java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
- method public java.lang.String getPermissionControllerPackageName();
- method public int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
method public android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.PermissionInfo getPermissionInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String);
method public java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int);
- method public java.util.List<android.os.storage.VolumeInfo> getPrimaryStorageCandidateVolumes();
- method public android.os.storage.VolumeInfo getPrimaryStorageCurrentVolume();
method public android.content.pm.ProviderInfo getProviderInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.ActivityInfo getReceiverInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.res.Resources getResourcesForActivity(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo);
method public android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
- method public android.content.res.Resources getResourcesForApplicationAsUser(java.lang.String, int);
method public android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public java.lang.String getServicesSystemSharedLibraryPackageName();
method public java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
- method public java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibrariesAsUser(int, int);
- method public java.lang.String getSharedSystemSharedLibraryPackageName();
- method public android.content.pm.KeySet getSigningKeySet(java.lang.String);
method public android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
method public java.lang.String[] getSystemSharedLibraryNames();
method public java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
- method public int getUidForSharedUser(java.lang.String);
- method public android.graphics.drawable.Drawable getUserBadgeForDensity(android.os.UserHandle, int);
- method public android.graphics.drawable.Drawable getUserBadgeForDensityNoBackground(android.os.UserHandle, int);
method public android.graphics.drawable.Drawable getUserBadgedDrawableForDensity(android.graphics.drawable.Drawable, android.os.UserHandle, android.graphics.Rect, int);
method public android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle);
method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
- method public android.content.pm.VerifierDeviceIdentity getVerifierDeviceIdentity();
method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
- method public void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public boolean hasSystemFeature(java.lang.String);
method public boolean hasSystemFeature(java.lang.String, int);
- method public int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
- method public int installExistingPackage(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public int installExistingPackageAsUser(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public void installPackage(android.net.Uri, android.app.PackageInstallObserver, int, java.lang.String);
method public boolean isInstantApp();
method public boolean isInstantApp(java.lang.String);
- method public boolean isPackageAvailable(java.lang.String);
- method public boolean isPackageSuspendedForUser(java.lang.String, int);
- method public boolean isPermissionReviewModeEnabled();
method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
method public boolean isSafeMode();
- method public boolean isSignedBy(java.lang.String, android.content.pm.KeySet);
- method public boolean isSignedByExactly(java.lang.String, android.content.pm.KeySet);
- method public boolean isUpgrade();
- method public android.graphics.drawable.Drawable loadItemIcon(android.content.pm.PackageItemInfo, android.content.pm.ApplicationInfo);
- method public android.graphics.drawable.Drawable loadUnbadgedItemIcon(android.content.pm.PackageItemInfo, android.content.pm.ApplicationInfo);
- method public int movePackage(java.lang.String, android.os.storage.VolumeInfo);
- method public int movePrimaryStorage(android.os.storage.VolumeInfo);
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
- method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, int);
method public java.util.List<android.content.pm.ProviderInfo> queryContentProviders(java.lang.String, int, int);
method public java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int);
method public java.util.List<android.content.pm.ResolveInfo> queryIntentActivities(android.content.Intent, int);
- method public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(android.content.Intent, int, int);
method public java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(android.content.ComponentName, android.content.Intent[], android.content.Intent, int);
method public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(android.content.Intent, int);
- method public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(android.content.Intent, int, int);
method public java.util.List<android.content.pm.ResolveInfo> queryIntentServices(android.content.Intent, int);
- method public java.util.List<android.content.pm.ResolveInfo> queryIntentServicesAsUser(android.content.Intent, int, int);
method public java.util.List<android.content.pm.PermissionInfo> queryPermissionsByGroup(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public void registerDexModule(java.lang.String, android.content.pm.PackageManager.DexModuleRegisterCallback);
- method public void registerMoveCallback(android.content.pm.PackageManager.MoveCallback, android.os.Handler);
- method public void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
method public void removePackageFromPreferred(java.lang.String);
method public void removePermission(java.lang.String);
- method public void replacePreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName);
method public android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int);
- method public android.content.pm.ResolveInfo resolveActivityAsUser(android.content.Intent, int, int);
method public android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int);
- method public android.content.pm.ProviderInfo resolveContentProviderAsUser(java.lang.String, int, int);
method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
- method public void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public void setApplicationCategoryHint(java.lang.String, int);
method public void setApplicationEnabledSetting(java.lang.String, int, int);
- method public boolean setApplicationHiddenSettingAsUser(java.lang.String, boolean, android.os.UserHandle);
method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
- method public boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
method public void setInstallerPackageName(java.lang.String, java.lang.String);
- method public boolean setInstantAppCookie(byte[]);
- method public java.lang.String[] setPackagesSuspendedAsUser(java.lang.String[], boolean, int);
- method public void setUpdateAvailable(java.lang.String, boolean);
- method public boolean shouldShowRequestPermissionRationale(java.lang.String);
- method public void unregisterMoveCallback(android.content.pm.PackageManager.MoveCallback);
method public void updateInstantAppCookie(byte[]);
- method public boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
- method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
- method public void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
method public void verifyPendingInstall(int, int);
}
diff --git a/test-mock/api/android-test-mock-removed.txt b/test-mock/api/android-test-mock-removed.txt
index 5b358cf..bd109a8 100644
--- a/test-mock/api/android-test-mock-removed.txt
+++ b/test-mock/api/android-test-mock-removed.txt
@@ -8,6 +8,7 @@
public deprecated class MockPackageManager extends android.content.pm.PackageManager {
method public deprecated java.lang.String getDefaultBrowserPackageName(int);
method public deprecated boolean setDefaultBrowserPackageName(java.lang.String, int);
+ method public boolean setInstantAppCookie(byte[]);
}
}
diff --git a/test-mock/api/android-test-mock-system-current.txt b/test-mock/api/android-test-mock-system-current.txt
new file mode 100644
index 0000000..20401a5
--- /dev/null
+++ b/test-mock/api/android-test-mock-system-current.txt
@@ -0,0 +1,38 @@
+package android.test.mock {
+
+ public class MockContext extends android.content.Context {
+ method public android.content.Context createCredentialProtectedStorageContext();
+ method public java.io.File getPreloadsFileCache();
+ method public boolean isCredentialProtectedStorage();
+ method public void sendBroadcast(android.content.Intent, java.lang.String, android.os.Bundle);
+ method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.os.Bundle);
+ method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+ }
+
+ public deprecated class MockPackageManager extends android.content.pm.PackageManager {
+ method public void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
+ method public java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
+ method public java.lang.String getDefaultBrowserPackageNameAsUser(int);
+ method public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
+ method public android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String);
+ method public android.content.ComponentName getInstantAppInstallerComponent();
+ method public android.content.ComponentName getInstantAppResolverSettingsComponent();
+ method public java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
+ method public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
+ method public int getIntentVerificationStatusAsUser(java.lang.String, int);
+ method public int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
+ method public void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+ method public int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public int installExistingPackage(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public void registerDexModule(java.lang.String, android.content.pm.PackageManager.DexModuleRegisterCallback);
+ method public void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
+ method public void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+ method public boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
+ method public void setUpdateAvailable(java.lang.String, boolean);
+ method public boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
+ method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
+ method public void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
+ }
+
+}
+
diff --git a/test-mock/api/android-test-mock-system-removed.txt b/test-mock/api/android-test-mock-system-removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test-mock/api/android-test-mock-system-removed.txt
diff --git a/test-mock/api/apicheck_msg_android_test_mock-system.txt b/test-mock/api/apicheck_msg_android_test_mock-system.txt
new file mode 100644
index 0000000..3a97117
--- /dev/null
+++ b/test-mock/api/apicheck_msg_android_test_mock-system.txt
@@ -0,0 +1,17 @@
+
+******************************
+You have tried to change the API from what has been previously approved.
+
+To make these errors go away, you have two choices:
+ 1) You can add "@hide" javadoc comments to the methods, etc. listed in the
+ errors above.
+
+ 2) You can update android-test-mock-current.txt by executing the following command:
+ make update-android-test-mock-system-api
+
+ To submit the revised android-test-mock-system-current.txt to the main Android repository,
+ you will need approval.
+******************************
+
+
+
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 5e5ba46..4dfd050 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -19,13 +19,12 @@
import android.annotation.SystemApi;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
-import android.app.Notification;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.BroadcastReceiver;
import android.content.IntentSender;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
@@ -44,8 +43,8 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.UserHandle;
-import android.view.DisplayAdjustments;
import android.view.Display;
+import android.view.DisplayAdjustments;
import java.io.File;
import java.io.FileInputStream;
@@ -53,6 +52,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.concurrent.Executor;
/**
* A mock {@link android.content.Context} class. All methods are non-functional and throw
@@ -87,6 +87,11 @@
}
@Override
+ public Executor getMainExecutor() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public Context getApplicationContext() {
throw new UnsupportedOperationException();
}
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index 67f1354..706f636 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -116,3 +116,6 @@
$(hide) $(ACP) $(ANDROID_TEST_RUNNER_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_RUNNER_REMOVED_API_FILE)
endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
+
+# additionally, build unit tests in a separate .apk
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/test-runner/tests/Android.mk b/test-runner/tests/Android.mk
index 7ee047e4..1a4f6d5 100644
--- a/test-runner/tests/Android.mk
+++ b/test-runner/tests/Android.mk
@@ -25,8 +25,8 @@
#
LOCAL_MODULE_TAGS := tests
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock
+LOCAL_STATIC_JAVA_LIBRARIES := junit
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/AppLaunch/Android.mk b/tests/AppLaunch/Android.mk
index 09739e5..917293f 100644
--- a/tests/AppLaunch/Android.mk
+++ b/tests/AppLaunch/Android.mk
@@ -13,6 +13,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
include $(BUILD_PACKAGE)
# Use the following include to make our test apk.
diff --git a/tests/Internal/Android.mk b/tests/Internal/Android.mk
index 73181ec..b69e3e4 100644
--- a/tests/Internal/Android.mk
+++ b/tests/Internal/Android.mk
@@ -14,6 +14,7 @@
android-support-test \
mockito-target-minus-junit4
+LOCAL_JAVA_RESOURCE_DIRS := res
LOCAL_CERTIFICATE := platform
LOCAL_PACKAGE_NAME := InternalTests
diff --git a/tests/Internal/AndroidManifest.xml b/tests/Internal/AndroidManifest.xml
index a2c95fb..e5a5694 100644
--- a/tests/Internal/AndroidManifest.xml
+++ b/tests/Internal/AndroidManifest.xml
@@ -18,8 +18,24 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.internal.tests">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.BIND_WALLPAPER" />
<application>
<uses-library android:name="android.test.runner" />
+
+ <service android:name="stub.DummyWallpaperService"
+ android:enabled="true"
+ android:directBootAware="true"
+ android:label="Dummy wallpaper"
+ android:permission="android.permission.BIND_WALLPAPER">
+
+ <intent-filter>
+ <action android:name="android.service.wallpaper.WallpaperService" />
+ </intent-filter>
+
+ <!-- Link to XML that defines the wallpaper info. -->
+ <meta-data android:name="android.service.wallpaper"
+ android:resource="@xml/livewallpaper" />
+ </service>
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/Internal/res/xml/livewallpaper.xml b/tests/Internal/res/xml/livewallpaper.xml
new file mode 100644
index 0000000..dbb0e47
--- /dev/null
+++ b/tests/Internal/res/xml/livewallpaper.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<wallpaper
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ androidprv:supportsAmbientMode="true"/>
\ No newline at end of file
diff --git a/tests/Internal/src/android/app/WallpaperInfoTest.java b/tests/Internal/src/android/app/WallpaperInfoTest.java
new file mode 100644
index 0000000..9d26270
--- /dev/null
+++ b/tests/Internal/src/android/app/WallpaperInfoTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Parcel;
+import android.service.wallpaper.WallpaperService;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * Tests for hidden WallpaperInfo methods.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class WallpaperInfoTest {
+
+ @Test
+ public void testSupportsAmbientMode() throws Exception {
+ Context context = InstrumentationRegistry.getTargetContext();
+
+ Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
+ intent.setPackage("com.android.internal.tests");
+ PackageManager pm = context.getPackageManager();
+ List<ResolveInfo> result = pm.queryIntentServices(intent, PackageManager.GET_META_DATA);
+ assertEquals(1, result.size());
+ ResolveInfo info = result.get(0);
+ WallpaperInfo wallpaperInfo = new WallpaperInfo(context, info);
+
+ // Defined as true in the XML
+ assertTrue("supportsAmbientMode should be true, as defined in the XML.",
+ wallpaperInfo.getSupportsAmbientMode());
+ Parcel parcel = Parcel.obtain();
+ wallpaperInfo.writeToParcel(parcel, 0 /* flags */);
+ parcel.setDataPosition(0);
+ WallpaperInfo fromParcel = WallpaperInfo.CREATOR.createFromParcel(parcel);
+ assertTrue("supportsAmbientMode should have been restored from parcelable",
+ fromParcel.getSupportsAmbientMode());
+ parcel.recycle();
+ }
+}
+
diff --git a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
new file mode 100644
index 0000000..f7ce2c7
--- /dev/null
+++ b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.service.wallpaper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class WallpaperServiceTest {
+
+ @Test
+ public void testDeliversAmbientModeChanged() {
+ int[] ambientModeChangedCount = {0};
+ WallpaperService service = new WallpaperService() {
+ @Override
+ public Engine onCreateEngine() {
+ return new Engine() {
+ @Override
+ public void onAmbientModeChanged(boolean inAmbientMode) {
+ ambientModeChangedCount[0]++;
+ }
+ };
+ }
+ };
+ WallpaperService.Engine engine = service.onCreateEngine();
+ engine.setCreated(true);
+
+ engine.doAmbientModeChanged(false);
+ assertFalse("ambient mode should be false", engine.isInAmbientMode());
+ assertEquals("onAmbientModeChanged should have been called",
+ ambientModeChangedCount[0], 1);
+
+ engine.doAmbientModeChanged(true);
+ assertTrue("ambient mode should be false", engine.isInAmbientMode());
+ assertEquals("onAmbientModeChanged should have been called",
+ ambientModeChangedCount[0], 2);
+ }
+
+}
diff --git a/tests/Internal/src/stub/DummyWallpaperService.java b/tests/Internal/src/stub/DummyWallpaperService.java
new file mode 100644
index 0000000..084c036
--- /dev/null
+++ b/tests/Internal/src/stub/DummyWallpaperService.java
@@ -0,0 +1,29 @@
+/*
+ * 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 stub;
+
+import android.service.wallpaper.WallpaperService;
+
+/**
+ * Dummy wallpaper service only for test purposes, won't draw anything.
+ */
+public class DummyWallpaperService extends WallpaperService {
+ @Override
+ public Engine onCreateEngine() {
+ return new Engine();
+ }
+}
diff --git a/tests/SurfaceComposition/Android.mk b/tests/SurfaceComposition/Android.mk
index 1962791..f59458d 100644
--- a/tests/SurfaceComposition/Android.mk
+++ b/tests/SurfaceComposition/Android.mk
@@ -29,7 +29,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := junit
-LOCAL_JAVA_LIBRARIES := android.test.runner.stubs
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
LOCAL_PACKAGE_NAME := SurfaceComposition
diff --git a/tests/WindowAnimationJank/Android.mk b/tests/WindowAnimationJank/Android.mk
index 8aac8a1..7800a80 100644
--- a/tests/WindowAnimationJank/Android.mk
+++ b/tests/WindowAnimationJank/Android.mk
@@ -29,6 +29,8 @@
ub-janktesthelper \
junit
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
LOCAL_SDK_VERSION := current
include $(BUILD_PACKAGE)
diff --git a/tests/net/OWNERS b/tests/net/OWNERS
index 2d71c20..6f77e04 100644
--- a/tests/net/OWNERS
+++ b/tests/net/OWNERS
@@ -1,10 +1,7 @@
set noparent
-per-file Android.mk = build.master@android.com
-per-file Android.mk = ek@google.com
-per-file Android.mk = hugobenichi@google.com
-per-file Android.mk = lorenzo@google.com
-
ek@google.com
hugobenichi@google.com
+jchalard@google.com
lorenzo@google.com
+satk@google.com
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
index ccb0f3b..0f40b45 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -80,7 +80,7 @@
int resourceId = 1;
IpSecSpiResponse spiResp =
new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
- when(mMockIpSecService.reserveSecurityParameterIndex(
+ when(mMockIpSecService.allocateSecurityParameterIndex(
eq(IpSecTransform.DIRECTION_IN),
eq(GOOGLE_DNS_4.getHostAddress()),
eq(DROID_SPI),
@@ -88,7 +88,7 @@
.thenReturn(spiResp);
IpSecManager.SecurityParameterIndex droidSpi =
- mIpSecManager.reserveSecurityParameterIndex(
+ mIpSecManager.allocateSecurityParameterIndex(
IpSecTransform.DIRECTION_IN, GOOGLE_DNS_4, DROID_SPI);
assertEquals(DROID_SPI, droidSpi.getSpi());
@@ -102,7 +102,7 @@
int resourceId = 1;
IpSecSpiResponse spiResp =
new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
- when(mMockIpSecService.reserveSecurityParameterIndex(
+ when(mMockIpSecService.allocateSecurityParameterIndex(
eq(IpSecTransform.DIRECTION_OUT),
eq(GOOGLE_DNS_4.getHostAddress()),
eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX),
@@ -110,7 +110,7 @@
.thenReturn(spiResp);
IpSecManager.SecurityParameterIndex randomSpi =
- mIpSecManager.reserveSecurityParameterIndex(
+ mIpSecManager.allocateSecurityParameterIndex(
IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
assertEquals(DROID_SPI, randomSpi.getSpi());
@@ -127,12 +127,13 @@
public void testAllocSpiResUnavaiableExeption() throws Exception {
IpSecSpiResponse spiResp =
new IpSecSpiResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE, 0, 0);
- when(mMockIpSecService.reserveSecurityParameterIndex(
+ when(mMockIpSecService.allocateSecurityParameterIndex(
anyInt(), anyString(), anyInt(), anyObject()))
.thenReturn(spiResp);
try {
- mIpSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+ mIpSecManager.allocateSecurityParameterIndex(
+ IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
fail("ResourceUnavailableException was not thrown");
} catch (IpSecManager.ResourceUnavailableException e) {
}
@@ -144,12 +145,13 @@
@Test
public void testAllocSpiSpiUnavaiableExeption() throws Exception {
IpSecSpiResponse spiResp = new IpSecSpiResponse(IpSecManager.Status.SPI_UNAVAILABLE, 0, 0);
- when(mMockIpSecService.reserveSecurityParameterIndex(
+ when(mMockIpSecService.allocateSecurityParameterIndex(
anyInt(), anyString(), anyInt(), anyObject()))
.thenReturn(spiResp);
try {
- mIpSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+ mIpSecManager.allocateSecurityParameterIndex(
+ IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
fail("ResourceUnavailableException was not thrown");
} catch (IpSecManager.ResourceUnavailableException e) {
}
@@ -161,7 +163,7 @@
@Test
public void testRequestAllocInvalidSpi() throws Exception {
try {
- mIpSecManager.reserveSecurityParameterIndex(
+ mIpSecManager.allocateSecurityParameterIndex(
IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4, 0);
fail("Able to allocate invalid spi");
} catch (IllegalArgumentException e) {
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 5c031eb..80e42a3 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -22,7 +22,6 @@
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -39,6 +38,7 @@
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.support.test.filters.SmallTest;
+import android.system.Os;
import java.net.Socket;
import java.util.Arrays;
@@ -125,7 +125,7 @@
.thenReturn(TEST_SPI_OUT);
IpSecSpiResponse spiResp =
- mIpSecService.reserveSecurityParameterIndex(
+ mIpSecService.allocateSecurityParameterIndex(
IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
assertEquals(IpSecManager.Status.OK, spiResp.status);
assertEquals(TEST_SPI_OUT, spiResp.spi);
@@ -142,7 +142,7 @@
.thenReturn(TEST_SPI_OUT);
IpSecSpiResponse spiResp =
- mIpSecService.reserveSecurityParameterIndex(
+ mIpSecService.allocateSecurityParameterIndex(
IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
@@ -154,6 +154,56 @@
anyString(),
anyString(),
eq(TEST_SPI_OUT));
+
+ // Verify quota and RefcountedResource objects cleaned up
+ IpSecService.UserRecord userRecord =
+ mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+ assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
+ try {
+ userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId);
+ fail("Expected IllegalArgumentException on attempt to access deleted resource");
+ } catch (IllegalArgumentException expected) {
+
+ }
+ }
+
+ @Test
+ public void testSecurityParameterIndexBinderDeath() throws Exception {
+ when(mMockNetd.ipSecAllocateSpi(
+ anyInt(),
+ eq(IpSecTransform.DIRECTION_OUT),
+ anyString(),
+ eq(mRemoteAddr),
+ eq(TEST_SPI_OUT)))
+ .thenReturn(TEST_SPI_OUT);
+
+ IpSecSpiResponse spiResp =
+ mIpSecService.allocateSecurityParameterIndex(
+ IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
+
+ IpSecService.UserRecord userRecord =
+ mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+ IpSecService.RefcountedResource refcountedRecord =
+ userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId);
+
+ refcountedRecord.binderDied();
+
+ verify(mMockNetd)
+ .ipSecDeleteSecurityAssociation(
+ eq(spiResp.resourceId),
+ anyInt(),
+ anyString(),
+ anyString(),
+ eq(TEST_SPI_OUT));
+
+ // Verify quota and RefcountedResource objects cleaned up
+ assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
+ try {
+ userRecord.mSpiRecords.getRefcountedResourceOrThrow(spiResp.resourceId);
+ fail("Expected IllegalArgumentException on attempt to access deleted resource");
+ } catch (IllegalArgumentException expected) {
+
+ }
}
private int getNewSpiResourceId(int direction, String remoteAddress, int returnSpi)
@@ -162,7 +212,7 @@
.thenReturn(returnSpi);
IpSecSpiResponse spi =
- mIpSecService.reserveSecurityParameterIndex(
+ mIpSecService.allocateSecurityParameterIndex(
direction,
NetworkUtils.numericToInetAddress(remoteAddress).getHostAddress(),
IpSecManager.INVALID_SECURITY_PARAMETER_INDEX,
@@ -379,6 +429,61 @@
anyString(),
anyString(),
eq(TEST_SPI_IN));
+
+ // Verify quota and RefcountedResource objects cleaned up
+ IpSecService.UserRecord userRecord =
+ mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+ assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent);
+ try {
+ userRecord.mTransformRecords.getRefcountedResourceOrThrow(
+ createTransformResp.resourceId);
+ fail("Expected IllegalArgumentException on attempt to access deleted resource");
+ } catch (IllegalArgumentException expected) {
+
+ }
+ }
+
+ @Test
+ public void testTransportModeTransformBinderDeath() throws Exception {
+ IpSecConfig ipSecConfig = new IpSecConfig();
+ addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+ addAuthAndCryptToIpSecConfig(ipSecConfig);
+
+ IpSecTransformResponse createTransformResp =
+ mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+
+ IpSecService.UserRecord userRecord =
+ mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+ IpSecService.RefcountedResource refcountedRecord =
+ userRecord.mTransformRecords.getRefcountedResourceOrThrow(
+ createTransformResp.resourceId);
+
+ refcountedRecord.binderDied();
+
+ verify(mMockNetd)
+ .ipSecDeleteSecurityAssociation(
+ eq(createTransformResp.resourceId),
+ eq(IpSecTransform.DIRECTION_OUT),
+ anyString(),
+ anyString(),
+ eq(TEST_SPI_OUT));
+ verify(mMockNetd)
+ .ipSecDeleteSecurityAssociation(
+ eq(createTransformResp.resourceId),
+ eq(IpSecTransform.DIRECTION_IN),
+ anyString(),
+ anyString(),
+ eq(TEST_SPI_IN));
+
+ // Verify quota and RefcountedResource objects cleaned up
+ assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent);
+ try {
+ userRecord.mTransformRecords.getRefcountedResourceOrThrow(
+ createTransformResp.resourceId);
+ fail("Expected IllegalArgumentException on attempt to access deleted resource");
+ } catch (IllegalArgumentException expected) {
+
+ }
}
@Test
diff --git a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java
new file mode 100644
index 0000000..cf8f715
--- /dev/null
+++ b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.IpSecService.IResource;
+import com.android.server.IpSecService.RefcountedResource;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link IpSecService.RefcountedResource}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class IpSecServiceRefcountedResourceTest {
+ Context mMockContext;
+ IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
+ IpSecService mIpSecService;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockContext = mock(Context.class);
+ mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
+ mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
+ }
+
+ private void assertResourceState(
+ RefcountedResource<IResource> resource,
+ int refCount,
+ int userReleaseCallCount,
+ int releaseReferenceCallCount,
+ int invalidateCallCount,
+ int freeUnderlyingResourcesCallCount)
+ throws RemoteException {
+ // Check refcount on RefcountedResource
+ assertEquals(refCount, resource.mRefCount);
+
+ // Check call count of RefcountedResource
+ verify(resource, times(userReleaseCallCount)).userRelease();
+ verify(resource, times(releaseReferenceCallCount)).releaseReference();
+
+ // Check call count of IResource
+ verify(resource.getResource(), times(invalidateCallCount)).invalidate();
+ verify(resource.getResource(), times(freeUnderlyingResourcesCallCount))
+ .freeUnderlyingResources();
+ }
+
+ /** Adds mockito instrumentation */
+ private RefcountedResource<IResource> getTestRefcountedResource(
+ RefcountedResource... children) {
+ return getTestRefcountedResource(new Binder(), children);
+ }
+
+ /** Adds mockito instrumentation with provided binder */
+ private RefcountedResource<IResource> getTestRefcountedResource(
+ IBinder binder, RefcountedResource... children) {
+ return spy(
+ mIpSecService
+ .new RefcountedResource<IResource>(mock(IResource.class), binder, children));
+ }
+
+ @Test
+ public void testConstructor() throws RemoteException {
+ IBinder binderMock = mock(IBinder.class);
+ RefcountedResource<IResource> resource = getTestRefcountedResource(binderMock);
+
+ // Verify resource's refcount starts at 1 (for user-reference)
+ assertResourceState(resource, 1, 0, 0, 0, 0);
+
+ // Verify linking to binder death
+ verify(binderMock).linkToDeath(anyObject(), anyInt());
+ }
+
+ @Test
+ public void testConstructorWithChildren() throws RemoteException {
+ IBinder binderMockChild = mock(IBinder.class);
+ IBinder binderMockParent = mock(IBinder.class);
+ RefcountedResource<IResource> childResource = getTestRefcountedResource(binderMockChild);
+ RefcountedResource<IResource> parentResource =
+ getTestRefcountedResource(binderMockParent, childResource);
+
+ // Verify parent's refcount starts at 1 (for user-reference)
+ assertResourceState(parentResource, 1, 0, 0, 0, 0);
+
+ // Verify child's refcounts were incremented
+ assertResourceState(childResource, 2, 0, 0, 0, 0);
+
+ // Verify linking to binder death
+ verify(binderMockChild).linkToDeath(anyObject(), anyInt());
+ verify(binderMockParent).linkToDeath(anyObject(), anyInt());
+ }
+
+ @Test
+ public void testFailLinkToDeath() throws RemoteException {
+ IBinder binderMock = mock(IBinder.class);
+ doThrow(new RemoteException()).when(binderMock).linkToDeath(anyObject(), anyInt());
+
+ RefcountedResource<IResource> refcountedResource = getTestRefcountedResource(binderMock);
+
+ // Verify that cleanup is performed (Spy limitations prevent verification of method calls
+ // for binder death scenario; check refcount to determine if cleanup was performed.)
+ assertEquals(-1, refcountedResource.mRefCount);
+ }
+
+ @Test
+ public void testCleanupAndRelease() throws RemoteException {
+ IBinder binderMock = mock(IBinder.class);
+ RefcountedResource<IResource> refcountedResource = getTestRefcountedResource(binderMock);
+
+ // Verify user-initiated cleanup path decrements refcount and calls full cleanup flow
+ refcountedResource.userRelease();
+ assertResourceState(refcountedResource, -1, 1, 1, 1, 1);
+
+ // Verify user-initated cleanup path unlinks from binder
+ verify(binderMock).unlinkToDeath(eq(refcountedResource), eq(0));
+ assertNull(refcountedResource.mBinder);
+ }
+
+ @Test
+ public void testMultipleCallsToCleanupAndRelease() throws RemoteException {
+ RefcountedResource<IResource> refcountedResource = getTestRefcountedResource();
+
+ // Verify calling userRelease multiple times does not trigger any other cleanup
+ // methods
+ refcountedResource.userRelease();
+ assertResourceState(refcountedResource, -1, 1, 1, 1, 1);
+
+ refcountedResource.userRelease();
+ refcountedResource.userRelease();
+ assertResourceState(refcountedResource, -1, 3, 1, 1, 1);
+ }
+
+ @Test
+ public void testBinderDeathAfterCleanupAndReleaseDoesNothing() throws RemoteException {
+ RefcountedResource<IResource> refcountedResource = getTestRefcountedResource();
+
+ refcountedResource.userRelease();
+ assertResourceState(refcountedResource, -1, 1, 1, 1, 1);
+
+ // Verify binder death call does not trigger any other cleanup methods if called after
+ // userRelease()
+ refcountedResource.binderDied();
+ assertResourceState(refcountedResource, -1, 2, 1, 1, 1);
+ }
+
+ @Test
+ public void testBinderDeath() throws RemoteException {
+ RefcountedResource<IResource> refcountedResource = getTestRefcountedResource();
+
+ // Verify binder death caused cleanup
+ refcountedResource.binderDied();
+ verify(refcountedResource, times(1)).binderDied();
+ assertResourceState(refcountedResource, -1, 1, 1, 1, 1);
+ assertNull(refcountedResource.mBinder);
+ }
+
+ @Test
+ public void testCleanupParentDecrementsChildRefcount() throws RemoteException {
+ RefcountedResource<IResource> childResource = getTestRefcountedResource();
+ RefcountedResource<IResource> parentResource = getTestRefcountedResource(childResource);
+
+ parentResource.userRelease();
+
+ // Verify parent gets cleaned up properly, and triggers releaseReference on
+ // child
+ assertResourceState(childResource, 1, 0, 1, 0, 0);
+ assertResourceState(parentResource, -1, 1, 1, 1, 1);
+ }
+
+ @Test
+ public void testCleanupReferencedChildDoesNotTriggerRelease() throws RemoteException {
+ RefcountedResource<IResource> childResource = getTestRefcountedResource();
+ RefcountedResource<IResource> parentResource = getTestRefcountedResource(childResource);
+
+ childResource.userRelease();
+
+ // Verify that child does not clean up kernel resources and quota.
+ assertResourceState(childResource, 1, 1, 1, 1, 0);
+ assertResourceState(parentResource, 1, 0, 0, 0, 0);
+ }
+
+ @Test
+ public void testTwoParents() throws RemoteException {
+ RefcountedResource<IResource> childResource = getTestRefcountedResource();
+ RefcountedResource<IResource> parentResource1 = getTestRefcountedResource(childResource);
+ RefcountedResource<IResource> parentResource2 = getTestRefcountedResource(childResource);
+
+ // Verify that child does not cleanup kernel resources and quota until all references
+ // have been released. Assumption: parents release correctly based on
+ // testCleanupParentDecrementsChildRefcount()
+ childResource.userRelease();
+ assertResourceState(childResource, 2, 1, 1, 1, 0);
+
+ parentResource1.userRelease();
+ assertResourceState(childResource, 1, 1, 2, 1, 0);
+
+ parentResource2.userRelease();
+ assertResourceState(childResource, -1, 1, 3, 1, 1);
+ }
+
+ @Test
+ public void testTwoChildren() throws RemoteException {
+ RefcountedResource<IResource> childResource1 = getTestRefcountedResource();
+ RefcountedResource<IResource> childResource2 = getTestRefcountedResource();
+ RefcountedResource<IResource> parentResource =
+ getTestRefcountedResource(childResource1, childResource2);
+
+ childResource1.userRelease();
+ assertResourceState(childResource1, 1, 1, 1, 1, 0);
+ assertResourceState(childResource2, 2, 0, 0, 0, 0);
+
+ parentResource.userRelease();
+ assertResourceState(childResource1, -1, 1, 2, 1, 1);
+ assertResourceState(childResource2, 1, 0, 1, 0, 0);
+
+ childResource2.userRelease();
+ assertResourceState(childResource1, -1, 1, 2, 1, 1);
+ assertResourceState(childResource2, -1, 1, 2, 1, 1);
+ }
+
+ @Test
+ public void testSampleUdpEncapTranform() throws RemoteException {
+ RefcountedResource<IResource> spi1 = getTestRefcountedResource();
+ RefcountedResource<IResource> spi2 = getTestRefcountedResource();
+ RefcountedResource<IResource> udpEncapSocket = getTestRefcountedResource();
+ RefcountedResource<IResource> transform =
+ getTestRefcountedResource(spi1, spi2, udpEncapSocket);
+
+ // Pretend one SPI goes out of reference (releaseManagedResource -> userRelease)
+ spi1.userRelease();
+
+ // User called releaseManagedResource on udpEncap socket
+ udpEncapSocket.userRelease();
+
+ // User dies, and binder kills the rest
+ spi2.binderDied();
+ transform.binderDied();
+
+ // Check resource states
+ assertResourceState(spi1, -1, 1, 2, 1, 1);
+ assertResourceState(spi2, -1, 1, 2, 1, 1);
+ assertResourceState(udpEncapSocket, -1, 1, 2, 1, 1);
+ assertResourceState(transform, -1, 1, 1, 1, 1);
+ }
+
+ @Test
+ public void testSampleDualTransformEncapSocket() throws RemoteException {
+ RefcountedResource<IResource> spi1 = getTestRefcountedResource();
+ RefcountedResource<IResource> spi2 = getTestRefcountedResource();
+ RefcountedResource<IResource> spi3 = getTestRefcountedResource();
+ RefcountedResource<IResource> spi4 = getTestRefcountedResource();
+ RefcountedResource<IResource> udpEncapSocket = getTestRefcountedResource();
+ RefcountedResource<IResource> transform1 =
+ getTestRefcountedResource(spi1, spi2, udpEncapSocket);
+ RefcountedResource<IResource> transform2 =
+ getTestRefcountedResource(spi3, spi4, udpEncapSocket);
+
+ // Pretend one SPIs goes out of reference (releaseManagedResource -> userRelease)
+ spi1.userRelease();
+
+ // User called releaseManagedResource on udpEncap socket and spi4
+ udpEncapSocket.userRelease();
+ spi4.userRelease();
+
+ // User dies, and binder kills the rest
+ spi2.binderDied();
+ spi3.binderDied();
+ transform2.binderDied();
+ transform1.binderDied();
+
+ // Check resource states
+ assertResourceState(spi1, -1, 1, 2, 1, 1);
+ assertResourceState(spi2, -1, 1, 2, 1, 1);
+ assertResourceState(spi3, -1, 1, 2, 1, 1);
+ assertResourceState(spi4, -1, 1, 2, 1, 1);
+ assertResourceState(udpEncapSocket, -1, 1, 3, 1, 1);
+ assertResourceState(transform1, -1, 1, 1, 1, 1);
+ assertResourceState(transform2, -1, 1, 1, 1, 1);
+ }
+
+ @Test
+ public void fuzzTest() throws RemoteException {
+ List<RefcountedResource<IResource>> resources = new ArrayList<>();
+
+ // Build a tree of resources
+ for (int i = 0; i < 100; i++) {
+ // Choose a random number of children from the existing list
+ int numChildren = ThreadLocalRandom.current().nextInt(0, resources.size() + 1);
+
+ // Build a (random) list of children
+ Set<RefcountedResource<IResource>> children = new HashSet<>();
+ for (int j = 0; j < numChildren; j++) {
+ int childIndex = ThreadLocalRandom.current().nextInt(0, resources.size());
+ children.add(resources.get(childIndex));
+ }
+
+ RefcountedResource<IResource> newRefcountedResource =
+ getTestRefcountedResource(
+ children.toArray(new RefcountedResource[children.size()]));
+ resources.add(newRefcountedResource);
+ }
+
+ // Cleanup all resources in a random order
+ List<RefcountedResource<IResource>> clonedResources =
+ new ArrayList<>(resources); // shallow copy
+ while (!clonedResources.isEmpty()) {
+ int index = ThreadLocalRandom.current().nextInt(0, clonedResources.size());
+ RefcountedResource<IResource> refcountedResource = clonedResources.get(index);
+ refcountedResource.userRelease();
+ clonedResources.remove(index);
+ }
+
+ // Verify all resources were cleaned up properly
+ for (RefcountedResource<IResource> refcountedResource : resources) {
+ assertEquals(-1, refcountedResource.mRefCount);
+ }
+ }
+}
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 0720886f..5d1e10e 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -27,6 +27,7 @@
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -40,10 +41,14 @@
import android.net.IpSecUdpEncapResponse;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.system.ErrnoException;
import android.system.Os;
+import android.system.StructStat;
+
+import dalvik.system.SocketTagger;
import java.io.FileDescriptor;
import java.net.InetAddress;
@@ -56,6 +61,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
/** Unit tests for {@link IpSecService}. */
@SmallTest
@@ -131,7 +137,39 @@
mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
udpEncapResp.fileDescriptor.close();
- // TODO: Added check for the resource tracker
+ IpSecService.UserRecord userRecord =
+ mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+ assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
+ try {
+ userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
+ fail("Expected IllegalArgumentException on attempt to access deleted resource");
+ } catch (IllegalArgumentException expected) {
+
+ }
+ }
+
+ @Test
+ public void testUdpEncapsulationSocketBinderDeath() throws Exception {
+ int localport = findUnusedPort();
+
+ IpSecUdpEncapResponse udpEncapResp =
+ mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
+
+ IpSecService.UserRecord userRecord =
+ mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+ IpSecService.RefcountedResource refcountedRecord =
+ userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(
+ udpEncapResp.resourceId);
+
+ refcountedRecord.binderDied();
+
+ assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
+ try {
+ userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
+ fail("Expected IllegalArgumentException on attempt to access deleted resource");
+ } catch (IllegalArgumentException expected) {
+
+ }
}
@Test
@@ -255,7 +293,7 @@
for (String address : invalidAddresses) {
try {
IpSecSpiResponse spiResp =
- mIpSecService.reserveSecurityParameterIndex(
+ mIpSecService.allocateSecurityParameterIndex(
IpSecTransform.DIRECTION_OUT, address, DROID_SPI, new Binder());
fail("Invalid address was passed through IpSecService validation: " + address);
} catch (IllegalArgumentException e) {
@@ -336,7 +374,7 @@
// Reserve spis until it fails.
for (int i = 0; i < MAX_NUM_SPIS; i++) {
IpSecSpiResponse newSpi =
- mIpSecService.reserveSecurityParameterIndex(
+ mIpSecService.allocateSecurityParameterIndex(
0x1,
InetAddress.getLoopbackAddress().getHostAddress(),
DROID_SPI + i,
@@ -352,7 +390,7 @@
// Try to reserve one more SPI, and should fail.
IpSecSpiResponse extraSpi =
- mIpSecService.reserveSecurityParameterIndex(
+ mIpSecService.allocateSecurityParameterIndex(
0x1,
InetAddress.getLoopbackAddress().getHostAddress(),
DROID_SPI + MAX_NUM_SPIS,
@@ -366,7 +404,7 @@
// Should successfully reserve one more spi.
extraSpi =
- mIpSecService.reserveSecurityParameterIndex(
+ mIpSecService.allocateSecurityParameterIndex(
0x1,
InetAddress.getLoopbackAddress().getHostAddress(),
DROID_SPI + MAX_NUM_SPIS,
@@ -379,4 +417,84 @@
mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
}
}
+
+ @Test
+ public void testUidFdtagger() throws Exception {
+ SocketTagger actualSocketTagger = SocketTagger.get();
+
+ try {
+ FileDescriptor sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ // Has to be done after socket creation because BlockGuardOS calls tag on new sockets
+ SocketTagger mockSocketTagger = mock(SocketTagger.class);
+ SocketTagger.set(mockSocketTagger);
+
+ mIpSecService.mUidFdTagger.tag(sockFd, Process.LAST_APPLICATION_UID);
+ verify(mockSocketTagger).tag(eq(sockFd));
+ } finally {
+ SocketTagger.set(actualSocketTagger);
+ }
+ }
+
+ /**
+ * Checks if two file descriptors point to the same file.
+ *
+ * <p>According to stat.h documentation, the correct way to check for equivalent or duplicated
+ * file descriptors is to check their inode and device. These two entries uniquely identify any
+ * file.
+ */
+ private boolean fileDescriptorsEqual(FileDescriptor fd1, FileDescriptor fd2) {
+ try {
+ StructStat fd1Stat = Os.fstat(fd1);
+ StructStat fd2Stat = Os.fstat(fd2);
+
+ return fd1Stat.st_ino == fd2Stat.st_ino && fd1Stat.st_dev == fd2Stat.st_dev;
+ } catch (ErrnoException e) {
+ return false;
+ }
+ }
+
+ @Test
+ public void testOpenUdpEncapSocketTagsSocket() throws Exception {
+ IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class);
+ IpSecService testIpSecService =
+ new IpSecService(mMockContext, mMockIpSecSrvConfig, mockTagger);
+
+ IpSecUdpEncapResponse udpEncapResp =
+ testIpSecService.openUdpEncapsulationSocket(0, new Binder());
+ assertNotNull(udpEncapResp);
+ assertEquals(IpSecManager.Status.OK, udpEncapResp.status);
+
+ FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
+ ArgumentMatcher<FileDescriptor> fdMatcher =
+ (argFd) -> {
+ return fileDescriptorsEqual(sockFd, argFd);
+ };
+ verify(mockTagger).tag(argThat(fdMatcher), eq(Os.getuid()));
+
+ testIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
+ udpEncapResp.fileDescriptor.close();
+ }
+
+ @Test
+ public void testOpenUdpEncapsulationSocketCallsSetEncapSocketOwner() throws Exception {
+ IpSecUdpEncapResponse udpEncapResp =
+ mIpSecService.openUdpEncapsulationSocket(0, new Binder());
+
+ FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
+ ArgumentMatcher<FileDescriptor> fdMatcher = (arg) -> {
+ try {
+ StructStat sockStat = Os.fstat(sockFd);
+ StructStat argStat = Os.fstat(arg);
+
+ return sockStat.st_ino == argStat.st_ino
+ && sockStat.st_dev == argStat.st_dev;
+ } catch (ErrnoException e) {
+ return false;
+ }
+ };
+
+ verify(mMockNetd).ipSecSetEncapSocketOwner(argThat(fdMatcher), eq(Os.getuid()));
+ mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
+ }
}
diff --git a/tests/utils/testutils/Android.mk b/tests/utils/testutils/Android.mk
index 543c652..a76616f 100644
--- a/tests/utils/testutils/Android.mk
+++ b/tests/utils/testutils/Android.mk
@@ -24,9 +24,12 @@
LOCAL_SRC_FILES := $(call all-java-files-under,java)
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
- mockito-target-minus-junit4
+ android-support-test
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock
+LOCAL_JAVA_LIBRARIES := \
+ android.test.runner \
+ android.test.base \
+ android.test.mock \
+ mockito-target-minus-junit4 \
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 0f6fb50..5831875 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -294,36 +294,42 @@
printer->Print("/");
printer->Print(entry->name);
- switch (entry->symbol_status.state) {
- case SymbolState::kPublic:
+ switch (entry->visibility.level) {
+ case Visibility::Level::kPublic:
printer->Print(" PUBLIC");
break;
- case SymbolState::kPrivate:
+ case Visibility::Level::kPrivate:
printer->Print(" _PRIVATE_");
break;
- case SymbolState::kUndefined:
+ case Visibility::Level::kUndefined:
// Print nothing.
break;
}
+ if (entry->overlayable) {
+ printer->Print(" OVERLAYABLE");
+ }
+
printer->Println();
- printer->Indent();
- for (const auto& value : entry->values) {
- printer->Print("(");
- printer->Print(value->config.to_string());
- printer->Print(") ");
- value->value->Accept(&headline_printer);
- if (options.show_sources && !value->value->GetSource().path.empty()) {
- printer->Print(" src=");
- printer->Print(value->value->GetSource().to_string());
- }
- printer->Println();
+ if (options.show_values) {
printer->Indent();
- value->value->Accept(&body_printer);
+ for (const auto& value : entry->values) {
+ printer->Print("(");
+ printer->Print(value->config.to_string());
+ printer->Print(") ");
+ value->value->Accept(&headline_printer);
+ if (options.show_sources && !value->value->GetSource().path.empty()) {
+ printer->Print(" src=");
+ printer->Print(value->value->GetSource().to_string());
+ }
+ printer->Println();
+ printer->Indent();
+ value->value->Accept(&body_printer);
+ printer->Undent();
+ }
printer->Undent();
}
- printer->Undent();
}
printer->Undent();
}
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index 3c1ee4c..6209a04 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -29,6 +29,7 @@
struct DebugPrintTableOptions {
bool show_sources = false;
+ bool show_values = true;
};
struct Debug {
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 4cc60a8..24b28dd 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -30,7 +30,7 @@
#include "util/Util.h"
#include "xml/XmlPullParser.h"
-using android::StringPiece;
+using ::android::StringPiece;
namespace aapt {
@@ -90,9 +90,12 @@
ConfigDescription config;
std::string product;
Source source;
+
ResourceId id;
- Maybe<SymbolState> symbol_state;
+ Visibility::Level visibility_level = Visibility::Level::kUndefined;
bool allow_new = false;
+ bool overlayable = false;
+
std::string comment;
std::unique_ptr<Value> value;
std::list<ParsedResource> child_resources;
@@ -106,24 +109,41 @@
res->comment = trimmed_comment.to_string();
}
- if (res->symbol_state) {
- Symbol symbol;
- symbol.state = res->symbol_state.value();
- symbol.source = res->source;
- symbol.comment = res->comment;
- symbol.allow_new = res->allow_new;
- if (!table->SetSymbolState(res->name, res->id, symbol, diag)) {
+ if (res->visibility_level != Visibility::Level::kUndefined) {
+ Visibility visibility;
+ visibility.level = res->visibility_level;
+ visibility.source = res->source;
+ visibility.comment = res->comment;
+ if (!table->SetVisibilityWithId(res->name, visibility, res->id, diag)) {
return false;
}
}
- if (res->value) {
+ if (res->allow_new) {
+ AllowNew allow_new;
+ allow_new.source = res->source;
+ allow_new.comment = res->comment;
+ if (!table->SetAllowNew(res->name, allow_new, diag)) {
+ return false;
+ }
+ }
+
+ if (res->overlayable) {
+ Overlayable overlayable;
+ overlayable.source = res->source;
+ overlayable.comment = res->comment;
+ if (!table->SetOverlayable(res->name, overlayable, diag)) {
+ return false;
+ }
+ }
+
+ if (res->value != nullptr) {
// Attach the comment, source and config to the value.
res->value->SetComment(std::move(res->comment));
res->value->SetSource(std::move(res->source));
- if (!table->AddResource(res->name, res->id, res->config, res->product, std::move(res->value),
- diag)) {
+ if (!table->AddResourceWithId(res->name, res->id, res->config, res->product,
+ std::move(res->value), diag)) {
return false;
}
}
@@ -601,8 +621,7 @@
// Process the raw value.
std::unique_ptr<Item> processed_item =
- ResourceUtils::TryParseItemForAttribute(raw_value, type_mask,
- on_create_reference);
+ ResourceUtils::TryParseItemForAttribute(raw_value, type_mask, on_create_reference);
if (processed_item) {
// Fix up the reference.
if (Reference* ref = ValueCast<Reference>(processed_item.get())) {
@@ -689,8 +708,7 @@
return true;
}
-bool ResourceParser::ParsePublic(xml::XmlPullParser* parser,
- ParsedResource* out_resource) {
+bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource) {
if (out_resource->config != ConfigDescription::DefaultConfig()) {
diag_->Warn(DiagMessage(out_resource->source)
<< "ignoring configuration '" << out_resource->config << "' for <public> tag");
@@ -728,7 +746,7 @@
out_resource->value = util::make_unique<Id>();
}
- out_resource->symbol_state = SymbolState::kPublic;
+ out_resource->visibility_level = Visibility::Level::kPublic;
return true;
}
@@ -818,7 +836,7 @@
child_resource.id = next_id;
child_resource.comment = std::move(comment);
child_resource.source = item_source;
- child_resource.symbol_state = SymbolState::kPublic;
+ child_resource.visibility_level = Visibility::Level::kPublic;
out_resource->child_resources.push_back(std::move(child_resource));
next_id.id += 1;
@@ -864,7 +882,7 @@
return false;
}
- out_resource->symbol_state = SymbolState::kPrivate;
+ out_resource->visibility_level = Visibility::Level::kPrivate;
return true;
}
@@ -920,8 +938,12 @@
continue;
}
- // TODO(b/64980941): Mark the symbol as overlayable and allow marking which entity can overlay
- // the resource (system/app).
+ ParsedResource child_resource;
+ child_resource.name.type = *type;
+ child_resource.name.entry = maybe_name.value().to_string();
+ child_resource.source = item_source;
+ child_resource.overlayable = true;
+ out_resource->child_resources.push_back(std::move(child_resource));
xml::XmlPullParser::SkipCurrentElement(parser);
} else if (!ShouldIgnoreElement(element_namespace, element_name)) {
@@ -932,10 +954,9 @@
return !error;
}
-bool ResourceParser::ParseAddResource(xml::XmlPullParser* parser,
- ParsedResource* out_resource) {
+bool ResourceParser::ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource) {
if (ParseSymbolImpl(parser, out_resource)) {
- out_resource->symbol_state = SymbolState::kUndefined;
+ out_resource->visibility_level = Visibility::Level::kUndefined;
out_resource->allow_new = true;
return true;
}
@@ -1395,9 +1416,8 @@
ParsedResource* out_resource) {
out_resource->name.type = ResourceType::kStyleable;
- // Declare-styleable is kPrivate by default, because it technically only
- // exists in R.java.
- out_resource->symbol_state = SymbolState::kPublic;
+ // Declare-styleable is kPrivate by default, because it technically only exists in R.java.
+ out_resource->visibility_level = Visibility::Level::kPublic;
// Declare-styleable only ends up in default config;
if (out_resource->config != ConfigDescription::DefaultConfig()) {
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 9a5cd3e..618c8ed 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -29,8 +29,8 @@
using ::aapt::io::StringInputStream;
using ::aapt::test::StrValueEq;
using ::aapt::test::ValueEq;
-using ::android::ResTable_map;
using ::android::Res_value;
+using ::android::ResTable_map;
using ::android::StringPiece;
using ::testing::Eq;
using ::testing::IsEmpty;
@@ -38,6 +38,7 @@
using ::testing::NotNull;
using ::testing::Pointee;
using ::testing::SizeIs;
+using ::testing::StrEq;
namespace aapt {
@@ -482,7 +483,7 @@
Maybe<ResourceTable::SearchResult> result =
table_.FindResource(test::ParseNameOrDie("styleable/foo"));
ASSERT_TRUE(result);
- EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kPublic));
+ EXPECT_THAT(result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
Attribute* attr = test::GetValue<Attribute>(&table_, "attr/bar");
ASSERT_THAT(attr, NotNull());
@@ -718,6 +719,26 @@
EXPECT_THAT(actual_id, Eq(ResourceId(0x01010041)));
}
+TEST_F(ResourceParserTest, StrongestSymbolVisibilityWins) {
+ std::string input = R"(
+ <!-- private -->
+ <java-symbol type="string" name="foo" />
+ <!-- public -->
+ <public type="string" name="foo" id="0x01020000" />
+ <!-- private2 -->
+ <java-symbol type="string" name="foo" />)";
+ ASSERT_TRUE(TestParse(input));
+
+ Maybe<ResourceTable::SearchResult> result =
+ table_.FindResource(test::ParseNameOrDie("string/foo"));
+ ASSERT_TRUE(result);
+
+ ResourceEntry* entry = result.value().entry;
+ ASSERT_THAT(entry, NotNull());
+ EXPECT_THAT(entry->visibility.level, Eq(Visibility::Level::kPublic));
+ EXPECT_THAT(entry->visibility.comment, StrEq("public"));
+}
+
TEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) {
ASSERT_TRUE(TestParse(R"(<item type="layout" name="foo">@layout/bar</item>)"));
ASSERT_FALSE(TestParse(R"(<item type="layout" name="bar">"this is a string"</item>)"));
@@ -731,8 +752,8 @@
ASSERT_TRUE(result);
const ResourceEntry* entry = result.value().entry;
ASSERT_THAT(entry, NotNull());
- EXPECT_THAT(entry->symbol_status.state, Eq(SymbolState::kUndefined));
- EXPECT_TRUE(entry->symbol_status.allow_new);
+ EXPECT_THAT(entry->visibility.level, Eq(Visibility::Level::kUndefined));
+ EXPECT_TRUE(entry->allow_new);
}
TEST_F(ResourceParserTest, ParseItemElementWithFormat) {
@@ -833,6 +854,22 @@
<item type="string" name="bar" />
</overlayable>)";
ASSERT_TRUE(TestParse(input));
+
+ Maybe<ResourceTable::SearchResult> search_result =
+ table_.FindResource(test::ParseNameOrDie("string/bar"));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+ EXPECT_TRUE(search_result.value().entry->overlayable);
+}
+
+TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
+ std::string input = R"(
+ <overlayable>
+ <item type="string" name="foo" />
+ <item type="string" name="foo" />
+ </overlayable>)";
+ EXPECT_FALSE(TestParse(input));
}
} // namespace aapt
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 0304e21..3172892 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -22,6 +22,7 @@
#include <tuple>
#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
#include "androidfw/ResourceTypes.h"
#include "ConfigDescription.h"
@@ -33,6 +34,7 @@
using ::aapt::text::IsValidResourceEntryName;
using ::android::StringPiece;
+using ::android::base::StringPrintf;
namespace aapt {
@@ -45,7 +47,7 @@
return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
}
-ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) {
+ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) const {
const auto last = packages.end();
auto iter = std::lower_bound(packages.begin(), last, name,
less_than_struct_with_name<ResourceTablePackage>);
@@ -55,7 +57,7 @@
return nullptr;
}
-ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) {
+ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) const {
for (auto& package : packages) {
if (package->id && package->id.value() == id) {
return package.get();
@@ -206,30 +208,23 @@
return results;
}
-/**
- * The default handler for collisions.
- *
- * Typically, a weak value will be overridden by a strong value. An existing
- * weak
- * value will not be overridden by an incoming weak value.
- *
- * There are some exceptions:
- *
- * Attributes: There are two types of Attribute values: USE and DECL.
- *
- * USE is anywhere an Attribute is declared without a format, and in a place
- * that would
- * be legal to declare if the Attribute already existed. This is typically in a
- * <declare-styleable> tag. Attributes defined in a <declare-styleable> are also
- * weak.
- *
- * DECL is an absolute declaration of an Attribute and specifies an explicit
- * format.
- *
- * A DECL will override a USE without error. Two DECLs must match in their
- * format for there to be
- * no error.
- */
+// The default handler for collisions.
+//
+// Typically, a weak value will be overridden by a strong value. An existing weak
+// value will not be overridden by an incoming weak value.
+//
+// There are some exceptions:
+//
+// Attributes: There are two types of Attribute values: USE and DECL.
+//
+// USE is anywhere an Attribute is declared without a format, and in a place that would
+// be legal to declare if the Attribute already existed. This is typically in a
+// <declare-styleable> tag. Attributes defined in a <declare-styleable> are also weak.
+//
+// DECL is an absolute declaration of an Attribute and specifies an explicit format.
+//
+// A DECL will override a USE without error. Two DECLs must match in their format for there to be
+// no error.
ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* existing,
Value* incoming) {
Attribute* existing_attr = ValueCast<Attribute>(existing);
@@ -287,14 +282,14 @@
return CollisionResult::kConflict;
}
-static StringPiece ValidateName(const StringPiece& name) {
+static StringPiece ResourceNameValidator(const StringPiece& name) {
if (!IsValidResourceEntryName(name)) {
return name;
}
return {};
}
-static StringPiece SkipValidateName(const StringPiece& /*name*/) {
+static StringPiece SkipNameValidator(const StringPiece& /*name*/) {
return {};
}
@@ -303,17 +298,14 @@
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return AddResourceImpl(name, {}, config, product, std::move(value), ValidateName,
+ return AddResourceImpl(name, {}, config, product, std::move(value), ResourceNameValidator,
ResolveValueCollision, diag);
}
-bool ResourceTable::AddResource(const ResourceNameRef& name,
- const ResourceId& res_id,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value,
- IDiagnostics* diag) {
- return AddResourceImpl(name, res_id, config, product, std::move(value), ValidateName,
+bool ResourceTable::AddResourceWithId(const ResourceNameRef& name, const ResourceId& res_id,
+ const ConfigDescription& config, const StringPiece& product,
+ std::unique_ptr<Value> value, IDiagnostics* diag) {
+ return AddResourceImpl(name, res_id, config, product, std::move(value), ResourceNameValidator,
ResolveValueCollision, diag);
}
@@ -322,14 +314,14 @@
const Source& source,
const StringPiece& path,
IDiagnostics* diag) {
- return AddFileReferenceImpl(name, config, source, path, nullptr, ValidateName, diag);
+ return AddFileReferenceImpl(name, config, source, path, nullptr, ResourceNameValidator, diag);
}
-bool ResourceTable::AddFileReferenceAllowMangled(
- const ResourceNameRef& name, const ConfigDescription& config,
- const Source& source, const StringPiece& path, io::IFile* file,
- IDiagnostics* diag) {
- return AddFileReferenceImpl(name, config, source, path, file, SkipValidateName, diag);
+bool ResourceTable::AddFileReferenceMangled(const ResourceNameRef& name,
+ const ConfigDescription& config, const Source& source,
+ const StringPiece& path, io::IFile* file,
+ IDiagnostics* diag) {
+ return AddFileReferenceImpl(name, config, source, path, file, SkipNameValidator, diag);
}
bool ResourceTable::AddFileReferenceImpl(const ResourceNameRef& name,
@@ -344,88 +336,85 @@
name_validator, ResolveValueCollision, diag);
}
-bool ResourceTable::AddResourceAllowMangled(const ResourceNameRef& name,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value,
- IDiagnostics* diag) {
- return AddResourceImpl(name, ResourceId{}, config, product, std::move(value), SkipValidateName,
+bool ResourceTable::AddResourceMangled(const ResourceNameRef& name, const ConfigDescription& config,
+ const StringPiece& product, std::unique_ptr<Value> value,
+ IDiagnostics* diag) {
+ return AddResourceImpl(name, ResourceId{}, config, product, std::move(value), SkipNameValidator,
ResolveValueCollision, diag);
}
-bool ResourceTable::AddResourceAllowMangled(const ResourceNameRef& name,
- const ResourceId& id,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value,
- IDiagnostics* diag) {
- return AddResourceImpl(name, id, config, product, std::move(value), SkipValidateName,
+bool ResourceTable::AddResourceWithIdMangled(const ResourceNameRef& name, const ResourceId& id,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value, IDiagnostics* diag) {
+ return AddResourceImpl(name, id, config, product, std::move(value), SkipNameValidator,
ResolveValueCollision, diag);
}
+bool ResourceTable::ValidateName(NameValidator name_validator, const ResourceNameRef& name,
+ const Source& source, IDiagnostics* diag) {
+ const StringPiece bad_char = name_validator(name.entry);
+ if (!bad_char.empty()) {
+ diag->Error(DiagMessage(source) << "resource '" << name << "' has invalid entry name '"
+ << name.entry << "'. Invalid character '" << bad_char << "'");
+ return false;
+ }
+ return true;
+}
+
bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
const ConfigDescription& config, const StringPiece& product,
std::unique_ptr<Value> value, NameValidator name_validator,
- const CollisionResolverFunc& conflictResolver,
+ const CollisionResolverFunc& conflict_resolver,
IDiagnostics* diag) {
CHECK(value != nullptr);
CHECK(diag != nullptr);
- const StringPiece bad_char = name_validator(name.entry);
- if (!bad_char.empty()) {
- diag->Error(DiagMessage(value->GetSource()) << "resource '" << name
- << "' has invalid entry name '" << name.entry
- << "'. Invalid character '" << bad_char << "'");
-
+ const Source& source = value->GetSource();
+ if (!ValidateName(name_validator, name, source, diag)) {
return false;
}
ResourceTablePackage* package = FindOrCreatePackage(name.package);
if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
- diag->Error(DiagMessage(value->GetSource())
- << "trying to add resource '" << name << "' with ID " << res_id
- << " but package '" << package->name << "' already has ID "
- << std::hex << (int)package->id.value() << std::dec);
+ diag->Error(DiagMessage(source) << "trying to add resource '" << name << "' with ID " << res_id
+ << " but package '" << package->name << "' already has ID "
+ << StringPrintf("%02x", package->id.value()));
return false;
}
ResourceTableType* type = package->FindOrCreateType(name.type);
if (res_id.is_valid_dynamic() && type->id && type->id.value() != res_id.type_id()) {
- diag->Error(DiagMessage(value->GetSource())
- << "trying to add resource '" << name << "' with ID " << res_id
- << " but type '" << type->type << "' already has ID "
- << std::hex << (int)type->id.value() << std::dec);
+ diag->Error(DiagMessage(source)
+ << "trying to add resource '" << name << "' with ID " << res_id << " but type '"
+ << type->type << "' already has ID " << StringPrintf("%02x", type->id.value()));
return false;
}
ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
if (res_id.is_valid_dynamic() && entry->id && entry->id.value() != res_id.entry_id()) {
- diag->Error(DiagMessage(value->GetSource())
+ diag->Error(DiagMessage(source)
<< "trying to add resource '" << name << "' with ID " << res_id
<< " but resource already has ID "
- << ResourceId(package->id.value(), type->id.value(),
- entry->id.value()));
+ << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
return false;
}
ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
- if (!config_value->value) {
+ if (config_value->value == nullptr) {
// Resource does not exist, add it now.
config_value->value = std::move(value);
-
} else {
- switch (conflictResolver(config_value->value.get(), value.get())) {
+ switch (conflict_resolver(config_value->value.get(), value.get())) {
case CollisionResult::kTakeNew:
// Take the incoming value.
config_value->value = std::move(value);
break;
case CollisionResult::kConflict:
- diag->Error(DiagMessage(value->GetSource())
- << "duplicate value for resource '" << name << "' "
- << "with config '" << config << "'");
- diag->Error(DiagMessage(config_value->value->GetSource())
- << "resource previously defined here");
+ diag->Error(DiagMessage(source) << "duplicate value for resource '" << name << "' "
+ << "with config '" << config << "'");
+ diag->Error(DiagMessage(source) << "resource previously defined here");
return false;
case CollisionResult::kKeepOriginal:
@@ -441,52 +430,56 @@
return true;
}
-bool ResourceTable::SetSymbolState(const ResourceNameRef& name, const ResourceId& res_id,
- const Symbol& symbol, IDiagnostics* diag) {
- return SetSymbolStateImpl(name, res_id, symbol, ValidateName, diag);
+bool ResourceTable::SetVisibility(const ResourceNameRef& name, const Visibility& visibility,
+ IDiagnostics* diag) {
+ return SetVisibilityImpl(name, visibility, ResourceId{}, ResourceNameValidator, diag);
}
-bool ResourceTable::SetSymbolStateAllowMangled(const ResourceNameRef& name,
- const ResourceId& res_id,
- const Symbol& symbol,
- IDiagnostics* diag) {
- return SetSymbolStateImpl(name, res_id, symbol, SkipValidateName, diag);
+bool ResourceTable::SetVisibilityMangled(const ResourceNameRef& name, const Visibility& visibility,
+ IDiagnostics* diag) {
+ return SetVisibilityImpl(name, visibility, ResourceId{}, SkipNameValidator, diag);
}
-bool ResourceTable::SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
- const Symbol& symbol, NameValidator name_validator,
- IDiagnostics* diag) {
+bool ResourceTable::SetVisibilityWithId(const ResourceNameRef& name, const Visibility& visibility,
+ const ResourceId& res_id, IDiagnostics* diag) {
+ return SetVisibilityImpl(name, visibility, res_id, ResourceNameValidator, diag);
+}
+
+bool ResourceTable::SetVisibilityWithIdMangled(const ResourceNameRef& name,
+ const Visibility& visibility,
+ const ResourceId& res_id, IDiagnostics* diag) {
+ return SetVisibilityImpl(name, visibility, res_id, SkipNameValidator, diag);
+}
+
+bool ResourceTable::SetVisibilityImpl(const ResourceNameRef& name, const Visibility& visibility,
+ const ResourceId& res_id, NameValidator name_validator,
+ IDiagnostics* diag) {
CHECK(diag != nullptr);
- const StringPiece bad_char = name_validator(name.entry);
- if (!bad_char.empty()) {
- diag->Error(DiagMessage(symbol.source) << "resource '" << name << "' has invalid entry name '"
- << name.entry << "'. Invalid character '" << bad_char
- << "'");
+ const Source& source = visibility.source;
+ if (!ValidateName(name_validator, name, source, diag)) {
return false;
}
ResourceTablePackage* package = FindOrCreatePackage(name.package);
if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
- diag->Error(DiagMessage(symbol.source)
- << "trying to add resource '" << name << "' with ID " << res_id
- << " but package '" << package->name << "' already has ID "
- << std::hex << (int)package->id.value() << std::dec);
+ diag->Error(DiagMessage(source) << "trying to add resource '" << name << "' with ID " << res_id
+ << " but package '" << package->name << "' already has ID "
+ << StringPrintf("%02x", package->id.value()));
return false;
}
ResourceTableType* type = package->FindOrCreateType(name.type);
if (res_id.is_valid_dynamic() && type->id && type->id.value() != res_id.type_id()) {
- diag->Error(DiagMessage(symbol.source)
- << "trying to add resource '" << name << "' with ID " << res_id
- << " but type '" << type->type << "' already has ID "
- << std::hex << (int)type->id.value() << std::dec);
+ diag->Error(DiagMessage(source)
+ << "trying to add resource '" << name << "' with ID " << res_id << " but type '"
+ << type->type << "' already has ID " << StringPrintf("%02x", type->id.value()));
return false;
}
ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
if (res_id.is_valid_dynamic() && entry->id && entry->id.value() != res_id.entry_id()) {
- diag->Error(DiagMessage(symbol.source)
+ diag->Error(DiagMessage(source)
<< "trying to add resource '" << name << "' with ID " << res_id
<< " but resource already has ID "
<< ResourceId(package->id.value(), type->id.value(), entry->id.value()));
@@ -499,48 +492,96 @@
entry->id = res_id.entry_id();
}
- // Only mark the type state as public, it doesn't care about being private.
- if (symbol.state == SymbolState::kPublic) {
- type->symbol_status.state = SymbolState::kPublic;
+ // Only mark the type visibility level as public, it doesn't care about being private.
+ if (visibility.level == Visibility::Level::kPublic) {
+ type->visibility_level = Visibility::Level::kPublic;
}
- if (symbol.allow_new) {
- // This symbol can be added as a new resource when merging (if it belongs to an overlay).
- entry->symbol_status.allow_new = true;
- }
-
- if (symbol.state == SymbolState::kUndefined &&
- entry->symbol_status.state != SymbolState::kUndefined) {
+ if (visibility.level == Visibility::Level::kUndefined &&
+ entry->visibility.level != Visibility::Level::kUndefined) {
// We can't undefine a symbol (remove its visibility). Ignore.
return true;
}
- if (symbol.state == SymbolState::kPrivate &&
- entry->symbol_status.state == SymbolState::kPublic) {
+ if (visibility.level < entry->visibility.level) {
// We can't downgrade public to private. Ignore.
return true;
}
// This symbol definition takes precedence, replace.
- entry->symbol_status.state = symbol.state;
- entry->symbol_status.source = symbol.source;
- entry->symbol_status.comment = symbol.comment;
+ entry->visibility = visibility;
return true;
}
-Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) {
+bool ResourceTable::SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new,
+ IDiagnostics* diag) {
+ return SetAllowNewImpl(name, allow_new, ResourceNameValidator, diag);
+}
+
+bool ResourceTable::SetAllowNewMangled(const ResourceNameRef& name, const AllowNew& allow_new,
+ IDiagnostics* diag) {
+ return SetAllowNewImpl(name, allow_new, SkipNameValidator, diag);
+}
+
+bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
+ NameValidator name_validator, IDiagnostics* diag) {
+ CHECK(diag != nullptr);
+
+ if (!ValidateName(name_validator, name, allow_new.source, diag)) {
+ return false;
+ }
+
+ ResourceTablePackage* package = FindOrCreatePackage(name.package);
+ ResourceTableType* type = package->FindOrCreateType(name.type);
+ ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
+ entry->allow_new = allow_new;
+ return true;
+}
+
+bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+ IDiagnostics* diag) {
+ return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
+}
+
+bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
+ const Overlayable& overlayable, IDiagnostics* diag) {
+ return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
+}
+
+bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
+ NameValidator name_validator, IDiagnostics* diag) {
+ CHECK(diag != nullptr);
+
+ if (!ValidateName(name_validator, name, overlayable.source, diag)) {
+ return false;
+ }
+
+ ResourceTablePackage* package = FindOrCreatePackage(name.package);
+ ResourceTableType* type = package->FindOrCreateType(name.type);
+ ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
+ if (entry->overlayable) {
+ diag->Error(DiagMessage(overlayable.source)
+ << "duplicate overlayable declaration for resource '" << name << "'");
+ diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
+ return false;
+ }
+ entry->overlayable = overlayable;
+ return true;
+}
+
+Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) const {
ResourceTablePackage* package = FindPackage(name.package);
- if (!package) {
+ if (package == nullptr) {
return {};
}
ResourceTableType* type = package->FindType(name.type);
- if (!type) {
+ if (type == nullptr) {
return {};
}
ResourceEntry* entry = type->FindEntry(name.entry);
- if (!entry) {
+ if (entry == nullptr) {
return {};
}
return SearchResult{package, type, entry};
@@ -552,23 +593,20 @@
ResourceTablePackage* new_pkg = new_table->CreatePackage(pkg->name, pkg->id);
for (const auto& type : pkg->types) {
ResourceTableType* new_type = new_pkg->FindOrCreateType(type->type);
- if (!new_type->id) {
- new_type->id = type->id;
- new_type->symbol_status = type->symbol_status;
- }
+ new_type->id = type->id;
+ new_type->visibility_level = type->visibility_level;
for (const auto& entry : type->entries) {
ResourceEntry* new_entry = new_type->FindOrCreateEntry(entry->name);
- if (!new_entry->id) {
- new_entry->id = entry->id;
- new_entry->symbol_status = entry->symbol_status;
- }
+ new_entry->id = entry->id;
+ new_entry->visibility = entry->visibility;
+ new_entry->allow_new = entry->allow_new;
+ new_entry->overlayable = entry->overlayable;
for (const auto& config_value : entry->values) {
ResourceConfigValue* new_value =
new_entry->FindOrCreateValue(config_value->config, config_value->product);
- Value* value = config_value->value->Clone(&new_table->string_pool);
- new_value->value = std::unique_ptr<Value>(value);
+ new_value->value.reset(config_value->value->Clone(&new_table->string_pool));
}
}
}
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index d5db67e..eaa2d0b 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -38,40 +38,40 @@
namespace aapt {
-enum class SymbolState {
- kUndefined,
- kPrivate,
- kPublic,
+// The Public status of a resource.
+struct Visibility {
+ enum class Level {
+ kUndefined,
+ kPrivate,
+ kPublic,
+ };
+
+ Level level = Level::kUndefined;
+ Source source;
+ std::string comment;
};
-/**
- * The Public status of a resource.
- */
-struct Symbol {
- SymbolState state = SymbolState::kUndefined;
+// Represents <add-resource> in an overlay.
+struct AllowNew {
Source source;
+ std::string comment;
+};
- // Whether this entry (originating from an overlay) can be added as a new resource.
- bool allow_new = false;
-
+// The policy dictating whether an entry is overlayable at runtime by RROs.
+struct Overlayable {
+ Source source;
std::string comment;
};
class ResourceConfigValue {
public:
- /**
- * The configuration for which this value is defined.
- */
+ // The configuration for which this value is defined.
const ConfigDescription config;
- /**
- * The product for which this value is defined.
- */
+ // The product for which this value is defined.
const std::string product;
- /**
- * The actual Value.
- */
+ // The actual Value.
std::unique_ptr<Value> value;
ResourceConfigValue(const ConfigDescription& config, const android::StringPiece& product)
@@ -87,27 +87,21 @@
*/
class ResourceEntry {
public:
- /**
- * The name of the resource. Immutable, as
- * this determines the order of this resource
- * when doing lookups.
- */
+ // The name of the resource. Immutable, as this determines the order of this resource
+ // when doing lookups.
const std::string name;
- /**
- * The entry ID for this resource.
- */
+ // The entry ID for this resource (the EEEE in 0xPPTTEEEE).
Maybe<uint16_t> id;
- /**
- * Whether this resource is public (and must maintain the same entry ID across
- * builds).
- */
- Symbol symbol_status;
+ // Whether this resource is public (and must maintain the same entry ID across builds).
+ Visibility visibility;
- /**
- * The resource's values for each configuration.
- */
+ Maybe<AllowNew> allow_new;
+
+ Maybe<Overlayable> overlayable;
+
+ // The resource's values for each configuration.
std::vector<std::unique_ptr<ResourceConfigValue>> values;
explicit ResourceEntry(const android::StringPiece& name) : name(name.to_string()) {}
@@ -125,31 +119,19 @@
DISALLOW_COPY_AND_ASSIGN(ResourceEntry);
};
-/**
- * Represents a resource type, which holds entries defined
- * for this type.
- */
+// Represents a resource type (eg. string, drawable, layout, etc.) containing resource entries.
class ResourceTableType {
public:
- /**
- * The logical type of resource (string, drawable, layout, etc.).
- */
+ // The logical type of resource (string, drawable, layout, etc.).
const ResourceType type;
- /**
- * The type ID for this resource.
- */
+ // The type ID for this resource (the TT in 0xPPTTEEEE).
Maybe<uint8_t> id;
- /**
- * Whether this type is public (and must maintain the same
- * type ID across builds).
- */
- Symbol symbol_status;
+ // Whether this type is public (and must maintain the same type ID across builds).
+ Visibility::Level visibility_level = Visibility::Level::kUndefined;
- /**
- * List of resources for this type.
- */
+ // List of resources for this type.
std::vector<std::unique_ptr<ResourceEntry>> entries;
explicit ResourceTableType(const ResourceType type) : type(type) {}
@@ -163,9 +145,11 @@
class ResourceTablePackage {
public:
- Maybe<uint8_t> id;
std::string name;
+ // The package ID (the PP in 0xPPTTEEEE).
+ Maybe<uint8_t> id;
+
std::vector<std::unique_ptr<ResourceTableType>> types;
ResourceTablePackage() = default;
@@ -176,10 +160,7 @@
DISALLOW_COPY_AND_ASSIGN(ResourceTablePackage);
};
-/**
- * The container and index for all resources defined for an app. This gets
- * flattened into a binary resource table (resources.arsc).
- */
+// The container and index for all resources defined for an app.
class ResourceTable {
public:
ResourceTable() = default;
@@ -188,47 +169,51 @@
using CollisionResolverFunc = std::function<CollisionResult(Value*, Value*)>;
- /**
- * When a collision of resources occurs, this method decides which value to
- * keep.
- */
+ // When a collision of resources occurs, this method decides which value to keep.
static CollisionResult ResolveValueCollision(Value* existing, Value* incoming);
bool AddResource(const ResourceNameRef& name, const ConfigDescription& config,
const android::StringPiece& product, std::unique_ptr<Value> value,
IDiagnostics* diag);
- bool AddResource(const ResourceNameRef& name, const ResourceId& res_id,
- const ConfigDescription& config, const android::StringPiece& product,
- std::unique_ptr<Value> value, IDiagnostics* diag);
+ bool AddResourceWithId(const ResourceNameRef& name, const ResourceId& res_id,
+ const ConfigDescription& config, const android::StringPiece& product,
+ std::unique_ptr<Value> value, IDiagnostics* diag);
bool AddFileReference(const ResourceNameRef& name, const ConfigDescription& config,
const Source& source, const android::StringPiece& path, IDiagnostics* diag);
- bool AddFileReferenceAllowMangled(const ResourceNameRef& name, const ConfigDescription& config,
- const Source& source, const android::StringPiece& path,
- io::IFile* file, IDiagnostics* diag);
+ bool AddFileReferenceMangled(const ResourceNameRef& name, const ConfigDescription& config,
+ const Source& source, const android::StringPiece& path,
+ io::IFile* file, IDiagnostics* diag);
- /**
- * Same as AddResource, but doesn't verify the validity of the name. This is
- * used
- * when loading resources from an existing binary resource table that may have
- * mangled
- * names.
- */
- bool AddResourceAllowMangled(const ResourceNameRef& name, const ConfigDescription& config,
- const android::StringPiece& product, std::unique_ptr<Value> value,
- IDiagnostics* diag);
+ // Same as AddResource, but doesn't verify the validity of the name. This is used
+ // when loading resources from an existing binary resource table that may have mangled names.
+ bool AddResourceMangled(const ResourceNameRef& name, const ConfigDescription& config,
+ const android::StringPiece& product, std::unique_ptr<Value> value,
+ IDiagnostics* diag);
- bool AddResourceAllowMangled(const ResourceNameRef& name, const ResourceId& id,
- const ConfigDescription& config, const android::StringPiece& product,
- std::unique_ptr<Value> value, IDiagnostics* diag);
+ bool AddResourceWithIdMangled(const ResourceNameRef& name, const ResourceId& id,
+ const ConfigDescription& config,
+ const android::StringPiece& product, std::unique_ptr<Value> value,
+ IDiagnostics* diag);
- bool SetSymbolState(const ResourceNameRef& name, const ResourceId& res_id,
- const Symbol& symbol, IDiagnostics* diag);
+ bool SetVisibility(const ResourceNameRef& name, const Visibility& visibility, IDiagnostics* diag);
+ bool SetVisibilityMangled(const ResourceNameRef& name, const Visibility& visibility,
+ IDiagnostics* diag);
+ bool SetVisibilityWithId(const ResourceNameRef& name, const Visibility& visibility,
+ const ResourceId& res_id, IDiagnostics* diag);
+ bool SetVisibilityWithIdMangled(const ResourceNameRef& name, const Visibility& visibility,
+ const ResourceId& res_id, IDiagnostics* diag);
- bool SetSymbolStateAllowMangled(const ResourceNameRef& name, const ResourceId& res_id,
- const Symbol& symbol, IDiagnostics* diag);
+ bool SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+ IDiagnostics* diag);
+ bool SetOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable,
+ IDiagnostics* diag);
+
+ bool SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new, IDiagnostics* diag);
+ bool SetAllowNewMangled(const ResourceNameRef& name, const AllowNew& allow_new,
+ IDiagnostics* diag);
struct SearchResult {
ResourceTablePackage* package;
@@ -236,40 +221,28 @@
ResourceEntry* entry;
};
- Maybe<SearchResult> FindResource(const ResourceNameRef& name);
+ Maybe<SearchResult> FindResource(const ResourceNameRef& name) const;
- /**
- * Returns the package struct with the given name, or nullptr if such a
- * package does not
- * exist. The empty string is a valid package and typically is used to
- * represent the
- * 'current' package before it is known to the ResourceTable.
- */
- ResourceTablePackage* FindPackage(const android::StringPiece& name);
+ // Returns the package struct with the given name, or nullptr if such a package does not
+ // exist. The empty string is a valid package and typically is used to represent the
+ // 'current' package before it is known to the ResourceTable.
+ ResourceTablePackage* FindPackage(const android::StringPiece& name) const;
- ResourceTablePackage* FindPackageById(uint8_t id);
+ ResourceTablePackage* FindPackageById(uint8_t id) const;
ResourceTablePackage* CreatePackage(const android::StringPiece& name, Maybe<uint8_t> id = {});
std::unique_ptr<ResourceTable> Clone() const;
- /**
- * The string pool used by this resource table. Values that reference strings
- * must use
- * this pool to create their strings.
- *
- * NOTE: `string_pool` must come before `packages` so that it is destroyed
- * after.
- * When `string_pool` references are destroyed (as they will be when
- * `packages`
- * is destroyed), they decrement a refCount, which would cause invalid
- * memory access if the pool was already destroyed.
- */
+ // The string pool used by this resource table. Values that reference strings must use
+ // this pool to create their strings.
+ // NOTE: `string_pool` must come before `packages` so that it is destroyed after.
+ // When `string_pool` references are destroyed (as they will be when `packages` is destroyed),
+ // they decrement a refCount, which would cause invalid memory access if the pool was already
+ // destroyed.
StringPool string_pool;
- /**
- * The list of packages in this table, sorted alphabetically by package name.
- */
+ // The list of packages in this table, sorted alphabetically by package name.
std::vector<std::unique_ptr<ResourceTablePackage>> packages;
// Set of dynamic packages that this table may reference. Their package names get encoded
@@ -284,6 +257,9 @@
ResourceTablePackage* FindOrCreatePackage(const android::StringPiece& name);
+ bool ValidateName(NameValidator validator, const ResourceNameRef& name, const Source& source,
+ IDiagnostics* diag);
+
bool AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
const ConfigDescription& config, const android::StringPiece& product,
std::unique_ptr<Value> value, NameValidator name_validator,
@@ -293,8 +269,19 @@
const Source& source, const android::StringPiece& path, io::IFile* file,
NameValidator name_validator, IDiagnostics* diag);
+ bool SetVisibilityImpl(const ResourceNameRef& name, const Visibility& visibility,
+ const ResourceId& res_id, NameValidator name_validator,
+ IDiagnostics* diag);
+
+ bool SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
+ NameValidator name_validator, IDiagnostics* diag);
+
+ bool SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
+ NameValidator name_validator, IDiagnostics* diag);
+
bool SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
- const Symbol& symbol, NameValidator name_validator, IDiagnostics* diag);
+ const Visibility& symbol, NameValidator name_validator,
+ IDiagnostics* diag);
DISALLOW_COPY_AND_ASSIGN(ResourceTable);
};
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 2a3c131..eb75f94 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -24,7 +24,10 @@
#include <ostream>
#include <string>
+using ::android::StringPiece;
+using ::testing::Eq;
using ::testing::NotNull;
+using ::testing::StrEq;
namespace aapt {
@@ -45,7 +48,7 @@
TEST(ResourceTableTest, AddResourceWithWeirdNameWhenAddingMangledResources) {
ResourceTable table;
- EXPECT_TRUE(table.AddResourceAllowMangled(
+ EXPECT_TRUE(table.AddResourceMangled(
test::ParseNameOrDie("android:id/heythere "), ConfigDescription{}, "",
test::ValueBuilder<Id>().SetSource("test.xml", 21u).Build(), test::GetDiagnostics()));
}
@@ -141,4 +144,104 @@
EXPECT_EQ(std::string("tablet"), values[1]->product);
}
+static StringPiece LevelToString(Visibility::Level level) {
+ switch (level) {
+ case Visibility::Level::kPrivate:
+ return "private";
+ case Visibility::Level::kPublic:
+ return "private";
+ default:
+ return "undefined";
+ }
+}
+
+static ::testing::AssertionResult VisibilityOfResource(const ResourceTable& table,
+ const ResourceNameRef& name,
+ Visibility::Level level,
+ const StringPiece& comment) {
+ Maybe<ResourceTable::SearchResult> result = table.FindResource(name);
+ if (!result) {
+ return ::testing::AssertionFailure() << "no resource '" << name << "' found in table";
+ }
+
+ const Visibility& visibility = result.value().entry->visibility;
+ if (visibility.level != level) {
+ return ::testing::AssertionFailure() << "expected visibility " << LevelToString(level)
+ << " but got " << LevelToString(visibility.level);
+ }
+
+ if (visibility.comment != comment) {
+ return ::testing::AssertionFailure() << "expected visibility comment '" << comment
+ << "' but got '" << visibility.comment << "'";
+ }
+ return ::testing::AssertionSuccess();
+}
+
+TEST(ResourceTableTest, SetVisibility) {
+ using Level = Visibility::Level;
+
+ ResourceTable table;
+ const ResourceName name = test::ParseNameOrDie("android:string/foo");
+
+ Visibility visibility;
+ visibility.level = Visibility::Level::kPrivate;
+ visibility.comment = "private";
+ ASSERT_TRUE(table.SetVisibility(name, visibility, test::GetDiagnostics()));
+ ASSERT_TRUE(VisibilityOfResource(table, name, Level::kPrivate, "private"));
+
+ visibility.level = Visibility::Level::kUndefined;
+ visibility.comment = "undefined";
+ ASSERT_TRUE(table.SetVisibility(name, visibility, test::GetDiagnostics()));
+ ASSERT_TRUE(VisibilityOfResource(table, name, Level::kPrivate, "private"));
+
+ visibility.level = Visibility::Level::kPublic;
+ visibility.comment = "public";
+ ASSERT_TRUE(table.SetVisibility(name, visibility, test::GetDiagnostics()));
+ ASSERT_TRUE(VisibilityOfResource(table, name, Level::kPublic, "public"));
+
+ visibility.level = Visibility::Level::kPrivate;
+ visibility.comment = "private";
+ ASSERT_TRUE(table.SetVisibility(name, visibility, test::GetDiagnostics()));
+ ASSERT_TRUE(VisibilityOfResource(table, name, Level::kPublic, "public"));
+}
+
+TEST(ResourceTableTest, SetAllowNew) {
+ ResourceTable table;
+ const ResourceName name = test::ParseNameOrDie("android:string/foo");
+
+ AllowNew allow_new;
+ Maybe<ResourceTable::SearchResult> result;
+
+ allow_new.comment = "first";
+ ASSERT_TRUE(table.SetAllowNew(name, allow_new, test::GetDiagnostics()));
+ result = table.FindResource(name);
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.value().entry->allow_new);
+ ASSERT_THAT(result.value().entry->allow_new.value().comment, StrEq("first"));
+
+ allow_new.comment = "second";
+ ASSERT_TRUE(table.SetAllowNew(name, allow_new, test::GetDiagnostics()));
+ result = table.FindResource(name);
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.value().entry->allow_new);
+ ASSERT_THAT(result.value().entry->allow_new.value().comment, StrEq("second"));
+}
+
+TEST(ResourceTableTest, SetOverlayable) {
+ ResourceTable table;
+ const ResourceName name = test::ParseNameOrDie("android:string/foo");
+
+ Overlayable overlayable;
+
+ overlayable.comment = "first";
+ ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+ Maybe<ResourceTable::SearchResult> result = table.FindResource(name);
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.value().entry->overlayable);
+ ASSERT_THAT(result.value().entry->overlayable.value().comment, StrEq("first"));
+
+ overlayable.comment = "second";
+ ASSERT_FALSE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 7e7c86d..8552195 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -93,11 +93,10 @@
repeated Entry entry = 3;
}
-// The status of a symbol/entry. This contains information like visibility (public/private),
-// comments, and whether the entry can be overridden.
-message SymbolStatus {
+// The Visibility of a symbol/entry (public, private, undefined).
+message Visibility {
// The visibility of the resource outside of its package.
- enum Visibility {
+ enum Level {
// No visibility was explicitly specified. This is typically treated as private.
// The distinction is important when two separate R.java files are generated: a public and
// private one. An unknown visibility, in this case, would cause the resource to be omitted
@@ -115,17 +114,32 @@
PUBLIC = 2;
}
- Visibility visibility = 1;
+ Level level = 1;
// The path at which this entry's visibility was defined (eg. public.xml).
Source source = 2;
// The comment associated with the <public> tag.
string comment = 3;
+}
- // Whether the symbol can be merged into another resource table without there being an existing
- // definition to override. Used for overlays and set to true when <add-resource> is specified.
- bool allow_new = 4;
+// Whether a resource comes from a compile-time overlay and is explicitly allowed to not overlay an
+// existing resource.
+message AllowNew {
+ // Where this was defined in source.
+ Source source = 1;
+
+ // Any comment associated with the declaration.
+ string comment = 2;
+}
+
+// Whether a resource is overlayable by runtime resource overlays (RRO).
+message Overlayable {
+ // Where this declaration was defined in source.
+ Source source = 1;
+
+ // Any comment associated with the declaration.
+ string comment = 2;
}
// An entry ID in the range [0x0000, 0xffff].
@@ -147,12 +161,19 @@
// form package:type/entry.
string name = 2;
- // The symbol status of this entry, which includes visibility information.
- SymbolStatus symbol_status = 3;
+ // The visibility of this entry (public, private, undefined).
+ Visibility visibility = 3;
+
+ // Whether this resource, when originating from a compile-time overlay, is allowed to NOT overlay
+ // any existing resources.
+ AllowNew allow_new = 4;
+
+ // Whether this resource can be overlaid by a runtime resource overlay (RRO).
+ Overlayable overlayable = 5;
// The set of values defined for this entry, each corresponding to a different
// configuration/variant.
- repeated ConfigValue config_value = 4;
+ repeated ConfigValue config_value = 6;
}
// A Configuration/Value pair.
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 83512b9..7c1e96e 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -49,6 +49,7 @@
#include "xml/XmlPullParser.h"
using ::aapt::io::FileInputStream;
+using ::aapt::text::Printer;
using ::android::StringPiece;
using ::android::base::SystemErrorCodeToString;
using ::google::protobuf::io::CopyingOutputStreamAdaptor;
@@ -112,6 +113,7 @@
struct CompileOptions {
std::string output_path;
Maybe<std::string> res_dir;
+ Maybe<std::string> generate_text_symbols_path;
bool pseudolocalize = false;
bool no_png_crunch = false;
bool legacy_mode = false;
@@ -261,6 +263,58 @@
context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to finish entry");
return false;
}
+
+ if (options.generate_text_symbols_path) {
+ io::FileOutputStream fout_text(options.generate_text_symbols_path.value());
+
+ if (fout_text.HadError()) {
+ context->GetDiagnostics()->Error(DiagMessage()
+ << "failed writing to'"
+ << options.generate_text_symbols_path.value()
+ << "': " << fout_text.GetError());
+ return false;
+ }
+
+ Printer r_txt_printer(&fout_text);
+ for (const auto& package : table.packages) {
+ for (const auto& type : package->types) {
+ for (const auto& entry : type->entries) {
+ // Check access modifiers.
+ switch(entry->visibility.level) {
+ case Visibility::Level::kUndefined :
+ r_txt_printer.Print("default ");
+ break;
+ case Visibility::Level::kPublic :
+ r_txt_printer.Print("public ");
+ break;
+ case Visibility::Level::kPrivate :
+ r_txt_printer.Print("private ");
+ }
+
+ if (type->type != ResourceType::kStyleable) {
+ r_txt_printer.Print("int ");
+ r_txt_printer.Print(to_string(type->type));
+ r_txt_printer.Print(" ");
+ r_txt_printer.Println(entry->name);
+ } else {
+ r_txt_printer.Print("int[] styleable ");
+ r_txt_printer.Println(entry->name);
+
+ if (!entry->values.empty()) {
+ auto styleable = static_cast<const Styleable*>(entry->values.front()->value.get());
+ for (const auto& attr : styleable->entries) {
+ r_txt_printer.Print("default int styleable ");
+ r_txt_printer.Print(entry->name);
+ r_txt_printer.Print("_");
+ r_txt_printer.Println(attr.name.value().entry);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
return true;
}
@@ -402,6 +456,31 @@
context->GetDiagnostics()->Error(DiagMessage(output_path) << "failed to finish writing data");
return false;
}
+
+ if (options.generate_text_symbols_path) {
+ io::FileOutputStream fout_text(options.generate_text_symbols_path.value());
+
+ if (fout_text.HadError()) {
+ context->GetDiagnostics()->Error(DiagMessage()
+ << "failed writing to'"
+ << options.generate_text_symbols_path.value()
+ << "': " << fout_text.GetError());
+ return false;
+ }
+
+ Printer r_txt_printer(&fout_text);
+ for (const auto res : xmlres->file.exported_symbols) {
+ r_txt_printer.Print("default int id ");
+ r_txt_printer.Println(res.name.entry);
+ }
+
+ // And print ourselves.
+ r_txt_printer.Print("default int ");
+ r_txt_printer.Print(path_data.resource_dir);
+ r_txt_printer.Print(" ");
+ r_txt_printer.Println(path_data.name);
+ }
+
return true;
}
@@ -609,6 +688,10 @@
Flags()
.RequiredFlag("-o", "Output path", &options.output_path)
.OptionalFlag("--dir", "Directory to scan for resources", &options.res_dir)
+ .OptionalFlag("--output-text-symbols",
+ "Generates a text file containing the resource symbols in the\n"
+ "specified file",
+ &options.generate_text_symbols_path)
.OptionalSwitch("--pseudo-localize",
"Generate resources for pseudo-locales "
"(en-XA and ar-XB)",
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index fc1f1d6..12113ed 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -75,14 +75,14 @@
std::cerr << source << ": " << message << "\n";
}
-static bool IsSymbolVisibilityDifferent(const Symbol& symbol_a, const Symbol& symbol_b) {
- return symbol_a.state != symbol_b.state;
+static bool IsSymbolVisibilityDifferent(const Visibility& vis_a, const Visibility& vis_b) {
+ return vis_a.level != vis_b.level;
}
template <typename Id>
-static bool IsIdDiff(const Symbol& symbol_a, const Maybe<Id>& id_a, const Symbol& symbol_b,
- const Maybe<Id>& id_b) {
- if (symbol_a.state == SymbolState::kPublic || symbol_b.state == SymbolState::kPublic) {
+static bool IsIdDiff(const Visibility::Level& level_a, const Maybe<Id>& id_a,
+ const Visibility::Level& level_b, const Maybe<Id>& id_b) {
+ if (level_a == Visibility::Level::kPublic || level_b == Visibility::Level::kPublic) {
return id_a != id_b;
}
return false;
@@ -157,17 +157,17 @@
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
} else {
- if (IsSymbolVisibilityDifferent(entry_a->symbol_status, entry_b->symbol_status)) {
+ if (IsSymbolVisibilityDifferent(entry_a->visibility, entry_b->visibility)) {
std::stringstream str_stream;
str_stream << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
<< " has different visibility (";
- if (entry_b->symbol_status.state == SymbolState::kPublic) {
+ if (entry_b->visibility.level == Visibility::Level::kPublic) {
str_stream << "PUBLIC";
} else {
str_stream << "PRIVATE";
}
str_stream << " vs ";
- if (entry_a->symbol_status.state == SymbolState::kPublic) {
+ if (entry_a->visibility.level == Visibility::Level::kPublic) {
str_stream << "PUBLIC";
} else {
str_stream << "PRIVATE";
@@ -175,7 +175,7 @@
str_stream << ")";
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
- } else if (IsIdDiff(entry_a->symbol_status, entry_a->id, entry_b->symbol_status,
+ } else if (IsIdDiff(entry_a->visibility.level, entry_a->id, entry_b->visibility.level,
entry_b->id)) {
std::stringstream str_stream;
str_stream << pkg_a->name << ":" << type_a->type << "/" << entry_a->name
@@ -225,16 +225,16 @@
EmitDiffLine(apk_a->GetSource(), str_stream.str());
diff = true;
} else {
- if (IsSymbolVisibilityDifferent(type_a->symbol_status, type_b->symbol_status)) {
+ if (type_a->visibility_level != type_b->visibility_level) {
std::stringstream str_stream;
str_stream << pkg_a->name << ":" << type_a->type << " has different visibility (";
- if (type_b->symbol_status.state == SymbolState::kPublic) {
+ if (type_b->visibility_level == Visibility::Level::kPublic) {
str_stream << "PUBLIC";
} else {
str_stream << "PRIVATE";
}
str_stream << " vs ";
- if (type_a->symbol_status.state == SymbolState::kPublic) {
+ if (type_a->visibility_level == Visibility::Level::kPublic) {
str_stream << "PUBLIC";
} else {
str_stream << "PRIVATE";
@@ -242,7 +242,8 @@
str_stream << ")";
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
- } else if (IsIdDiff(type_a->symbol_status, type_a->id, type_b->symbol_status, type_b->id)) {
+ } else if (IsIdDiff(type_a->visibility_level, type_a->id, type_b->visibility_level,
+ type_b->id)) {
std::stringstream str_stream;
str_stream << pkg_a->name << ":" << type_a->type << " has different public ID (";
if (type_b->id) {
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index bc7f5a8..3d2fb55 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -69,15 +69,13 @@
printer->Println(StringPrintf("Data: offset=%" PRIi64 " length=%zd", offset, len));
}
-static bool TryDumpFile(IAaptContext* context, const std::string& file_path) {
+static bool TryDumpFile(IAaptContext* context, const std::string& file_path,
+ const DebugPrintTableOptions& print_options) {
// Use a smaller buffer so that there is less latency for dumping to stdout.
constexpr size_t kStdOutBufferSize = 1024u;
io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
Printer printer(&fout);
- DebugPrintTableOptions print_options;
- print_options.show_sources = true;
-
std::string err;
std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(file_path, &err);
if (zip) {
@@ -244,7 +242,12 @@
// Entry point for dump command.
int Dump(const std::vector<StringPiece>& args) {
bool verbose = false;
- Flags flags = Flags().OptionalSwitch("-v", "increase verbosity of output", &verbose);
+ bool no_values = false;
+ Flags flags = Flags()
+ .OptionalSwitch("--no-values",
+ "Suppresses output of values when displaying resource tables.",
+ &no_values)
+ .OptionalSwitch("-v", "increase verbosity of output", &verbose);
if (!flags.Parse("aapt2 dump", args, &std::cerr)) {
return 1;
}
@@ -252,8 +255,11 @@
DumpContext context;
context.SetVerbose(verbose);
+ DebugPrintTableOptions dump_table_options;
+ dump_table_options.show_sources = true;
+ dump_table_options.show_values = !no_values;
for (const std::string& arg : flags.GetArgs()) {
- if (!TryDumpFile(&context, arg)) {
+ if (!TryDumpFile(&context, arg, dump_table_options)) {
return 1;
}
}
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 13dd93e..72e07dc 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -631,9 +631,9 @@
dst_path =
ResourceUtils::BuildResourceFileName(doc->file, context_->GetNameMangler());
- bool result = table->AddFileReferenceAllowMangled(doc->file.name, doc->file.config,
- doc->file.source, dst_path, nullptr,
- context_->GetDiagnostics());
+ bool result =
+ table->AddFileReferenceMangled(doc->file.name, doc->file.config, doc->file.source,
+ dst_path, nullptr, context_->GetDiagnostics());
if (!result) {
return false;
}
@@ -1069,25 +1069,29 @@
bool WriteJavaFile(ResourceTable* table, const StringPiece& package_name_to_generate,
const StringPiece& out_package, const JavaClassGeneratorOptions& java_options,
const Maybe<std::string>& out_text_symbols_path = {}) {
- if (!options_.generate_java_class_path) {
+ if (!options_.generate_java_class_path && !out_text_symbols_path) {
return true;
}
- std::string out_path = options_.generate_java_class_path.value();
- file::AppendPath(&out_path, file::PackageToPath(out_package));
- if (!file::mkdirs(out_path)) {
- context_->GetDiagnostics()->Error(DiagMessage() << "failed to create directory '" << out_path
- << "'");
- return false;
- }
+ std::string out_path;
+ std::unique_ptr<io::FileOutputStream> fout;
+ if (options_.generate_java_class_path) {
+ out_path = options_.generate_java_class_path.value();
+ file::AppendPath(&out_path, file::PackageToPath(out_package));
+ if (!file::mkdirs(out_path)) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed to create directory '" << out_path << "'");
+ return false;
+ }
- file::AppendPath(&out_path, "R.java");
+ file::AppendPath(&out_path, "R.java");
- io::FileOutputStream fout(out_path);
- if (fout.HadError()) {
- context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
- << "': " << fout.GetError());
- return false;
+ fout = util::make_unique<io::FileOutputStream>(out_path);
+ if (fout->HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
+ << "': " << fout->GetError());
+ return false;
+ }
}
std::unique_ptr<io::FileOutputStream> fout_text;
@@ -1102,18 +1106,11 @@
}
JavaClassGenerator generator(context_, table, java_options);
- if (!generator.Generate(package_name_to_generate, out_package, &fout, fout_text.get())) {
+ if (!generator.Generate(package_name_to_generate, out_package, fout.get(), fout_text.get())) {
context_->GetDiagnostics()->Error(DiagMessage(out_path) << generator.GetError());
return false;
}
- fout.Flush();
-
- if (fout.HadError()) {
- context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
- << "': " << fout.GetError());
- return false;
- }
return true;
}
@@ -1346,9 +1343,9 @@
std::unique_ptr<Id> id = util::make_unique<Id>();
id->SetSource(source.WithLine(exported_symbol.line));
- bool result = final_table_.AddResourceAllowMangled(
- res_name, ConfigDescription::DefaultConfig(), std::string(), std::move(id),
- context_->GetDiagnostics());
+ bool result =
+ final_table_.AddResourceMangled(res_name, ConfigDescription::DefaultConfig(),
+ std::string(), std::move(id), context_->GetDiagnostics());
if (!result) {
return false;
}
@@ -1934,7 +1931,7 @@
return 1;
}
- if (options_.generate_java_class_path) {
+ if (options_.generate_java_class_path || options_.generate_text_symbols_path) {
if (!GenerateJavaClasses()) {
return 1;
}
@@ -2124,6 +2121,9 @@
&options.manifest_fixer_options.rename_instrumentation_target_package)
.OptionalFlagList("-0", "File extensions not to compress.",
&options.extensions_to_not_compress)
+ .OptionalSwitch("--warn-manifest-validation",
+ "Treat manifest validation errors as warnings.",
+ &options.manifest_fixer_options.warn_validation)
.OptionalFlagList("--split",
"Split resources matching a set of configs out to a Split APK.\n"
"Syntax: path/to/output.apk:<config>[,<config>[...]].\n"
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 1bdb762..9c76119 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -44,8 +44,7 @@
#include "util/Util.h"
using ::aapt::configuration::Abi;
-using ::aapt::configuration::Artifact;
-using ::aapt::configuration::PostProcessingConfiguration;
+using ::aapt::configuration::OutputArtifact;
using ::android::ResTable_config;
using ::android::StringPiece;
using ::android::base::ReadFileToString;
@@ -74,7 +73,7 @@
TableFlattenerOptions table_flattener_options;
- Maybe<PostProcessingConfiguration> configuration;
+ Maybe<std::vector<OutputArtifact>> apk_artifacts;
// Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts
// are kept and will be written as output.
@@ -199,14 +198,11 @@
++split_constraints_iter;
}
- if (options_.configuration && options_.output_dir) {
+ if (options_.apk_artifacts && options_.output_dir) {
MultiApkGenerator generator{apk.get(), context_};
MultiApkGeneratorOptions generator_options = {
- options_.output_dir.value(),
- options_.configuration.value(),
- options_.table_flattener_options,
- options_.kept_artifacts,
- };
+ options_.output_dir.value(), options_.apk_artifacts.value(),
+ options_.table_flattener_options, options_.kept_artifacts};
if (!generator.FromBaseApk(generator_options)) {
return 1;
}
@@ -327,7 +323,6 @@
Maybe<std::string> config_path;
Maybe<std::string> whitelist_path;
Maybe<std::string> target_densities;
- Maybe<std::string> target_abis;
std::vector<std::string> configs;
std::vector<std::string> split_args;
std::unordered_set<std::string> kept_artifacts;
@@ -349,12 +344,6 @@
"Path to the whitelist.cfg file containing whitelisted resources \n"
"whose names should not be altered in final resource tables.",
&whitelist_path)
- .OptionalFlag(
- "--target-abis",
- "Comma separated list of the CPU ABIs that the APK will be optimized for.\n"
- "All the native libraries that would be unused on devices of the given ABIs will \n"
- "be removed from the APK.",
- &target_abis)
.OptionalFlagList("-c",
"Comma separated list of configurations to include. The default\n"
"is all configurations.",
@@ -388,14 +377,56 @@
}
const std::string& apk_path = flags.GetArgs()[0];
+
+ context.SetVerbose(verbose);
+ IDiagnostics* diag = context.GetDiagnostics();
+
+ if (config_path) {
+ std::string& path = config_path.value();
+ Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
+ if (for_path) {
+ options.apk_artifacts = for_path.value().WithDiagnostics(diag).Parse(apk_path);
+ if (!options.apk_artifacts) {
+ diag->Error(DiagMessage() << "Failed to parse the output artifact list");
+ return 1;
+ }
+
+ } else {
+ diag->Error(DiagMessage() << "Could not parse config file " << path);
+ return 1;
+ }
+
+ if (print_only) {
+ for (const OutputArtifact& artifact : options.apk_artifacts.value()) {
+ std::cout << artifact.name << std::endl;
+ }
+ return 0;
+ }
+
+ if (!kept_artifacts.empty()) {
+ for (const std::string& artifact_str : kept_artifacts) {
+ for (const StringPiece& artifact : util::Tokenize(artifact_str, ',')) {
+ options.kept_artifacts.insert(artifact.to_string());
+ }
+ }
+ }
+
+ // Since we know that we are going to process the APK (not just print targets), make sure we
+ // have somewhere to write them to.
+ if (!options.output_dir) {
+ diag->Error(DiagMessage() << "Output directory is required when using a configuration file");
+ return 1;
+ }
+ } else if (print_only) {
+ diag->Error(DiagMessage() << "Asked to print artifacts without providing a configurations");
+ return 1;
+ }
+
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(apk_path, context.GetDiagnostics());
if (!apk) {
return 1;
}
- context.SetVerbose(verbose);
- IDiagnostics* diag = context.GetDiagnostics();
-
if (target_densities) {
// Parse the target screen densities.
for (const StringPiece& config_str : util::Tokenize(target_densities.value(), ',')) {
@@ -426,49 +457,6 @@
}
}
- if (config_path) {
- std::string& path = config_path.value();
- Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
- if (for_path) {
- options.configuration = for_path.value().WithDiagnostics(diag).Parse();
- } else {
- diag->Error(DiagMessage() << "Could not parse config file " << path);
- return 1;
- }
-
- if (print_only) {
- std::vector<std::string> names;
- const PostProcessingConfiguration& config = options.configuration.value();
- if (!config.AllArtifactNames(file::GetFilename(apk_path), &names, diag)) {
- diag->Error(DiagMessage() << "Failed to generate output artifact list");
- return 1;
- }
-
- for (const auto& name : names) {
- std::cout << name << std::endl;
- }
- return 0;
- }
-
- if (!kept_artifacts.empty()) {
- for (const auto& artifact_str : kept_artifacts) {
- for (const auto& artifact : util::Tokenize(artifact_str, ',')) {
- options.kept_artifacts.insert(artifact.to_string());
- }
- }
- }
-
- // Since we know that we are going to process the APK (not just print targets), make sure we
- // have somewhere to write them to.
- if (!options.output_dir) {
- diag->Error(DiagMessage() << "Output directory is required when using a configuration file");
- return 1;
- }
- } else if (print_only) {
- diag->Error(DiagMessage() << "Asked to print artifacts without providing a configurations");
- return 1;
- }
-
if (options.table_flattener_options.collapse_key_stringpool) {
if (whitelist_path) {
std::string& path = whitelist_path.value();
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index b99240f..eabeb47 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -28,6 +28,7 @@
#include "ConfigDescription.h"
#include "Diagnostics.h"
#include "ResourceUtils.h"
+#include "configuration/ConfigurationParser.internal.h"
#include "io/File.h"
#include "io/FileSystem.h"
#include "io/StringStream.h"
@@ -45,11 +46,24 @@
using ::aapt::configuration::Abi;
using ::aapt::configuration::AndroidManifest;
using ::aapt::configuration::AndroidSdk;
-using ::aapt::configuration::Artifact;
-using ::aapt::configuration::PostProcessingConfiguration;
+using ::aapt::configuration::ConfiguredArtifact;
+using ::aapt::configuration::DeviceFeature;
+using ::aapt::configuration::Entry;
+using ::aapt::configuration::ExtractConfiguration;
using ::aapt::configuration::GlTexture;
using ::aapt::configuration::Group;
using ::aapt::configuration::Locale;
+using ::aapt::configuration::OrderedEntry;
+using ::aapt::configuration::OutputArtifact;
+using ::aapt::configuration::PostProcessingConfiguration;
+using ::aapt::configuration::handler::AbiGroupTagHandler;
+using ::aapt::configuration::handler::AndroidSdkTagHandler;
+using ::aapt::configuration::handler::ArtifactFormatTagHandler;
+using ::aapt::configuration::handler::ArtifactTagHandler;
+using ::aapt::configuration::handler::DeviceFeatureGroupTagHandler;
+using ::aapt::configuration::handler::GlTextureGroupTagHandler;
+using ::aapt::configuration::handler::LocaleGroupTagHandler;
+using ::aapt::configuration::handler::ScreenDensityGroupTagHandler;
using ::aapt::io::IFile;
using ::aapt::io::RegularFile;
using ::aapt::io::StringInputStream;
@@ -59,19 +73,16 @@
using ::aapt::xml::XmlActionExecutor;
using ::aapt::xml::XmlActionExecutorPolicy;
using ::aapt::xml::XmlNodeAction;
-using ::android::base::ReadFileToString;
using ::android::StringPiece;
+using ::android::base::ReadFileToString;
-const std::unordered_map<std::string, Abi> kStringToAbiMap = {
+const std::unordered_map<StringPiece, Abi> kStringToAbiMap = {
{"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a}, {"arm64-v8a", Abi::kArm64V8a},
{"x86", Abi::kX86}, {"x86_64", Abi::kX86_64}, {"mips", Abi::kMips},
{"mips64", Abi::kMips64}, {"universal", Abi::kUniversal},
};
-const std::map<Abi, std::string> kAbiToStringMap = {
- {Abi::kArmeV6, "armeabi"}, {Abi::kArmV7a, "armeabi-v7a"}, {Abi::kArm64V8a, "arm64-v8a"},
- {Abi::kX86, "x86"}, {Abi::kX86_64, "x86_64"}, {Abi::kMips, "mips"},
- {Abi::kMips64, "mips64"}, {Abi::kUniversal, "universal"},
-};
+const std::array<StringPiece, 8> kAbiToStringMap = {
+ {"armeabi", "armeabi-v7a", "arm64-v8a", "x86", "x86_64", "mips", "mips64", "universal"}};
constexpr const char* kAaptXmlNs = "http://schemas.android.com/tools/aapt";
@@ -106,12 +117,25 @@
}
};
-} // namespace
+/** Copies the values referenced in a configuration group to the target list. */
+template <typename T>
+bool CopyXmlReferences(const Maybe<std::string>& name, const Group<T>& groups,
+ std::vector<T>* target) {
+ // If there was no item configured, there is nothing to do and no error.
+ if (!name) {
+ return true;
+ }
-namespace configuration {
+ // If the group could not be found, then something is wrong.
+ auto group = groups.find(name.value());
+ if (group == groups.end()) {
+ return false;
+ }
-const std::string& AbiToString(Abi abi) {
- return kAbiToStringMap.find(abi)->second;
+ for (const T& item : group->second.entry) {
+ target->push_back(item);
+ }
+ return true;
}
/**
@@ -119,8 +143,8 @@
* success, or false if the either the placeholder is not found in the name, or the value is not
* present and the placeholder was.
*/
-static bool ReplacePlaceholder(const StringPiece& placeholder, const Maybe<StringPiece>& value,
- std::string* name, IDiagnostics* diag) {
+bool ReplacePlaceholder(const StringPiece& placeholder, const Maybe<StringPiece>& value,
+ std::string* name, IDiagnostics* diag) {
size_t offset = name->find(placeholder.data());
bool found = (offset != std::string::npos);
@@ -152,6 +176,163 @@
}
/**
+ * An ActionHandler for processing XML elements in the XmlActionExecutor. Returns true if the
+ * element was successfully processed, otherwise returns false.
+ */
+using ActionHandler = std::function<bool(configuration::PostProcessingConfiguration* config,
+ xml::Element* element, IDiagnostics* diag)>;
+
+/** Binds an ActionHandler to the current configuration being populated. */
+xml::XmlNodeAction::ActionFuncWithDiag Bind(configuration::PostProcessingConfiguration* config,
+ const ActionHandler& handler) {
+ return [config, handler](xml::Element* root_element, SourcePathDiagnostics* diag) {
+ return handler(config, root_element, diag);
+ };
+}
+
+/** Converts a ConfiguredArtifact into an OutputArtifact. */
+Maybe<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,
+ const std::string& apk_name,
+ const PostProcessingConfiguration& config,
+ IDiagnostics* diag) {
+ if (!artifact.name && !config.artifact_format) {
+ diag->Error(
+ DiagMessage() << "Artifact does not have a name and no global name template defined");
+ return {};
+ }
+
+ Maybe<std::string> artifact_name =
+ (artifact.name) ? artifact.Name(apk_name, diag)
+ : artifact.ToArtifactName(config.artifact_format.value(), apk_name, diag);
+
+ if (!artifact_name) {
+ diag->Error(DiagMessage() << "Could not determine split APK artifact name");
+ return {};
+ }
+
+ OutputArtifact output_artifact;
+ output_artifact.name = artifact_name.value();
+
+ SourcePathDiagnostics src_diag{{output_artifact.name}, diag};
+ bool has_errors = false;
+
+ if (!CopyXmlReferences(artifact.abi_group, config.abi_groups, &output_artifact.abis)) {
+ src_diag.Error(DiagMessage() << "Could not lookup required ABIs: "
+ << artifact.abi_group.value());
+ has_errors = true;
+ }
+
+ if (!CopyXmlReferences(artifact.locale_group, config.locale_groups, &output_artifact.locales)) {
+ src_diag.Error(DiagMessage() << "Could not lookup required locales: "
+ << artifact.locale_group.value());
+ has_errors = true;
+ }
+
+ if (!CopyXmlReferences(artifact.screen_density_group, config.screen_density_groups,
+ &output_artifact.screen_densities)) {
+ src_diag.Error(DiagMessage() << "Could not lookup required screen densities: "
+ << artifact.screen_density_group.value());
+ has_errors = true;
+ }
+
+ if (!CopyXmlReferences(artifact.device_feature_group, config.device_feature_groups,
+ &output_artifact.features)) {
+ src_diag.Error(DiagMessage() << "Could not lookup required device features: "
+ << artifact.device_feature_group.value());
+ has_errors = true;
+ }
+
+ if (!CopyXmlReferences(artifact.gl_texture_group, config.gl_texture_groups,
+ &output_artifact.textures)) {
+ src_diag.Error(DiagMessage() << "Could not lookup required OpenGL texture formats: "
+ << artifact.gl_texture_group.value());
+ has_errors = true;
+ }
+
+ if (artifact.android_sdk) {
+ auto entry = config.android_sdks.find(artifact.android_sdk.value());
+ if (entry == config.android_sdks.end()) {
+ src_diag.Error(DiagMessage() << "Could not lookup required Android SDK version: "
+ << artifact.android_sdk.value());
+ has_errors = true;
+ } else {
+ output_artifact.android_sdk = {entry->second};
+ }
+ }
+
+ if (has_errors) {
+ return {};
+ }
+ return {output_artifact};
+}
+
+} // namespace
+
+namespace configuration {
+
+/** Returns the binary reprasentation of the XML configuration. */
+Maybe<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
+ const std::string& config_path,
+ IDiagnostics* diag) {
+ StringInputStream in(contents);
+ std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, diag, Source(config_path));
+ if (!doc) {
+ return {};
+ }
+
+ // Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace.
+ Element* root = doc->root.get();
+ if (root == nullptr) {
+ diag->Error(DiagMessage() << "Could not find the root element in the XML document");
+ return {};
+ }
+
+ std::string& xml_ns = root->namespace_uri;
+ if (!xml_ns.empty()) {
+ if (xml_ns != kAaptXmlNs) {
+ diag->Error(DiagMessage() << "Unknown namespace found on root element: " << xml_ns);
+ return {};
+ }
+
+ xml_ns.clear();
+ NamespaceVisitor visitor;
+ root->Accept(&visitor);
+ }
+
+ XmlActionExecutor executor;
+ XmlNodeAction& root_action = executor["post-process"];
+ XmlNodeAction& artifacts_action = root_action["artifacts"];
+
+ PostProcessingConfiguration config;
+
+ // Parse the artifact elements.
+ artifacts_action["artifact"].Action(Bind(&config, ArtifactTagHandler));
+ artifacts_action["artifact-format"].Action(Bind(&config, ArtifactFormatTagHandler));
+
+ // Parse the different configuration groups.
+ root_action["abi-groups"]["abi-group"].Action(Bind(&config, AbiGroupTagHandler));
+ root_action["screen-density-groups"]["screen-density-group"].Action(
+ Bind(&config, ScreenDensityGroupTagHandler));
+ root_action["locale-groups"]["locale-group"].Action(Bind(&config, LocaleGroupTagHandler));
+ root_action["android-sdks"]["android-sdk"].Action(Bind(&config, AndroidSdkTagHandler));
+ root_action["gl-texture-groups"]["gl-texture-group"].Action(
+ Bind(&config, GlTextureGroupTagHandler));
+ root_action["device-feature-groups"]["device-feature-group"].Action(
+ Bind(&config, DeviceFeatureGroupTagHandler));
+
+ if (!executor.Execute(XmlActionExecutorPolicy::kNone, diag, doc.get())) {
+ diag->Error(DiagMessage() << "Could not process XML document");
+ return {};
+ }
+
+ return {config};
+}
+
+const StringPiece& AbiToString(Abi abi) {
+ return kAbiToStringMap.at(static_cast<size_t>(abi));
+}
+
+/**
* Returns the common artifact base name from a template string.
*/
Maybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, IDiagnostics* diag) {
@@ -186,8 +367,9 @@
return result;
}
-Maybe<std::string> Artifact::ToArtifactName(const StringPiece& format, const StringPiece& apk_name,
- IDiagnostics* diag) const {
+Maybe<std::string> ConfiguredArtifact::ToArtifactName(const StringPiece& format,
+ const StringPiece& apk_name,
+ IDiagnostics* diag) const {
Maybe<std::string> base = ToBaseName(format.to_string(), apk_name, diag);
if (!base) {
return {};
@@ -206,7 +388,7 @@
return {};
}
- if (!ReplacePlaceholder("${sdk}", android_sdk_group, &result, diag)) {
+ if (!ReplacePlaceholder("${sdk}", android_sdk, &result, diag)) {
return {};
}
@@ -221,7 +403,7 @@
return result;
}
-Maybe<std::string> Artifact::Name(const StringPiece& apk_name, IDiagnostics* diag) const {
+Maybe<std::string> ConfiguredArtifact::Name(const StringPiece& apk_name, IDiagnostics* diag) const {
if (!name) {
return {};
}
@@ -229,31 +411,6 @@
return ToBaseName(name.value(), apk_name, diag);
}
-bool PostProcessingConfiguration::AllArtifactNames(const StringPiece& apk_name,
- std::vector<std::string>* artifact_names,
- IDiagnostics* diag) const {
- for (const auto& artifact : artifacts) {
- Maybe<std::string> name;
- if (artifact.name) {
- name = artifact.Name(apk_name, diag);
- } else {
- if (!artifact_format) {
- diag->Error(DiagMessage() << "No global artifact template and an artifact name is missing");
- return false;
- }
- name = artifact.ToArtifactName(artifact_format.value(), apk_name, diag);
- }
-
- if (!name) {
- return false;
- }
-
- artifact_names->push_back(std::move(name.value()));
- }
-
- return true;
-}
-
} // namespace configuration
/** Returns a ConfigurationParser for the file located at the provided path. */
@@ -262,108 +419,64 @@
if (!ReadFileToString(path, &contents, true)) {
return {};
}
- return ConfigurationParser(contents);
+ return ConfigurationParser(contents, path);
}
-ConfigurationParser::ConfigurationParser(std::string contents)
- : contents_(std::move(contents)),
- diag_(&noop_) {
+ConfigurationParser::ConfigurationParser(std::string contents, const std::string& config_path)
+ : contents_(std::move(contents)), config_path_(config_path), diag_(&noop_) {
}
-Maybe<PostProcessingConfiguration> ConfigurationParser::Parse() {
- StringInputStream in(contents_);
- std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, diag_, Source("config.xml"));
- if (!doc) {
+Maybe<std::vector<OutputArtifact>> ConfigurationParser::Parse(
+ const android::StringPiece& apk_path) {
+ Maybe<PostProcessingConfiguration> maybe_config =
+ ExtractConfiguration(contents_, config_path_, diag_);
+ if (!maybe_config) {
return {};
}
- // Strip any namespaces from the XML as the XmlActionExecutor ignores anything with a namespace.
- Element* root = doc->root.get();
- if (root == nullptr) {
- diag_->Error(DiagMessage() << "Could not find the root element in the XML document");
- return {};
- }
+ // Convert from a parsed configuration to a list of artifacts for processing.
+ const std::string& apk_name = file::GetFilename(apk_path).to_string();
+ std::vector<OutputArtifact> output_artifacts;
+ bool has_errors = false;
- std::string& xml_ns = root->namespace_uri;
- if (!xml_ns.empty()) {
- if (xml_ns != kAaptXmlNs) {
- diag_->Error(DiagMessage() << "Unknown namespace found on root element: " << xml_ns);
- return {};
+ PostProcessingConfiguration& config = maybe_config.value();
+ config.SortArtifacts();
+
+ int version = 1;
+ for (const ConfiguredArtifact& artifact : config.artifacts) {
+ Maybe<OutputArtifact> output_artifact = ToOutputArtifact(artifact, apk_name, config, diag_);
+ if (!output_artifact) {
+ // Defer return an error condition so that all errors are reported.
+ has_errors = true;
+ } else {
+ output_artifact.value().version = version++;
+ output_artifacts.push_back(std::move(output_artifact.value()));
}
-
- xml_ns.clear();
- NamespaceVisitor visitor;
- root->Accept(&visitor);
}
- XmlActionExecutor executor;
- XmlNodeAction& root_action = executor["post-process"];
- XmlNodeAction& artifacts_action = root_action["artifacts"];
- XmlNodeAction& groups_action = root_action["groups"];
-
- PostProcessingConfiguration config;
-
- // Helper to bind a static method to an action handler in the DOM executor.
- auto bind_handler =
- [&config](std::function<bool(PostProcessingConfiguration*, Element*, IDiagnostics*)> h)
- -> XmlNodeAction::ActionFuncWithDiag {
- return std::bind(h, &config, std::placeholders::_1, std::placeholders::_2);
- };
-
- // Parse the artifact elements.
- artifacts_action["artifact"].Action(bind_handler(artifact_handler_));
- artifacts_action["artifact-format"].Action(bind_handler(artifact_format_handler_));
-
- // Parse the different configuration groups.
- groups_action["abi-group"].Action(bind_handler(abi_group_handler_));
- groups_action["screen-density-group"].Action(bind_handler(screen_density_group_handler_));
- groups_action["locale-group"].Action(bind_handler(locale_group_handler_));
- groups_action["android-sdk-group"].Action(bind_handler(android_sdk_group_handler_));
- groups_action["gl-texture-group"].Action(bind_handler(gl_texture_group_handler_));
- groups_action["device-feature-group"].Action(bind_handler(device_feature_group_handler_));
-
- if (!executor.Execute(XmlActionExecutorPolicy::kNone, diag_, doc.get())) {
- diag_->Error(DiagMessage() << "Could not process XML document");
+ if (has_errors) {
return {};
}
-
- // TODO: Validate all references in the configuration are valid. It should be safe to assume from
- // this point on that any references from one section to another will be present.
-
- // TODO: Automatically arrange artifacts so that they match Play Store multi-APK requirements.
- // see: https://developer.android.com/google/play/publishing/multiple-apks.html
- //
- // For now, make sure the version codes are unique.
- std::vector<Artifact>& artifacts = config.artifacts;
- std::sort(artifacts.begin(), artifacts.end());
- if (std::adjacent_find(artifacts.begin(), artifacts.end()) != artifacts.end()) {
- diag_->Error(DiagMessage() << "Configuration has duplicate versions");
- return {};
- }
-
- return {config};
+ return {output_artifacts};
}
-ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ =
- [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
- // This will be incremented later so the first version will always be different to the base APK.
- int current_version = (config->artifacts.empty()) ? 0 : config->artifacts.back().version;
+namespace configuration {
+namespace handler {
- Artifact artifact{};
- Maybe<int> version;
+bool ArtifactTagHandler(PostProcessingConfiguration* config, Element* root_element,
+ IDiagnostics* diag) {
+ ConfiguredArtifact artifact{};
for (const auto& attr : root_element->attributes) {
if (attr.name == "name") {
artifact.name = attr.value;
- } else if (attr.name == "version") {
- version = std::stoi(attr.value);
} else if (attr.name == "abi-group") {
artifact.abi_group = {attr.value};
} else if (attr.name == "screen-density-group") {
artifact.screen_density_group = {attr.value};
} else if (attr.name == "locale-group") {
artifact.locale_group = {attr.value};
- } else if (attr.name == "android-sdk-group") {
- artifact.android_sdk_group = {attr.value};
+ } else if (attr.name == "android-sdk") {
+ artifact.android_sdk = {attr.value};
} else if (attr.name == "gl-texture-group") {
artifact.gl_texture_group = {attr.value};
} else if (attr.name == "device-feature-group") {
@@ -373,15 +486,12 @@
<< attr.value);
}
}
-
- artifact.version = (version) ? version.value() : current_version + 1;
-
config->artifacts.push_back(artifact);
return true;
};
-ConfigurationParser::ActionHandler ConfigurationParser::artifact_format_handler_ =
- [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+bool ArtifactFormatTagHandler(PostProcessingConfiguration* config, Element* root_element,
+ IDiagnostics* /* diag */) {
for (auto& node : root_element->children) {
xml::Text* t;
if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
@@ -392,16 +502,26 @@
return true;
};
-ConfigurationParser::ActionHandler ConfigurationParser::abi_group_handler_ =
- [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+bool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
+ IDiagnostics* diag) {
std::string label = GetLabel(root_element, diag);
if (label.empty()) {
return false;
}
- auto& group = config->abi_groups[label];
+ auto& group = GetOrCreateGroup(label, &config->abi_groups);
bool valid = true;
+ // Special case for empty abi-group tag. Label will be used as the ABI.
+ if (root_element->GetChildElements().empty()) {
+ auto abi = kStringToAbiMap.find(label);
+ if (abi == kStringToAbiMap.end()) {
+ return false;
+ }
+ group.push_back(abi->second);
+ return true;
+ }
+
for (auto* child : root_element->GetChildElements()) {
if (child->name != "abi") {
diag->Error(DiagMessage() << "Unexpected element in ABI group: " << child->name);
@@ -410,7 +530,13 @@
for (auto& node : child->children) {
xml::Text* t;
if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
- group.push_back(kStringToAbiMap.at(TrimWhitespace(t->text).to_string()));
+ auto abi = kStringToAbiMap.find(TrimWhitespace(t->text).to_string());
+ if (abi != kStringToAbiMap.end()) {
+ group.push_back(abi->second);
+ } else {
+ diag->Error(DiagMessage() << "Could not parse ABI value: " << t->text);
+ valid = false;
+ }
break;
}
}
@@ -420,16 +546,35 @@
return valid;
};
-ConfigurationParser::ActionHandler ConfigurationParser::screen_density_group_handler_ =
- [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+bool ScreenDensityGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
+ IDiagnostics* diag) {
std::string label = GetLabel(root_element, diag);
if (label.empty()) {
return false;
}
- auto& group = config->screen_density_groups[label];
+ auto& group = GetOrCreateGroup(label, &config->screen_density_groups);
bool valid = true;
+ // Special case for empty screen-density-group tag. Label will be used as the screen density.
+ if (root_element->GetChildElements().empty()) {
+ ConfigDescription config_descriptor;
+ bool parsed = ConfigDescription::Parse(label, &config_descriptor);
+ if (parsed &&
+ (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
+ android::ResTable_config::CONFIG_DENSITY)) {
+ // Copy the density with the minimum SDK version stripped out.
+ group.push_back(config_descriptor.CopyWithoutSdkVersion());
+ } else {
+ diag->Error(DiagMessage()
+ << "Could not parse config descriptor for empty screen-density-group: "
+ << label);
+ valid = false;
+ }
+
+ return valid;
+ }
+
for (auto* child : root_element->GetChildElements()) {
if (child->name != "screen-density") {
diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
@@ -461,16 +606,35 @@
return valid;
};
-ConfigurationParser::ActionHandler ConfigurationParser::locale_group_handler_ =
- [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+bool LocaleGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
+ IDiagnostics* diag) {
std::string label = GetLabel(root_element, diag);
if (label.empty()) {
return false;
}
- auto& group = config->locale_groups[label];
+ auto& group = GetOrCreateGroup(label, &config->locale_groups);
bool valid = true;
+ // Special case to auto insert a locale for an empty group. Label will be used for locale.
+ if (root_element->GetChildElements().empty()) {
+ ConfigDescription config_descriptor;
+ bool parsed = ConfigDescription::Parse(label, &config_descriptor);
+ if (parsed &&
+ (config_descriptor.CopyWithoutSdkVersion().diff(ConfigDescription::DefaultConfig()) ==
+ android::ResTable_config::CONFIG_LOCALE)) {
+ // Copy the locale with the minimum SDK version stripped out.
+ group.push_back(config_descriptor.CopyWithoutSdkVersion());
+ } else {
+ diag->Error(DiagMessage()
+ << "Could not parse config descriptor for empty screen-density-group: "
+ << label);
+ valid = false;
+ }
+
+ return valid;
+ }
+
for (auto* child : root_element->GetChildElements()) {
if (child->name != "locale") {
diag->Error(DiagMessage() << "Unexpected root_element in screen density group: "
@@ -502,64 +666,69 @@
return valid;
};
-ConfigurationParser::ActionHandler ConfigurationParser::android_sdk_group_handler_ =
- [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
- std::string label = GetLabel(root_element, diag);
- if (label.empty()) {
- return false;
- }
-
+bool AndroidSdkTagHandler(PostProcessingConfiguration* config, Element* root_element,
+ IDiagnostics* diag) {
+ AndroidSdk entry = AndroidSdk::ForMinSdk(-1);
bool valid = true;
- bool found = false;
+ for (const auto& attr : root_element->attributes) {
+ bool valid_attr = false;
+ if (attr.name == "label") {
+ entry.label = attr.value;
+ valid_attr = true;
+ } else if (attr.name == "minSdkVersion") {
+ Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
+ if (version) {
+ valid_attr = true;
+ entry.min_sdk_version = version.value();
+ }
+ } else if (attr.name == "targetSdkVersion") {
+ Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
+ if (version) {
+ valid_attr = true;
+ entry.target_sdk_version = version;
+ }
+ } else if (attr.name == "maxSdkVersion") {
+ Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
+ if (version) {
+ valid_attr = true;
+ entry.max_sdk_version = version;
+ }
+ }
- for (auto* child : root_element->GetChildElements()) {
- if (child->name != "android-sdk") {
- diag->Error(DiagMessage() << "Unexpected root_element in ABI group: " << child->name);
+ if (!valid_attr) {
+ diag->Error(DiagMessage() << "Invalid attribute: " << attr.name << " = " << attr.value);
valid = false;
- } else {
- AndroidSdk entry;
- for (const auto& attr : child->attributes) {
- if (attr.name == "minSdkVersion") {
- entry.min_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
- } else if (attr.name == "targetSdkVersion") {
- entry.target_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
- } else if (attr.name == "maxSdkVersion") {
- entry.max_sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
- } else {
- diag->Warn(DiagMessage() << "Unknown attribute: " << attr.name << " = " << attr.value);
- }
- }
-
- // TODO: Fill in the manifest details when they are finalised.
- for (auto node : child->GetChildElements()) {
- if (node->name == "manifest") {
- if (entry.manifest) {
- diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
- continue;
- }
- entry.manifest = {AndroidManifest()};
- }
- }
-
- config->android_sdk_groups[label] = entry;
- if (found) {
- valid = false;
- }
- found = true;
}
}
+ if (entry.min_sdk_version == -1) {
+ diag->Error(DiagMessage() << "android-sdk is missing minSdkVersion attribute");
+ valid = false;
+ }
+
+ // TODO: Fill in the manifest details when they are finalised.
+ for (auto node : root_element->GetChildElements()) {
+ if (node->name == "manifest") {
+ if (entry.manifest) {
+ diag->Warn(DiagMessage() << "Found multiple manifest tags. Ignoring duplicates.");
+ continue;
+ }
+ entry.manifest = {AndroidManifest()};
+ }
+ }
+
+ config->android_sdks[entry.label] = entry;
return valid;
};
-ConfigurationParser::ActionHandler ConfigurationParser::gl_texture_group_handler_ =
- [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+bool GlTextureGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
+ IDiagnostics* diag) {
std::string label = GetLabel(root_element, diag);
if (label.empty()) {
return false;
}
- auto& group = config->gl_texture_groups[label];
+ auto& group = GetOrCreateGroup(label, &config->gl_texture_groups);
bool valid = true;
GlTexture result;
@@ -595,14 +764,14 @@
return valid;
};
-ConfigurationParser::ActionHandler ConfigurationParser::device_feature_group_handler_ =
- [](PostProcessingConfiguration* config, Element* root_element, IDiagnostics* diag) -> bool {
+bool DeviceFeatureGroupTagHandler(PostProcessingConfiguration* config, Element* root_element,
+ IDiagnostics* diag) {
std::string label = GetLabel(root_element, diag);
if (label.empty()) {
return false;
}
- auto& group = config->device_feature_groups[label];
+ auto& group = GetOrCreateGroup(label, &config->device_feature_groups);
bool valid = true;
for (auto* child : root_element->GetChildElements()) {
@@ -624,4 +793,7 @@
return valid;
};
+} // namespace handler
+} // namespace configuration
+
} // namespace aapt
diff --git a/tools/aapt2/configuration/ConfigurationParser.h b/tools/aapt2/configuration/ConfigurationParser.h
index c5d3284..7f1d445 100644
--- a/tools/aapt2/configuration/ConfigurationParser.h
+++ b/tools/aapt2/configuration/ConfigurationParser.h
@@ -30,54 +30,6 @@
namespace configuration {
-/** A mapping of group labels to group of configuration items. */
-template<class T>
-using Group = std::unordered_map<std::string, std::vector<T>>;
-
-/** A mapping of group label to a single configuration item. */
-template <class T>
-using Entry = std::unordered_map<std::string, T>;
-
-/** Output artifact configuration options. */
-struct Artifact {
- /** Name to use for output of processing foo.apk -> foo.<name>.apk. */
- Maybe<std::string> name;
- /**
- * Value to add to the base Android manifest versionCode. If it is not present in the
- * configuration file, it is set to the previous artifact + 1. If the first artifact does not have
- * a value, artifacts are a 1 based index.
- */
- int version;
- /** If present, uses the ABI group with this name. */
- Maybe<std::string> abi_group;
- /** If present, uses the screen density group with this name. */
- Maybe<std::string> screen_density_group;
- /** If present, uses the locale group with this name. */
- Maybe<std::string> locale_group;
- /** If present, uses the Android SDK group with this name. */
- Maybe<std::string> android_sdk_group;
- /** If present, uses the device feature group with this name. */
- Maybe<std::string> device_feature_group;
- /** If present, uses the OpenGL texture group with this name. */
- Maybe<std::string> gl_texture_group;
-
- /** Convert an artifact name template into a name string based on configuration contents. */
- Maybe<std::string> ToArtifactName(const android::StringPiece& format,
- const android::StringPiece& apk_name, IDiagnostics* diag) const;
-
- /** Convert an artifact name template into a name string based on configuration contents. */
- Maybe<std::string> Name(const android::StringPiece& apk_name, IDiagnostics* diag) const;
-
- bool operator<(const Artifact& rhs) const {
- // TODO(safarmer): Order by play store multi-APK requirements.
- return version < rhs.version;
- }
-
- bool operator==(const Artifact& rhs) const {
- return version == rhs.version;
- }
-};
-
/** Enumeration of currently supported ABIs. */
enum class Abi {
kArmeV6,
@@ -91,7 +43,7 @@
};
/** Helper method to convert an ABI to a string representing the path within the APK. */
-const std::string& AbiToString(Abi abi);
+const android::StringPiece& AbiToString(Abi abi);
/**
* Represents an individual locale. When a locale is included, it must be
@@ -119,7 +71,8 @@
};
struct AndroidSdk {
- Maybe<int> min_sdk_version;
+ std::string label;
+ int min_sdk_version; // min_sdk_version is mandatory if splitting by SDK.
Maybe<int> target_sdk_version;
Maybe<int> max_sdk_version;
Maybe<AndroidManifest> manifest;
@@ -151,31 +104,29 @@
}
};
-/** AAPT2 XML configuration file binary representation. */
-struct PostProcessingConfiguration {
- // TODO: Support named artifacts?
- std::vector<Artifact> artifacts;
- Maybe<std::string> artifact_format;
+/** An artifact with all the details pulled from the PostProcessingConfiguration. */
+struct OutputArtifact {
+ std::string name;
+ int version;
+ std::vector<Abi> abis;
+ std::vector<ConfigDescription> screen_densities;
+ std::vector<ConfigDescription> locales;
+ Maybe<AndroidSdk> android_sdk;
+ std::vector<DeviceFeature> features;
+ std::vector<GlTexture> textures;
- Group<Abi> abi_groups;
- Group<ConfigDescription> screen_density_groups;
- Group<ConfigDescription> locale_groups;
- Entry<AndroidSdk> android_sdk_groups;
- Group<DeviceFeature> device_feature_groups;
- Group<GlTexture> gl_texture_groups;
-
- /** Helper method that generates a list of artifact names and returns true on success. */
- bool AllArtifactNames(const android::StringPiece& apk_name,
- std::vector<std::string>* artifact_names, IDiagnostics* diag) const;
+ inline int GetMinSdk(int default_value = -1) const {
+ if (!android_sdk) {
+ return default_value;
+ }
+ return android_sdk.value().min_sdk_version;
+ }
};
} // namespace configuration
// Forward declaration of classes used in the API.
struct IDiagnostics;
-namespace xml {
-class Element;
-}
/**
* XML configuration file parser for the split and optimize commands.
@@ -187,8 +138,8 @@
static Maybe<ConfigurationParser> ForPath(const std::string& path);
/** Returns a ConfigurationParser for the configuration in the provided file contents. */
- static ConfigurationParser ForContents(const std::string& contents) {
- ConfigurationParser parser{contents};
+ static ConfigurationParser ForContents(const std::string& contents, const std::string& path) {
+ ConfigurationParser parser{contents, path};
return parser;
}
@@ -202,7 +153,7 @@
* Parses the configuration file and returns the results. If the configuration could not be parsed
* the result is empty and any errors will be displayed with the provided diagnostics context.
*/
- Maybe<configuration::PostProcessingConfiguration> Parse();
+ Maybe<std::vector<configuration::OutputArtifact>> Parse(const android::StringPiece& apk_path);
protected:
/**
@@ -210,40 +161,18 @@
* diagnostics context. The default diagnostics context can be overridden with a call to
* WithDiagnostics(IDiagnostics *).
*/
- explicit ConfigurationParser(std::string contents);
+ ConfigurationParser(std::string contents, const std::string& config_path);
/** Returns the current diagnostics context to any subclasses. */
IDiagnostics* diagnostics() {
return diag_;
}
- /**
- * An ActionHandler for processing XML elements in the XmlActionExecutor. Returns true if the
- * element was successfully processed, otherwise returns false.
- */
- using ActionHandler = std::function<bool(configuration::PostProcessingConfiguration* config,
- xml::Element* element, IDiagnostics* diag)>;
-
- /** Handler for <artifact> tags. */
- static ActionHandler artifact_handler_;
- /** Handler for <artifact-format> tags. */
- static ActionHandler artifact_format_handler_;
- /** Handler for <abi-group> tags. */
- static ActionHandler abi_group_handler_;
- /** Handler for <screen-density-group> tags. */
- static ActionHandler screen_density_group_handler_;
- /** Handler for <locale-group> tags. */
- static ActionHandler locale_group_handler_;
- /** Handler for <android-sdk-group> tags. */
- static ActionHandler android_sdk_group_handler_;
- /** Handler for <gl-texture-group> tags. */
- static ActionHandler gl_texture_group_handler_;
- /** Handler for <device-feature-group> tags. */
- static ActionHandler device_feature_group_handler_;
-
private:
/** The contents of the configuration file to parse. */
const std::string contents_;
+ /** Path to the input configuration. */
+ const std::string config_path_;
/** The diagnostics context to send messages to. */
IDiagnostics* diag_;
};
diff --git a/tools/aapt2/configuration/ConfigurationParser.internal.h b/tools/aapt2/configuration/ConfigurationParser.internal.h
new file mode 100644
index 0000000..a583057
--- /dev/null
+++ b/tools/aapt2/configuration/ConfigurationParser.internal.h
@@ -0,0 +1,232 @@
+/*
+ * 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 AAPT2_CONFIGURATIONPARSER_INTERNAL_H
+#define AAPT2_CONFIGURATIONPARSER_INTERNAL_H
+
+#include "configuration/ConfigurationParser.h"
+
+#include <algorithm>
+#include <limits>
+
+namespace aapt {
+
+// Forward declaration of classes used in the API.
+namespace xml {
+class Element;
+}
+
+namespace configuration {
+
+template <typename T>
+struct OrderedEntry {
+ size_t order;
+ std::vector<T> entry;
+};
+
+/** A mapping of group labels to group of configuration items. */
+template <class T>
+using Group = std::unordered_map<std::string, OrderedEntry<T>>;
+
+/** A mapping of group label to a single configuration item. */
+template <class T>
+using Entry = std::unordered_map<std::string, T>;
+
+/** Retrieves an entry from the provided Group, creating a new instance if one does not exist. */
+template <typename T>
+std::vector<T>& GetOrCreateGroup(std::string label, Group<T>* group) {
+ OrderedEntry<T>& entry = (*group)[label];
+ // If this is a new entry, set the order.
+ if (entry.order == 0) {
+ entry.order = group->size();
+ }
+ return entry.entry;
+}
+
+/**
+ * A ComparisonChain is a grouping of comparisons to perform when sorting groups that have a well
+ * defined order of precedence. Comparisons are only made if none of the previous comparisons had a
+ * definite result. A comparison has a result if at least one of the items has an entry for that
+ * value and that they are not equal.
+ */
+class ComparisonChain {
+ public:
+ /**
+ * Adds a new comparison of items in a group to the chain. The new comparison is only used if we
+ * have not been able to determine the sort order with the previous comparisons.
+ */
+ template <typename T>
+ ComparisonChain& Add(const Group<T>& groups, const Maybe<std::string>& lhs,
+ const Maybe<std::string>& rhs) {
+ return Add(GetGroupOrder(groups, lhs), GetGroupOrder(groups, rhs));
+ }
+
+ /**
+ * Adds a new comparison to the chain. The new comparison is only used if we have not been able to
+ * determine the sort order with the previous comparisons.
+ */
+ ComparisonChain& Add(int lhs, int rhs) {
+ if (!has_result_) {
+ has_result_ = (lhs != rhs);
+ result_ = (lhs < rhs);
+ }
+ return *this;
+ }
+
+ /** Returns true if the left hand side should come before the right hand side. */
+ bool Compare() {
+ return result_;
+ }
+
+ private:
+ template <typename T>
+ inline size_t GetGroupOrder(const Group<T>& groups, const Maybe<std::string>& label) {
+ if (!label) {
+ return std::numeric_limits<size_t>::max();
+ }
+ return groups.at(label.value()).order;
+ }
+
+ bool has_result_ = false;
+ bool result_ = false;
+};
+
+/** Output artifact configuration options. */
+struct ConfiguredArtifact {
+ /** Name to use for output of processing foo.apk -> foo.<name>.apk. */
+ Maybe<std::string> name;
+ /** If present, uses the ABI group with this name. */
+ Maybe<std::string> abi_group;
+ /** If present, uses the screen density group with this name. */
+ Maybe<std::string> screen_density_group;
+ /** If present, uses the locale group with this name. */
+ Maybe<std::string> locale_group;
+ /** If present, uses the Android SDK with this name. */
+ Maybe<std::string> android_sdk;
+ /** If present, uses the device feature group with this name. */
+ Maybe<std::string> device_feature_group;
+ /** If present, uses the OpenGL texture group with this name. */
+ Maybe<std::string> gl_texture_group;
+
+ /** Convert an artifact name template into a name string based on configuration contents. */
+ Maybe<std::string> ToArtifactName(const android::StringPiece& format,
+ const android::StringPiece& apk_name, IDiagnostics* diag) const;
+
+ /** Convert an artifact name template into a name string based on configuration contents. */
+ Maybe<std::string> Name(const android::StringPiece& apk_name, IDiagnostics* diag) const;
+};
+
+/** AAPT2 XML configuration file binary representation. */
+struct PostProcessingConfiguration {
+ std::vector<ConfiguredArtifact> artifacts;
+ Maybe<std::string> artifact_format;
+
+ Group<Abi> abi_groups;
+ Group<ConfigDescription> screen_density_groups;
+ Group<ConfigDescription> locale_groups;
+ Group<DeviceFeature> device_feature_groups;
+ Group<GlTexture> gl_texture_groups;
+ Entry<AndroidSdk> android_sdks;
+
+ /**
+ * Sorts the configured artifacts based on the ordering of the groups in the configuration file.
+ * The only exception to this rule is Android SDK versions. Larger SDK versions will have a larger
+ * versionCode to ensure users get the correct APK when they upgrade their OS.
+ */
+ void SortArtifacts() {
+ std::sort(artifacts.begin(), artifacts.end(), *this);
+ }
+
+ /** Comparator that ensures artifacts are in the preferred order for versionCode rewriting. */
+ bool operator()(const ConfiguredArtifact& lhs, const ConfiguredArtifact& rhs) {
+ // Split dimensions are added in the order of precedence. Items higher in the list result in
+ // higher version codes.
+ return ComparisonChain()
+ // All splits with a minSdkVersion specified must be last to ensure the application will be
+ // updated if a user upgrades the version of Android on their device.
+ .Add(GetMinSdk(lhs), GetMinSdk(rhs))
+ // ABI version is important, especially on x86 phones where they may begin to run in ARM
+ // emulation mode on newer Android versions. This allows us to ensure that the x86 version
+ // is installed on these devices rather than ARM.
+ .Add(abi_groups, lhs.abi_group, rhs.abi_group)
+ // The rest are in arbitrary order based on estimated usage.
+ .Add(screen_density_groups, lhs.screen_density_group, rhs.screen_density_group)
+ .Add(locale_groups, lhs.locale_group, rhs.locale_group)
+ .Add(gl_texture_groups, lhs.gl_texture_group, rhs.gl_texture_group)
+ .Add(device_feature_groups, lhs.device_feature_group, rhs.device_feature_group)
+ .Compare();
+ }
+
+ private:
+ /**
+ * Returns the min_sdk_version from the provided artifact or 0 if none is present. This allows
+ * artifacts that have an Android SDK version to have a higher versionCode than those that do not.
+ */
+ inline int GetMinSdk(const ConfiguredArtifact& artifact) {
+ if (!artifact.android_sdk) {
+ return 0;
+ }
+ const auto& entry = android_sdks.find(artifact.android_sdk.value());
+ if (entry == android_sdks.end()) {
+ return 0;
+ }
+ return entry->second.min_sdk_version;
+ }
+};
+
+/** Parses the provided XML document returning the post processing configuration. */
+Maybe<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
+ const std::string& config_path,
+ IDiagnostics* diag);
+
+namespace handler {
+
+/** Handler for <artifact> tags. */
+bool ArtifactTagHandler(configuration::PostProcessingConfiguration* config, xml::Element* element,
+ IDiagnostics* diag);
+
+/** Handler for <artifact-format> tags. */
+bool ArtifactFormatTagHandler(configuration::PostProcessingConfiguration* config,
+ xml::Element* element, IDiagnostics* diag);
+
+/** Handler for <abi-group> tags. */
+bool AbiGroupTagHandler(configuration::PostProcessingConfiguration* config, xml::Element* element,
+ IDiagnostics* diag);
+
+/** Handler for <screen-density-group> tags. */
+bool ScreenDensityGroupTagHandler(configuration::PostProcessingConfiguration* config,
+ xml::Element* element, IDiagnostics* diag);
+
+/** Handler for <locale-group> tags. */
+bool LocaleGroupTagHandler(configuration::PostProcessingConfiguration* config,
+ xml::Element* element, IDiagnostics* diag);
+
+/** Handler for <android-sdk> tags. */
+bool AndroidSdkTagHandler(configuration::PostProcessingConfiguration* config, xml::Element* element,
+ IDiagnostics* diag);
+
+/** Handler for <gl-texture-group> tags. */
+bool GlTextureGroupTagHandler(configuration::PostProcessingConfiguration* config,
+ xml::Element* element, IDiagnostics* diag);
+
+/** Handler for <device-feature-group> tags. */
+bool DeviceFeatureGroupTagHandler(configuration::PostProcessingConfiguration* config,
+ xml::Element* element, IDiagnostics* diag);
+
+} // namespace handler
+} // namespace configuration
+} // namespace aapt
+#endif // AAPT2_CONFIGURATIONPARSER_INTERNAL_H
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index afa155f..0329846 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -18,8 +18,11 @@
#include <string>
+#include "android-base/stringprintf.h"
#include "androidfw/ResourceTypes.h"
+#include "SdkConstants.h"
+#include "configuration/ConfigurationParser.internal.h"
#include "test/Test.h"
#include "xml/XmlDom.h"
@@ -27,30 +30,58 @@
namespace configuration {
void PrintTo(const AndroidSdk& sdk, std::ostream* os) {
- *os << "SDK: min=" << sdk.min_sdk_version.value_or_default(-1)
+ *os << "SDK: min=" << sdk.min_sdk_version
<< ", target=" << sdk.target_sdk_version.value_or_default(-1)
<< ", max=" << sdk.max_sdk_version.value_or_default(-1);
}
-} // namespace configuration
+
+bool operator==(const ConfiguredArtifact& lhs, const ConfiguredArtifact& rhs) {
+ return lhs.name == rhs.name && lhs.abi_group == rhs.abi_group &&
+ lhs.screen_density_group == rhs.screen_density_group &&
+ lhs.locale_group == rhs.locale_group && lhs.android_sdk == rhs.android_sdk &&
+ lhs.device_feature_group == rhs.device_feature_group &&
+ lhs.gl_texture_group == rhs.gl_texture_group;
+}
+
+std::ostream& operator<<(std::ostream& out, const Maybe<std::string>& value) {
+ PrintTo(value, &out);
+ return out;
+}
+
+void PrintTo(const ConfiguredArtifact& artifact, std::ostream* os) {
+ *os << "\n{"
+ << "\n name: " << artifact.name << "\n sdk: " << artifact.android_sdk
+ << "\n abi: " << artifact.abi_group << "\n density: " << artifact.screen_density_group
+ << "\n locale: " << artifact.locale_group
+ << "\n features: " << artifact.device_feature_group
+ << "\n textures: " << artifact.gl_texture_group << "\n}\n";
+}
+
+namespace handler {
namespace {
+using ::aapt::configuration::Abi;
+using ::aapt::configuration::AndroidManifest;
+using ::aapt::configuration::AndroidSdk;
+using ::aapt::configuration::ConfiguredArtifact;
+using ::aapt::configuration::DeviceFeature;
+using ::aapt::configuration::ExtractConfiguration;
+using ::aapt::configuration::GlTexture;
+using ::aapt::configuration::Locale;
+using ::aapt::configuration::PostProcessingConfiguration;
+using ::aapt::xml::Element;
+using ::aapt::xml::NodeCast;
using ::android::ResTable_config;
-using configuration::Abi;
-using configuration::AndroidSdk;
-using configuration::Artifact;
-using configuration::PostProcessingConfiguration;
-using configuration::DeviceFeature;
-using configuration::GlTexture;
-using configuration::Locale;
-using configuration::AndroidManifest;
+using ::android::base::StringPrintf;
using ::testing::ElementsAre;
-using xml::Element;
-using xml::NodeCast;
+using ::testing::Eq;
+using ::testing::SizeIs;
+using ::testing::StrEq;
constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?>
<post-process xmlns="http://schemas.android.com/tools/aapt">
- <groups>
+ <abi-groups>
<abi-group label="arm">
<abi>armeabi-v7a</abi>
<abi>arm64-v8a</abi>
@@ -59,6 +90,8 @@
<abi>x86</abi>
<abi>mips</abi>
</abi-group>
+ </abi-groups>
+ <screen-density-groups>
<screen-density-group label="large">
<screen-density>xhdpi</screen-density>
<screen-density>xxhdpi</screen-density>
@@ -72,6 +105,8 @@
<screen-density>xxhdpi</screen-density>
<screen-density>xxxhdpi</screen-density>
</screen-density-group>
+ </screen-density-groups>
+ <locale-groups>
<locale-group label="europe">
<locale>en</locale>
<locale>es</locale>
@@ -83,25 +118,30 @@
<locale>es-rMX</locale>
<locale>fr-rCA</locale>
</locale-group>
- <android-sdk-group label="v19">
- <android-sdk
- minSdkVersion="19"
- targetSdkVersion="24"
- maxSdkVersion="25">
- <manifest>
- <!--- manifest additions here XSLT? TODO -->
- </manifest>
- </android-sdk>
- </android-sdk-group>
+ </locale-groups>
+ <android-sdks>
+ <android-sdk
+ label="v19"
+ minSdkVersion="19"
+ targetSdkVersion="24"
+ maxSdkVersion="25">
+ <manifest>
+ <!--- manifest additions here XSLT? TODO -->
+ </manifest>
+ </android-sdk>
+ </android-sdks>
+ <gl-texture-groups>
<gl-texture-group label="dxt1">
<gl-texture name="GL_EXT_texture_compression_dxt1">
<texture-path>assets/dxt1/*</texture-path>
</gl-texture>
</gl-texture-group>
+ </gl-texture-groups>
+ <device-feature-groups>
<device-feature-group label="low-latency">
<supports-feature>android.hardware.audio.low_latency</supports-feature>
</device-feature-group>
- </groups>
+ </device-feature-groups>
<artifacts>
<artifact-format>
${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release
@@ -111,7 +151,7 @@
abi-group="arm"
screen-density-group="large"
locale-group="europe"
- android-sdk-group="v19"
+ android-sdk="v19"
gl-texture-group="dxt1"
device-feature-group="low-latency"/>
<artifact
@@ -119,7 +159,7 @@
abi-group="other"
screen-density-group="alldpi"
locale-group="north-america"
- android-sdk-group="v19"
+ android-sdk="v19"
gl-texture-group="dxt1"
device-feature-group="low-latency"/>
</artifacts>
@@ -128,7 +168,8 @@
class ConfigurationParserTest : public ConfigurationParser, public ::testing::Test {
public:
- ConfigurationParserTest() : ConfigurationParser("") {}
+ ConfigurationParserTest() : ConfigurationParser("", "config.xml") {
+ }
protected:
StdErrDiagnostics diag_;
@@ -139,154 +180,198 @@
EXPECT_FALSE(result);
}
+TEST_F(ConfigurationParserTest, ExtractConfiguration) {
+ Maybe<PostProcessingConfiguration> maybe_config =
+ ExtractConfiguration(kValidConfig, "dummy.xml", &diag_);
+
+ PostProcessingConfiguration config = maybe_config.value();
+
+ auto& arm = config.abi_groups["arm"];
+ auto& other = config.abi_groups["other"];
+ EXPECT_EQ(arm.order, 1ul);
+ EXPECT_EQ(other.order, 2ul);
+
+ auto& large = config.screen_density_groups["large"];
+ auto& alldpi = config.screen_density_groups["alldpi"];
+ EXPECT_EQ(large.order, 1ul);
+ EXPECT_EQ(alldpi.order, 2ul);
+
+ auto& north_america = config.locale_groups["north-america"];
+ auto& europe = config.locale_groups["europe"];
+ // Checked in reverse to make sure access order does not matter.
+ EXPECT_EQ(north_america.order, 2ul);
+ EXPECT_EQ(europe.order, 1ul);
+}
+
TEST_F(ConfigurationParserTest, ValidateFile) {
- auto parser = ConfigurationParser::ForContents(kValidConfig).WithDiagnostics(&diag_);
- auto result = parser.Parse();
+ auto parser = ConfigurationParser::ForContents(kValidConfig, "conf.xml").WithDiagnostics(&diag_);
+ auto result = parser.Parse("test.apk");
ASSERT_TRUE(result);
- PostProcessingConfiguration& value = result.value();
- EXPECT_EQ(2ul, value.artifacts.size());
- ASSERT_TRUE(value.artifact_format);
- EXPECT_EQ(
- "${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release",
- value.artifact_format.value()
- );
+ const std::vector<OutputArtifact>& value = result.value();
+ EXPECT_THAT(value, SizeIs(2ul));
- EXPECT_EQ(2ul, value.abi_groups.size());
- EXPECT_EQ(2ul, value.abi_groups["arm"].size());
- EXPECT_EQ(2ul, value.abi_groups["other"].size());
+ const OutputArtifact& a1 = value[0];
+ EXPECT_EQ(a1.name, "art1.apk");
+ EXPECT_EQ(a1.version, 1);
+ EXPECT_THAT(a1.abis, ElementsAre(Abi::kArmV7a, Abi::kArm64V8a));
+ EXPECT_THAT(a1.screen_densities,
+ ElementsAre(test::ParseConfigOrDie("xhdpi").CopyWithoutSdkVersion(),
+ test::ParseConfigOrDie("xxhdpi").CopyWithoutSdkVersion(),
+ test::ParseConfigOrDie("xxxhdpi").CopyWithoutSdkVersion()));
+ EXPECT_THAT(a1.locales, ElementsAre(test::ParseConfigOrDie("en"), test::ParseConfigOrDie("es"),
+ test::ParseConfigOrDie("fr"), test::ParseConfigOrDie("de")));
+ ASSERT_TRUE(a1.android_sdk);
+ ASSERT_TRUE(a1.android_sdk.value().min_sdk_version);
+ EXPECT_EQ(a1.android_sdk.value().min_sdk_version, 19l);
+ EXPECT_THAT(a1.textures, SizeIs(1ul));
+ EXPECT_THAT(a1.features, SizeIs(1ul));
- EXPECT_EQ(2ul, value.screen_density_groups.size());
- EXPECT_EQ(3ul, value.screen_density_groups["large"].size());
- EXPECT_EQ(6ul, value.screen_density_groups["alldpi"].size());
+ const OutputArtifact& a2 = value[1];
+ EXPECT_EQ(a2.name, "art2.apk");
+ EXPECT_EQ(a2.version, 2);
+ EXPECT_THAT(a2.abis, ElementsAre(Abi::kX86, Abi::kMips));
+ EXPECT_THAT(a2.screen_densities,
+ ElementsAre(test::ParseConfigOrDie("ldpi").CopyWithoutSdkVersion(),
+ test::ParseConfigOrDie("mdpi").CopyWithoutSdkVersion(),
+ test::ParseConfigOrDie("hdpi").CopyWithoutSdkVersion(),
+ test::ParseConfigOrDie("xhdpi").CopyWithoutSdkVersion(),
+ test::ParseConfigOrDie("xxhdpi").CopyWithoutSdkVersion(),
+ test::ParseConfigOrDie("xxxhdpi").CopyWithoutSdkVersion()));
+ EXPECT_THAT(a2.locales,
+ ElementsAre(test::ParseConfigOrDie("en"), test::ParseConfigOrDie("es-rMX"),
+ test::ParseConfigOrDie("fr-rCA")));
+ ASSERT_TRUE(a2.android_sdk);
+ ASSERT_TRUE(a2.android_sdk.value().min_sdk_version);
+ EXPECT_EQ(a2.android_sdk.value().min_sdk_version, 19l);
+ EXPECT_THAT(a2.textures, SizeIs(1ul));
+ EXPECT_THAT(a2.features, SizeIs(1ul));
+}
- EXPECT_EQ(2ul, value.locale_groups.size());
- EXPECT_EQ(4ul, value.locale_groups["europe"].size());
- EXPECT_EQ(3ul, value.locale_groups["north-america"].size());
+TEST_F(ConfigurationParserTest, ConfiguredArtifactOrdering) {
+ // Create a base builder with the configuration groups but no artifacts to allow it to be copied.
+ test::PostProcessingConfigurationBuilder base_builder = test::PostProcessingConfigurationBuilder()
+ .AddAbiGroup("arm")
+ .AddAbiGroup("arm64")
+ .AddAndroidSdk("v23", 23)
+ .AddAndroidSdk("v19", 19);
- EXPECT_EQ(1ul, value.android_sdk_groups.size());
- EXPECT_TRUE(value.android_sdk_groups["v19"].min_sdk_version);
- EXPECT_EQ(19, value.android_sdk_groups["v19"].min_sdk_version.value());
+ {
+ // Test version ordering.
+ ConfiguredArtifact v23;
+ v23.android_sdk = {"v23"};
+ ConfiguredArtifact v19;
+ v19.android_sdk = {"v19"};
- EXPECT_EQ(1ul, value.gl_texture_groups.size());
- EXPECT_EQ(1ul, value.gl_texture_groups["dxt1"].size());
+ test::PostProcessingConfigurationBuilder builder = base_builder;
+ PostProcessingConfiguration config = builder.AddArtifact(v23).AddArtifact(v19).Build();
- EXPECT_EQ(1ul, value.device_feature_groups.size());
- EXPECT_EQ(1ul, value.device_feature_groups["low-latency"].size());
+ config.SortArtifacts();
+ ASSERT_THAT(config.artifacts, SizeIs(2));
+ EXPECT_THAT(config.artifacts[0], Eq(v19));
+ EXPECT_THAT(config.artifacts[1], Eq(v23));
+ }
+
+ {
+ // Test ABI ordering.
+ ConfiguredArtifact arm;
+ arm.android_sdk = {"v19"};
+ arm.abi_group = {"arm"};
+ ConfiguredArtifact arm64;
+ arm64.android_sdk = {"v19"};
+ arm64.abi_group = {"arm64"};
+
+ test::PostProcessingConfigurationBuilder builder = base_builder;
+ PostProcessingConfiguration config = builder.AddArtifact(arm64).AddArtifact(arm).Build();
+
+ config.SortArtifacts();
+ ASSERT_THAT(config.artifacts, SizeIs(2));
+ EXPECT_THAT(config.artifacts[0], Eq(arm));
+ EXPECT_THAT(config.artifacts[1], Eq(arm64));
+ }
+
+ {
+ // Test Android SDK has precedence over ABI.
+ ConfiguredArtifact arm;
+ arm.android_sdk = {"v23"};
+ arm.abi_group = {"arm"};
+ ConfiguredArtifact arm64;
+ arm64.android_sdk = {"v19"};
+ arm64.abi_group = {"arm64"};
+
+ test::PostProcessingConfigurationBuilder builder = base_builder;
+ PostProcessingConfiguration config = builder.AddArtifact(arm64).AddArtifact(arm).Build();
+
+ config.SortArtifacts();
+ ASSERT_THAT(config.artifacts, SizeIs(2));
+ EXPECT_THAT(config.artifacts[0], Eq(arm64));
+ EXPECT_THAT(config.artifacts[1], Eq(arm));
+ }
+
+ {
+ // Test version is better than ABI.
+ ConfiguredArtifact arm;
+ arm.abi_group = {"arm"};
+ ConfiguredArtifact v19;
+ v19.android_sdk = {"v19"};
+
+ test::PostProcessingConfigurationBuilder builder = base_builder;
+ PostProcessingConfiguration config = builder.AddArtifact(v19).AddArtifact(arm).Build();
+
+ config.SortArtifacts();
+ ASSERT_THAT(config.artifacts, SizeIs(2));
+ EXPECT_THAT(config.artifacts[0], Eq(arm));
+ EXPECT_THAT(config.artifacts[1], Eq(v19));
+ }
+
+ {
+ // Test version is sorted higher than no version.
+ ConfiguredArtifact arm;
+ arm.abi_group = {"arm"};
+ ConfiguredArtifact v19;
+ v19.abi_group = {"arm"};
+ v19.android_sdk = {"v19"};
+
+ test::PostProcessingConfigurationBuilder builder = base_builder;
+ PostProcessingConfiguration config = builder.AddArtifact(v19).AddArtifact(arm).Build();
+
+ config.SortArtifacts();
+ ASSERT_THAT(config.artifacts, SizeIs(2));
+ EXPECT_THAT(config.artifacts[0], Eq(arm));
+ EXPECT_THAT(config.artifacts[1], Eq(v19));
+ }
}
TEST_F(ConfigurationParserTest, InvalidNamespace) {
constexpr const char* invalid_ns = R"(<?xml version="1.0" encoding="utf-8" ?>
- <post-process xmlns="http://schemas.android.com/tools/another-unknown-tool" />)";
+ <post-process xmlns="http://schemas.android.com/tools/another-unknown-tool" />)";
- auto result = ConfigurationParser::ForContents(invalid_ns).Parse();
+ auto result = ConfigurationParser::ForContents(invalid_ns, "config.xml").Parse("test.apk");
ASSERT_FALSE(result);
}
TEST_F(ConfigurationParserTest, ArtifactAction) {
PostProcessingConfiguration config;
- {
- const auto doc = test::BuildXmlDom(R"xml(
+ const auto doc = test::BuildXmlDom(R"xml(
<artifact
abi-group="arm"
screen-density-group="large"
locale-group="europe"
- android-sdk-group="v19"
+ android-sdk="v19"
gl-texture-group="dxt1"
device-feature-group="low-latency"/>)xml");
- ASSERT_TRUE(artifact_handler_(&config, NodeCast<Element>(doc->root.get()), &diag_));
+ ASSERT_TRUE(ArtifactTagHandler(&config, NodeCast<Element>(doc->root.get()), &diag_));
- EXPECT_EQ(1ul, config.artifacts.size());
+ EXPECT_THAT(config.artifacts, SizeIs(1ul));
- auto& artifact = config.artifacts.back();
- EXPECT_FALSE(artifact.name); // TODO: make this fail.
- EXPECT_EQ(1, artifact.version);
- EXPECT_EQ("arm", artifact.abi_group.value());
- EXPECT_EQ("large", artifact.screen_density_group.value());
- EXPECT_EQ("europe", artifact.locale_group.value());
- EXPECT_EQ("v19", artifact.android_sdk_group.value());
- EXPECT_EQ("dxt1", artifact.gl_texture_group.value());
- EXPECT_EQ("low-latency", artifact.device_feature_group.value());
- }
-
- {
- // Perform a second action to ensure we get 2 artifacts.
- const auto doc = test::BuildXmlDom(R"xml(
- <artifact
- abi-group="other"
- screen-density-group="large"
- locale-group="europe"
- android-sdk-group="v19"
- gl-texture-group="dxt1"
- device-feature-group="low-latency"/>)xml");
-
- ASSERT_TRUE(artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_));
- EXPECT_EQ(2ul, config.artifacts.size());
- EXPECT_EQ(2, config.artifacts.back().version);
- }
-
- {
- // Perform a third action with a set version code.
- const auto doc = test::BuildXmlDom(R"xml(
- <artifact
- version="5"
- abi-group="other"
- screen-density-group="large"
- locale-group="europe"
- android-sdk-group="v19"
- gl-texture-group="dxt1"
- device-feature-group="low-latency"/>)xml");
-
- ASSERT_TRUE(artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_));
- EXPECT_EQ(3ul, config.artifacts.size());
- EXPECT_EQ(5, config.artifacts.back().version);
- }
-
- {
- // Perform a fourth action to ensure the version code still increments.
- const auto doc = test::BuildXmlDom(R"xml(
- <artifact
- abi-group="other"
- screen-density-group="large"
- locale-group="europe"
- android-sdk-group="v19"
- gl-texture-group="dxt1"
- device-feature-group="low-latency"/>)xml");
-
- ASSERT_TRUE(artifact_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_));
- EXPECT_EQ(4ul, config.artifacts.size());
- EXPECT_EQ(6, config.artifacts.back().version);
- }
-}
-
-TEST_F(ConfigurationParserTest, DuplicateArtifactVersion) {
- static constexpr const char* configuration = R"xml(<?xml version="1.0" encoding="utf-8" ?>
- <pst-process xmlns="http://schemas.android.com/tools/aapt">>
- <artifacts>
- <artifact-format>
- ${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release
- </artifact-format>
- <artifact
- name="art1"
- abi-group="arm"
- screen-density-group="large"
- locale-group="europe"
- android-sdk-group="v19"
- gl-texture-group="dxt1"
- device-feature-group="low-latency"/>
- <artifact
- name="art2"
- version = "1"
- abi-group="other"
- screen-density-group="alldpi"
- locale-group="north-america"
- android-sdk-group="v19"
- gl-texture-group="dxt1"
- device-feature-group="low-latency"/>
- </artifacts>
- </post-process>)xml";
- auto result = ConfigurationParser::ForContents(configuration).Parse();
- ASSERT_FALSE(result);
+ auto& artifact = config.artifacts.back();
+ EXPECT_FALSE(artifact.name); // TODO: make this fail.
+ EXPECT_EQ("arm", artifact.abi_group.value());
+ EXPECT_EQ("large", artifact.screen_density_group.value());
+ EXPECT_EQ("europe", artifact.locale_group.value());
+ EXPECT_EQ("v19", artifact.android_sdk.value());
+ EXPECT_EQ("dxt1", artifact.gl_texture_group.value());
+ EXPECT_EQ("low-latency", artifact.device_feature_group.value());
}
TEST_F(ConfigurationParserTest, ArtifactFormatAction) {
@@ -296,7 +381,7 @@
</artifact-format>)xml");
PostProcessingConfiguration config;
- bool ok = artifact_format_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ bool ok = ArtifactFormatTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
ASSERT_TRUE(ok);
ASSERT_TRUE(config.artifact_format);
EXPECT_EQ(
@@ -319,16 +404,42 @@
auto doc = test::BuildXmlDom(xml);
PostProcessingConfiguration config;
- bool ok = abi_group_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ bool ok = AbiGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
ASSERT_TRUE(ok);
- EXPECT_EQ(1ul, config.abi_groups.size());
+ EXPECT_THAT(config.abi_groups, SizeIs(1ul));
ASSERT_EQ(1u, config.abi_groups.count("arm"));
- auto& out = config.abi_groups["arm"];
+ auto& out = config.abi_groups["arm"].entry;
ASSERT_THAT(out, ElementsAre(Abi::kArmV7a, Abi::kArm64V8a));
}
+TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup) {
+ static constexpr const char* xml = R"xml(<abi-group label="arm64-v8a"/>)xml";
+
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = AbiGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_TRUE(ok);
+
+ EXPECT_THAT(config.abi_groups, SizeIs(1ul));
+ ASSERT_EQ(1u, config.abi_groups.count("arm64-v8a"));
+
+ auto& out = config.abi_groups["arm64-v8a"].entry;
+ ASSERT_THAT(out, ElementsAre(Abi::kArm64V8a));
+}
+
+TEST_F(ConfigurationParserTest, AbiGroupAction_InvalidEmptyGroup) {
+ static constexpr const char* xml = R"xml(<abi-group label="arm"/>)xml";
+
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = AbiGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_FALSE(ok);
+}
+
TEST_F(ConfigurationParserTest, ScreenDensityGroupAction) {
static constexpr const char* xml = R"xml(
<screen-density-group label="large">
@@ -342,11 +453,10 @@
auto doc = test::BuildXmlDom(xml);
PostProcessingConfiguration config;
- bool ok =
- screen_density_group_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ bool ok = ScreenDensityGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
ASSERT_TRUE(ok);
- EXPECT_EQ(1ul, config.screen_density_groups.size());
+ EXPECT_THAT(config.screen_density_groups, SizeIs(1ul));
ASSERT_EQ(1u, config.screen_density_groups.count("large"));
ConfigDescription xhdpi;
@@ -356,10 +466,39 @@
ConfigDescription xxxhdpi;
xxxhdpi.density = ResTable_config::DENSITY_XXXHIGH;
- auto& out = config.screen_density_groups["large"];
+ auto& out = config.screen_density_groups["large"].entry;
ASSERT_THAT(out, ElementsAre(xhdpi, xxhdpi, xxxhdpi));
}
+TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup) {
+ static constexpr const char* xml = R"xml(<screen-density-group label="xhdpi"/>)xml";
+
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = ScreenDensityGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_TRUE(ok);
+
+ EXPECT_THAT(config.screen_density_groups, SizeIs(1ul));
+ ASSERT_EQ(1u, config.screen_density_groups.count("xhdpi"));
+
+ ConfigDescription xhdpi;
+ xhdpi.density = ResTable_config::DENSITY_XHIGH;
+
+ auto& out = config.screen_density_groups["xhdpi"].entry;
+ ASSERT_THAT(out, ElementsAre(xhdpi));
+}
+
+TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_InvalidEmtpyGroup) {
+ static constexpr const char* xml = R"xml(<screen-density-group label="really-big-screen"/>)xml";
+
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = ScreenDensityGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_FALSE(ok);
+}
+
TEST_F(ConfigurationParserTest, LocaleGroupAction) {
static constexpr const char* xml = R"xml(
<locale-group label="europe">
@@ -372,13 +511,13 @@
auto doc = test::BuildXmlDom(xml);
PostProcessingConfiguration config;
- bool ok = locale_group_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ bool ok = LocaleGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
ASSERT_TRUE(ok);
ASSERT_EQ(1ul, config.locale_groups.size());
ASSERT_EQ(1u, config.locale_groups.count("europe"));
- const auto& out = config.locale_groups["europe"];
+ const auto& out = config.locale_groups["europe"].entry;
ConfigDescription en = test::ParseConfigOrDie("en");
ConfigDescription es = test::ParseConfigOrDie("es");
@@ -388,29 +527,56 @@
ASSERT_THAT(out, ElementsAre(en, es, fr, de));
}
+TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup) {
+ static constexpr const char* xml = R"xml(<locale-group label="en"/>)xml";
+
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = LocaleGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_TRUE(ok);
+
+ ASSERT_EQ(1ul, config.locale_groups.size());
+ ASSERT_EQ(1u, config.locale_groups.count("en"));
+
+ const auto& out = config.locale_groups["en"].entry;
+
+ ConfigDescription en = test::ParseConfigOrDie("en");
+
+ ASSERT_THAT(out, ElementsAre(en));
+}
+
+TEST_F(ConfigurationParserTest, LocaleGroupAction_InvalidEmtpyGroup) {
+ static constexpr const char* xml = R"xml(<locale-group label="arm64"/>)xml";
+
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = LocaleGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_FALSE(ok);
+}
+
TEST_F(ConfigurationParserTest, AndroidSdkGroupAction) {
static constexpr const char* xml = R"xml(
- <android-sdk-group label="v19">
- <android-sdk
+ <android-sdk label="v19"
minSdkVersion="19"
targetSdkVersion="24"
maxSdkVersion="25">
<manifest>
<!--- manifest additions here XSLT? TODO -->
</manifest>
- </android-sdk>
- </android-sdk-group>)xml";
+ </android-sdk>)xml";
auto doc = test::BuildXmlDom(xml);
PostProcessingConfiguration config;
- bool ok = android_sdk_group_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
ASSERT_TRUE(ok);
- ASSERT_EQ(1ul, config.android_sdk_groups.size());
- ASSERT_EQ(1u, config.android_sdk_groups.count("v19"));
+ ASSERT_EQ(1ul, config.android_sdks.size());
+ ASSERT_EQ(1u, config.android_sdks.count("v19"));
- auto& out = config.android_sdk_groups["v19"];
+ auto& out = config.android_sdks["v19"];
AndroidSdk sdk;
sdk.min_sdk_version = 19;
@@ -421,31 +587,108 @@
ASSERT_EQ(sdk, out);
}
-TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_NonNumeric) {
+TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_SingleVersion) {
+ {
+ const char* xml = "<android-sdk label='v19' minSdkVersion='19'></android-sdk>";
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_TRUE(ok);
+
+ ASSERT_EQ(1ul, config.android_sdks.size());
+ ASSERT_EQ(1u, config.android_sdks.count("v19"));
+
+ auto& out = config.android_sdks["v19"];
+ EXPECT_EQ(19, out.min_sdk_version);
+ EXPECT_FALSE(out.max_sdk_version);
+ EXPECT_FALSE(out.target_sdk_version);
+ }
+
+ {
+ const char* xml =
+ "<android-sdk label='v19' minSdkVersion='19' maxSdkVersion='19'></android-sdk>";
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_TRUE(ok);
+
+ ASSERT_EQ(1ul, config.android_sdks.size());
+ ASSERT_EQ(1u, config.android_sdks.count("v19"));
+
+ auto& out = config.android_sdks["v19"];
+ EXPECT_EQ(19, out.max_sdk_version.value());
+ EXPECT_EQ(19, out.min_sdk_version);
+ EXPECT_FALSE(out.target_sdk_version);
+ }
+
+ {
+ const char* xml =
+ "<android-sdk label='v19' minSdkVersion='19' targetSdkVersion='19'></android-sdk>";
+ auto doc = test::BuildXmlDom(xml);
+
+ PostProcessingConfiguration config;
+ bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_TRUE(ok);
+
+ ASSERT_EQ(1ul, config.android_sdks.size());
+ ASSERT_EQ(1u, config.android_sdks.count("v19"));
+
+ auto& out = config.android_sdks["v19"];
+ EXPECT_EQ(19, out.target_sdk_version.value());
+ EXPECT_EQ(19, out.min_sdk_version);
+ EXPECT_FALSE(out.max_sdk_version);
+ }
+}
+
+TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_InvalidVersion) {
static constexpr const char* xml = R"xml(
- <android-sdk-group label="P">
- <android-sdk
- minSdkVersion="M"
- targetSdkVersion="P"
- maxSdkVersion="P">
- </android-sdk>
- </android-sdk-group>)xml";
+ <android-sdk
+ label="v19"
+ minSdkVersion="v19"
+ targetSdkVersion="v24"
+ maxSdkVersion="v25">
+ <manifest>
+ <!--- manifest additions here XSLT? TODO -->
+ </manifest>
+ </android-sdk>)xml";
auto doc = test::BuildXmlDom(xml);
PostProcessingConfiguration config;
- bool ok = android_sdk_group_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ ASSERT_FALSE(ok);
+}
+
+TEST_F(ConfigurationParserTest, AndroidSdkGroupAction_NonNumeric) {
+ static constexpr const char* xml = R"xml(
+ <android-sdk
+ label="P"
+ minSdkVersion="25"
+ targetSdkVersion="%s"
+ maxSdkVersion="%s">
+ </android-sdk>)xml";
+
+ const auto& dev_sdk = GetDevelopmentSdkCodeNameAndVersion();
+ const char* codename = dev_sdk.first.data();
+ const ApiVersion& version = dev_sdk.second;
+
+ auto doc = test::BuildXmlDom(StringPrintf(xml, codename, codename));
+
+ PostProcessingConfiguration config;
+ bool ok = AndroidSdkTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
ASSERT_TRUE(ok);
- ASSERT_EQ(1ul, config.android_sdk_groups.size());
- ASSERT_EQ(1u, config.android_sdk_groups.count("P"));
+ ASSERT_EQ(1ul, config.android_sdks.size());
+ ASSERT_EQ(1u, config.android_sdks.count("P"));
- auto& out = config.android_sdk_groups["P"];
+ auto& out = config.android_sdks["P"];
AndroidSdk sdk;
- sdk.min_sdk_version = {}; // Only the latest development version is supported.
- sdk.target_sdk_version = 28;
- sdk.max_sdk_version = 28;
+ sdk.min_sdk_version = 25;
+ sdk.target_sdk_version = version;
+ sdk.max_sdk_version = version;
ASSERT_EQ(sdk, out);
}
@@ -464,13 +707,13 @@
auto doc = test::BuildXmlDom(xml);
PostProcessingConfiguration config;
- bool ok = gl_texture_group_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ bool ok = GlTextureGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
ASSERT_TRUE(ok);
- EXPECT_EQ(1ul, config.gl_texture_groups.size());
+ EXPECT_THAT(config.gl_texture_groups, SizeIs(1ul));
ASSERT_EQ(1u, config.gl_texture_groups.count("dxt1"));
- auto& out = config.gl_texture_groups["dxt1"];
+ auto& out = config.gl_texture_groups["dxt1"].entry;
GlTexture texture{
std::string("GL_EXT_texture_compression_dxt1"),
@@ -493,14 +736,13 @@
auto doc = test::BuildXmlDom(xml);
PostProcessingConfiguration config;
- bool ok
- = device_feature_group_handler_(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+ bool ok = DeviceFeatureGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
ASSERT_TRUE(ok);
- EXPECT_EQ(1ul, config.device_feature_groups.size());
+ EXPECT_THAT(config.device_feature_groups, SizeIs(1ul));
ASSERT_EQ(1u, config.device_feature_groups.count("low-latency"));
- auto& out = config.device_feature_groups["low-latency"];
+ auto& out = config.device_feature_groups["low-latency"].entry;
DeviceFeature low_latency = "android.hardware.audio.low_latency";
DeviceFeature pro = "android.hardware.audio.pro";
@@ -511,14 +753,14 @@
TEST(ArtifactTest, Simple) {
StdErrDiagnostics diag;
- Artifact x86;
+ ConfiguredArtifact x86;
x86.abi_group = {"x86"};
auto x86_result = x86.ToArtifactName("something.${abi}.apk", "", &diag);
ASSERT_TRUE(x86_result);
EXPECT_EQ(x86_result.value(), "something.x86.apk");
- Artifact arm;
+ ConfiguredArtifact arm;
arm.abi_group = {"armeabi-v7a"};
{
@@ -548,13 +790,13 @@
TEST(ArtifactTest, Complex) {
StdErrDiagnostics diag;
- Artifact artifact;
+ ConfiguredArtifact artifact;
artifact.abi_group = {"mips64"};
artifact.screen_density_group = {"ldpi"};
artifact.device_feature_group = {"df1"};
artifact.gl_texture_group = {"glx1"};
artifact.locale_group = {"en-AU"};
- artifact.android_sdk_group = {"v26"};
+ artifact.android_sdk = {"v26"};
{
auto result = artifact.ToArtifactName(
@@ -594,7 +836,7 @@
TEST(ArtifactTest, Missing) {
StdErrDiagnostics diag;
- Artifact x86;
+ ConfiguredArtifact x86;
x86.abi_group = {"x86"};
EXPECT_FALSE(x86.ToArtifactName("something.${density}.apk", "", &diag));
@@ -605,7 +847,7 @@
TEST(ArtifactTest, Empty) {
StdErrDiagnostics diag;
- Artifact artifact;
+ ConfiguredArtifact artifact;
EXPECT_FALSE(artifact.ToArtifactName("something.${density}.apk", "", &diag));
EXPECT_TRUE(artifact.ToArtifactName("something.apk", "", &diag));
@@ -615,7 +857,7 @@
TEST(ArtifactTest, Repeated) {
StdErrDiagnostics diag;
- Artifact artifact;
+ ConfiguredArtifact artifact;
artifact.screen_density_group = {"mdpi"};
ASSERT_TRUE(artifact.ToArtifactName("something.${density}.apk", "", &diag));
@@ -625,7 +867,7 @@
TEST(ArtifactTest, Nesting) {
StdErrDiagnostics diag;
- Artifact x86;
+ ConfiguredArtifact x86;
x86.abi_group = {"x86"};
EXPECT_FALSE(x86.ToArtifactName("something.${abi${density}}.apk", "", &diag));
@@ -637,7 +879,7 @@
TEST(ArtifactTest, Recursive) {
StdErrDiagnostics diag;
- Artifact artifact;
+ ConfiguredArtifact artifact;
artifact.device_feature_group = {"${gl}"};
artifact.gl_texture_group = {"glx1"};
@@ -663,4 +905,6 @@
}
} // namespace
+} // namespace handler
+} // namespace configuration
} // namespace aapt
diff --git a/tools/aapt2/configuration/aapt2.xsd b/tools/aapt2/configuration/aapt2.xsd
index 134153a..fb2f49b 100644
--- a/tools/aapt2/configuration/aapt2.xsd
+++ b/tools/aapt2/configuration/aapt2.xsd
@@ -8,22 +8,52 @@
<xsd:element name="post-process">
<xsd:complexType>
<xsd:sequence>
- <xsd:element name="groups" type="groups"/>
<xsd:element name="artifacts" type="artifacts"/>
+ <xsd:element name="android-sdks" type="android-sdks"/>
+ <xsd:element name="abi-groups" type="abi-groups"/>
+ <xsd:element name="screen-density-groups" type="screen-density-groups"/>
+ <xsd:element name="locale-groups" type="locale-groups"/>
+ <xsd:element name="gl-texture-groups" type="gl-texture-groups"/>
+ <xsd:element name="device-feature-groups" type="device-feature-groups"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
- <xsd:complexType name="groups">
+ <xsd:complexType name="android-sdks">
+ <xsd:sequence>
+ <xsd:element name="android-sdk" type="android-sdk" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:complexType name="abi-groups">
<xsd:sequence>
<xsd:element name="abi-group" type="abi-group" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:complexType name="screen-density-groups">
+ <xsd:sequence>
<xsd:element name="screen-density-group" type="screen-density-group" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:complexType name="locale-groups">
+ <xsd:sequence>
<xsd:element name="locale-group" type="locale-group" maxOccurs="unbounded"/>
- <xsd:element name="android-sdk-group" type="android-sdk-group" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:complexType name="gl-texture-groups">
+ <xsd:sequence>
<xsd:element
name="gl-texture-group"
type="gl-texture-group"
maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:complexType name="device-feature-groups">
+ <xsd:sequence>
<xsd:element name="device-feature-group" type="device-feature-group" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
@@ -38,8 +68,6 @@
<!-- Groups output artifacts together by dimension labels. -->
<xsd:complexType name="artifact">
- <xsd:attribute name="name" type="xsd:string"/>
- <xsd:attribute name="version" type="xsd:integer"/>
<xsd:attribute name="abi-group" type="xsd:string"/>
<xsd:attribute name="android-sdk-group" type="xsd:string"/>
<xsd:attribute name="device-feature-group" type="xsd:string"/>
@@ -52,7 +80,7 @@
<xsd:sequence>
<xsd:element name="gl-texture" type="gl-texture" maxOccurs="unbounded"/>
</xsd:sequence>
- <xsd:attribute name="label" type="xsd:string" use="optional"/>
+ <xsd:attribute name="label" type="xsd:string"/>
</xsd:complexType>
<xsd:complexType name="gl-texture">
@@ -66,14 +94,14 @@
<xsd:sequence>
<xsd:element name="supports-feature" type="xsd:string" maxOccurs="unbounded"/>
</xsd:sequence>
- <xsd:attribute name="label" type="xsd:string" use="optional"/>
+ <xsd:attribute name="label" type="xsd:string"/>
</xsd:complexType>
<xsd:complexType name="abi-group">
<xsd:sequence>
<xsd:element name="abi" type="abi-name" maxOccurs="unbounded"/>
</xsd:sequence>
- <xsd:attribute name="label" type="xsd:string" use="optional"/>
+ <xsd:attribute name="label" type="xsd:string"/>
</xsd:complexType>
<xsd:simpleType name="abi-name">
@@ -93,7 +121,7 @@
<xsd:sequence>
<xsd:element name="screen-density" type="screen-density" maxOccurs="unbounded"/>
</xsd:sequence>
- <xsd:attribute name="label" type="xsd:string" use="optional"/>
+ <xsd:attribute name="label" type="xsd:string"/>
</xsd:complexType>
<xsd:simpleType name="screen-density">
@@ -108,20 +136,14 @@
</xsd:restriction>
</xsd:simpleType>
- <xsd:complexType name="android-sdk-group">
- <xsd:sequence>
- <xsd:element name="android-sdk" type="android-sdk" maxOccurs="unbounded"/>
- </xsd:sequence>
- <xsd:attribute name="label" type="xsd:string" use="optional"/>
- </xsd:complexType>
-
<xsd:complexType name="android-sdk">
<!-- TODO(safarmer): Add permissions to add/remove. -->
<!-- TODO(safarmer): Add option for uncompressed native libs. -->
<xsd:sequence>
<xsd:element name="manifest" type="manifest"/>
</xsd:sequence>
- <xsd:attribute name="minSdkVersion" type="xsd:integer"/>
+ <xsd:attribute name="label" type="xsd:string" use="required"/>
+ <xsd:attribute name="minSdkVersion" type="xsd:integer" use="required"/>
<xsd:attribute name="targetSdkVersion" type="xsd:integer"/>
<xsd:attribute name="maxSdkVersion" type="xsd:integer"/>
</xsd:complexType>
@@ -135,7 +157,7 @@
<xsd:sequence>
<xsd:element name="locale" type="locale" maxOccurs="unbounded"/>
</xsd:sequence>
- <xsd:attribute name="label" type="xsd:string" use="optional"/>
+ <xsd:attribute name="label" type="xsd:string"/>
</xsd:complexType>
<xsd:complexType name="locale">
diff --git a/tools/aapt2/configuration/example/config.xml b/tools/aapt2/configuration/example/config.xml
index ce31e61..d8aba09 100644
--- a/tools/aapt2/configuration/example/config.xml
+++ b/tools/aapt2/configuration/example/config.xml
@@ -1,70 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
<post-process xmlns="http://schemas.android.com/tools/aapt">
- <groups>
- <abi-group label="arm">
- <abi>armeabi-v7a</abi>
- <abi>arm64-v8a</abi>
- </abi-group>
-
- <abi-group label="other">
- <abi>x86</abi>
- <abi>mips</abi>
- </abi-group>
-
- <screen-density-group label="large">
- <screen-density>xhdpi</screen-density>
- <screen-density>xxhdpi</screen-density>
- <screen-density>xxxhdpi</screen-density>
- </screen-density-group>
-
- <screen-density-group label="alldpi">
- <screen-density>ldpi</screen-density>
- <screen-density>mdpi</screen-density>
- <screen-density>hdpi</screen-density>
- <screen-density>xhdpi</screen-density>
- <screen-density>xxhdpi</screen-density>
- <screen-density>xxxhdpi</screen-density>
- </screen-density-group>
-
- <locale-group label="europe">
- <locale lang="en"/>
- <locale lang="es"/>
- <locale lang="fr"/>
- <locale lang="de" compressed="true"/>
- </locale-group>
-
- <locale-group label="north-america">
- <locale lang="en"/>
- <locale lang="es" region="MX"/>
- <locale lang="fr" region="CA" compressed="true"/>
- </locale-group>
-
- <locale-group label="all">
- <locale compressed="true"/>
- </locale-group>
-
- <android-sdk-group label="19">
- <android-sdk
- minSdkVersion="19"
- targetSdkVersion="24"
- maxSdkVersion="25">
- <manifest>
- <!--- manifest additions here XSLT? TODO -->
- </manifest>
- </android-sdk>
- </android-sdk-group>
-
- <gl-texture-group label="dxt1">
- <gl-texture name="GL_EXT_texture_compression_dxt1">
- <texture-path>assets/dxt1/*</texture-path>
- </gl-texture>
- </gl-texture-group>
-
- <device-feature-group label="low-latency">
- <supports-feature>android.hardware.audio.low_latency</supports-feature>
- </device-feature-group>
- </groups>
-
<artifacts>
<artifact-format>
${base}.${abi}.${screen-density}.${locale}.${sdk}.${gl}.${feature}.release
@@ -87,4 +22,79 @@
device-feature-group="low-latency"/>
</artifacts>
+
+ <android-sdks>
+ <android-sdk
+ label="19"
+ minSdkVersion="19"
+ targetSdkVersion="24"
+ maxSdkVersion="25">
+ <manifest>
+ <!--- manifest additions here XSLT? TODO -->
+ </manifest>
+ </android-sdk>
+ </android-sdks>
+
+ <abi-groups>
+ <abi-group label="arm">
+ <abi>armeabi-v7a</abi>
+ <abi>arm64-v8a</abi>
+ </abi-group>
+
+ <abi-group label="other">
+ <abi>x86</abi>
+ <abi>mips</abi>
+ </abi-group>
+ </abi-groups>
+
+ <screen-density-groups>
+ <screen-density-group label="large">
+ <screen-density>xhdpi</screen-density>
+ <screen-density>xxhdpi</screen-density>
+ <screen-density>xxxhdpi</screen-density>
+ </screen-density-group>
+
+ <screen-density-group label="alldpi">
+ <screen-density>ldpi</screen-density>
+ <screen-density>mdpi</screen-density>
+ <screen-density>hdpi</screen-density>
+ <screen-density>xhdpi</screen-density>
+ <screen-density>xxhdpi</screen-density>
+ <screen-density>xxxhdpi</screen-density>
+ </screen-density-group>
+ </screen-density-groups>
+
+ <locale-groups>
+ <locale-group label="europe">
+ <locale lang="en"/>
+ <locale lang="es"/>
+ <locale lang="fr"/>
+ <locale lang="de" compressed="true"/>
+ </locale-group>
+
+ <locale-group label="north-america">
+ <locale lang="en"/>
+ <locale lang="es" region="MX"/>
+ <locale lang="fr" region="CA" compressed="true"/>
+ </locale-group>
+
+ <locale-group label="all">
+ <locale compressed="true"/>
+ </locale-group>
+ </locale-groups>
+
+ <gl-texture-groups>
+ <gl-texture-group label="dxt1">
+ <gl-texture name="GL_EXT_texture_compression_dxt1">
+ <texture-path>assets/dxt1/*</texture-path>
+ </gl-texture>
+ </gl-texture-group>
+ </gl-texture-groups>
+
+ <device-feature-groups>
+ <device-feature-group label="low-latency">
+ <supports-feature>android.hardware.audio.low_latency</supports-feature>
+ </device-feature-group>
+ </device-feature-groups>
+
</post-process>
diff --git a/tools/aapt2/filter/AbiFilter.cpp b/tools/aapt2/filter/AbiFilter.cpp
index cb96235..9ace82a 100644
--- a/tools/aapt2/filter/AbiFilter.cpp
+++ b/tools/aapt2/filter/AbiFilter.cpp
@@ -25,7 +25,7 @@
std::unique_ptr<AbiFilter> AbiFilter::FromAbiList(const std::vector<configuration::Abi>& abi_list) {
std::unordered_set<std::string> abi_set;
for (auto& abi : abi_list) {
- abi_set.insert(configuration::AbiToString(abi));
+ abi_set.insert(configuration::AbiToString(abi).to_string());
}
// Make unique by hand as the constructor is private.
return std::unique_ptr<AbiFilter>(new AbiFilter(abi_set));
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 5078678..8d079ff 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -223,7 +223,7 @@
break;
case android::RES_TABLE_TYPE_SPEC_TYPE:
- if (!ParseTypeSpec(parser.chunk())) {
+ if (!ParseTypeSpec(package, parser.chunk())) {
return false;
}
break;
@@ -260,7 +260,8 @@
return true;
}
-bool BinaryResourceParser::ParseTypeSpec(const ResChunk_header* chunk) {
+bool BinaryResourceParser::ParseTypeSpec(const ResourceTablePackage* package,
+ const ResChunk_header* chunk) {
if (type_pool_.getError() != NO_ERROR) {
diag_->Error(DiagMessage(source_) << "missing type string pool");
return false;
@@ -276,6 +277,34 @@
diag_->Error(DiagMessage(source_) << "ResTable_typeSpec has invalid id: " << type_spec->id);
return false;
}
+
+ // The data portion of this chunk contains entry_count 32bit entries,
+ // each one representing a set of flags.
+ const size_t entry_count = dtohl(type_spec->entryCount);
+
+ // There can only be 2^16 entries in a type, because that is the ID
+ // space for entries (EEEE) in the resource ID 0xPPTTEEEE.
+ if (entry_count > std::numeric_limits<uint16_t>::max()) {
+ diag_->Error(DiagMessage(source_)
+ << "ResTable_typeSpec has too many entries (" << entry_count << ")");
+ return false;
+ }
+
+ const size_t data_size = util::DeviceToHost32(type_spec->header.size) -
+ util::DeviceToHost16(type_spec->header.headerSize);
+ if (entry_count * sizeof(uint32_t) > data_size) {
+ diag_->Error(DiagMessage(source_) << "ResTable_typeSpec too small to hold entries.");
+ return false;
+ }
+
+ // Record the type_spec_flags for later. We don't know resource names yet, and we need those
+ // to mark resources as overlayable.
+ const uint32_t* type_spec_flags = reinterpret_cast<const uint32_t*>(
+ reinterpret_cast<uintptr_t>(type_spec) + util::DeviceToHost16(type_spec->header.headerSize));
+ for (size_t i = 0; i < entry_count; i++) {
+ ResourceId id(package->id.value_or_default(0x0), type_spec->id, static_cast<size_t>(i));
+ entry_type_spec_flags_[id] = util::DeviceToHost32(type_spec_flags[i]);
+ }
return true;
}
@@ -346,18 +375,34 @@
return false;
}
- if (!table_->AddResourceAllowMangled(name, res_id, config, {}, std::move(resource_value),
- diag_)) {
+ if (!table_->AddResourceWithIdMangled(name, res_id, config, {}, std::move(resource_value),
+ diag_)) {
return false;
}
- if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) {
- Symbol symbol;
- symbol.state = SymbolState::kPublic;
- symbol.source = source_.WithLine(0);
- if (!table_->SetSymbolStateAllowMangled(name, res_id, symbol, diag_)) {
- return false;
+ const uint32_t type_spec_flags = entry_type_spec_flags_[res_id];
+ if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0 ||
+ (type_spec_flags & ResTable_typeSpec::SPEC_OVERLAYABLE) != 0) {
+ if (entry->flags & ResTable_entry::FLAG_PUBLIC) {
+ Visibility visibility;
+ visibility.level = Visibility::Level::kPublic;
+ visibility.source = source_.WithLine(0);
+ if (!table_->SetVisibilityWithIdMangled(name, visibility, res_id, diag_)) {
+ return false;
+ }
}
+
+ if (type_spec_flags & ResTable_typeSpec::SPEC_OVERLAYABLE) {
+ Overlayable overlayable;
+ overlayable.source = source_.WithLine(0);
+ if (!table_->SetOverlayableMangled(name, overlayable, diag_)) {
+ return false;
+ }
+ }
+
+ // Erase the ID from the map once processed, so that we don't mark the same symbol more than
+ // once.
+ entry_type_spec_flags_.erase(res_id);
}
// Add this resource name->id mapping to the index so
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.h b/tools/aapt2/format/binary/BinaryResourceParser.h
index 052f806..a1f9f83 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.h
+++ b/tools/aapt2/format/binary/BinaryResourceParser.h
@@ -50,7 +50,7 @@
bool ParseTable(const android::ResChunk_header* chunk);
bool ParsePackage(const android::ResChunk_header* chunk);
- bool ParseTypeSpec(const android::ResChunk_header* chunk);
+ bool ParseTypeSpec(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
bool ParseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
bool ParseLibrary(const android::ResChunk_header* chunk);
@@ -105,6 +105,9 @@
// A mapping of resource ID to resource name. When we finish parsing
// we use this to convert all resource IDs to symbolic references.
std::map<ResourceId, ResourceName> id_index_;
+
+ // A mapping of resource ID to type spec flags.
+ std::unordered_map<ResourceId, uint32_t> entry_type_spec_flags_;
};
} // namespace aapt
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index a3034df..24a4112 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -283,7 +283,7 @@
T* result = buffer->NextBlock<T>();
ResTable_entry* out_entry = (ResTable_entry*)result;
- if (entry->entry->symbol_status.state == SymbolState::kPublic) {
+ if (entry->entry->visibility.level == Visibility::Level::kPublic) {
out_entry->flags |= ResTable_entry::FLAG_PUBLIC;
}
@@ -443,10 +443,15 @@
// Populate the config masks for this entry.
- if (entry->symbol_status.state == SymbolState::kPublic) {
+ if (entry->visibility.level == Visibility::Level::kPublic) {
config_masks[entry->id.value()] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
}
+ if (entry->overlayable) {
+ config_masks[entry->id.value()] |=
+ util::HostToDevice32(ResTable_typeSpec::SPEC_OVERLAYABLE);
+ }
+
const size_t config_count = entry->values.size();
for (size_t i = 0; i < config_count; i++) {
const ConfigDescription& config = entry->values[i]->config;
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index f0b80d2..51ccdc7 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -26,6 +26,7 @@
using namespace android;
+using ::testing::Gt;
using ::testing::IsNull;
using ::testing::NotNull;
@@ -250,15 +251,15 @@
const ResourceId resid(context->GetPackageId(), 0x02, static_cast<uint16_t>(i));
const auto value =
util::make_unique<BinaryPrimitive>(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(i));
- CHECK(table->AddResource(name, resid, ConfigDescription::DefaultConfig(), "",
- std::unique_ptr<Value>(value->Clone(nullptr)),
- context->GetDiagnostics()));
+ CHECK(table->AddResourceWithId(name, resid, ConfigDescription::DefaultConfig(), "",
+ std::unique_ptr<Value>(value->Clone(nullptr)),
+ context->GetDiagnostics()));
// Every few entries, write out a sparse_config value. This will give us the desired load.
if (i % stride == 0) {
- CHECK(table->AddResource(name, resid, sparse_config, "",
- std::unique_ptr<Value>(value->Clone(nullptr)),
- context->GetDiagnostics()));
+ CHECK(table->AddResourceWithId(name, resid, sparse_config, "",
+ std::unique_ptr<Value>(value->Clone(nullptr)),
+ context->GetDiagnostics()));
}
}
return table;
@@ -568,4 +569,25 @@
ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u));
}
+TEST_F(TableFlattenerTest, FlattenOverlayable) {
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.test", 0x7f)
+ .AddSimple("com.app.test:integer/overlayable", ResourceId(0x7f020000))
+ .Build();
+
+ ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.test:integer/overlayable"),
+ Overlayable{}, test::GetDiagnostics()));
+
+ ResTable res_table;
+ ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table));
+
+ const StringPiece16 overlayable_name(u"com.app.test:integer/overlayable");
+ uint32_t spec_flags = 0u;
+ ASSERT_THAT(res_table.identifierForName(overlayable_name.data(), overlayable_name.size(), nullptr,
+ 0u, nullptr, 0u, &spec_flags),
+ Gt(0u));
+ EXPECT_TRUE(spec_flags & android::ResTable_typeSpec::SPEC_OVERLAYABLE);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 0f0bce8..3d6975d 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -358,16 +358,16 @@
out_source->line = static_cast<size_t>(pb_source.position().line_number());
}
-static SymbolState DeserializeVisibilityFromPb(const pb::SymbolStatus_Visibility& pb_visibility) {
- switch (pb_visibility) {
- case pb::SymbolStatus_Visibility_PRIVATE:
- return SymbolState::kPrivate;
- case pb::SymbolStatus_Visibility_PUBLIC:
- return SymbolState::kPublic;
+static Visibility::Level DeserializeVisibilityFromPb(const pb::Visibility::Level& pb_level) {
+ switch (pb_level) {
+ case pb::Visibility::PRIVATE:
+ return Visibility::Level::kPrivate;
+ case pb::Visibility::PUBLIC:
+ return Visibility::Level::kPublic;
default:
break;
}
- return SymbolState::kUndefined;
+ return Visibility::Level::kUndefined;
}
static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStringPool& src_pool,
@@ -402,28 +402,48 @@
}
// Deserialize the symbol status (public/private with source and comments).
- if (pb_entry.has_symbol_status()) {
- const pb::SymbolStatus& pb_status = pb_entry.symbol_status();
- if (pb_status.has_source()) {
- DeserializeSourceFromPb(pb_status.source(), src_pool, &entry->symbol_status.source);
+ if (pb_entry.has_visibility()) {
+ const pb::Visibility& pb_visibility = pb_entry.visibility();
+ if (pb_visibility.has_source()) {
+ DeserializeSourceFromPb(pb_visibility.source(), src_pool, &entry->visibility.source);
}
+ entry->visibility.comment = pb_visibility.comment();
- entry->symbol_status.comment = pb_status.comment();
- entry->symbol_status.allow_new = pb_status.allow_new();
-
- const SymbolState visibility = DeserializeVisibilityFromPb(pb_status.visibility());
- entry->symbol_status.state = visibility;
- if (visibility == SymbolState::kPublic) {
+ const Visibility::Level level = DeserializeVisibilityFromPb(pb_visibility.level());
+ entry->visibility.level = level;
+ if (level == Visibility::Level::kPublic) {
// Propagate the public visibility up to the Type.
- type->symbol_status.state = SymbolState::kPublic;
- } else if (visibility == SymbolState::kPrivate) {
+ type->visibility_level = Visibility::Level::kPublic;
+ } else if (level == Visibility::Level::kPrivate) {
// Only propagate if no previous state was assigned.
- if (type->symbol_status.state == SymbolState::kUndefined) {
- type->symbol_status.state = SymbolState::kPrivate;
+ if (type->visibility_level == Visibility::Level::kUndefined) {
+ type->visibility_level = Visibility::Level::kPrivate;
}
}
}
+ if (pb_entry.has_allow_new()) {
+ const pb::AllowNew& pb_allow_new = pb_entry.allow_new();
+
+ AllowNew allow_new;
+ if (pb_allow_new.has_source()) {
+ DeserializeSourceFromPb(pb_allow_new.source(), src_pool, &allow_new.source);
+ }
+ allow_new.comment = pb_allow_new.comment();
+ entry->allow_new = std::move(allow_new);
+ }
+
+ if (pb_entry.has_overlayable()) {
+ const pb::Overlayable& pb_overlayable = pb_entry.overlayable();
+
+ Overlayable overlayable;
+ if (pb_overlayable.has_source()) {
+ DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &overlayable.source);
+ }
+ overlayable.comment = pb_overlayable.comment();
+ entry->overlayable = std::move(overlayable);
+ }
+
ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(),
pb_entry.entry_id().id());
if (resid.is_valid()) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 97ce01a..78f1281 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -43,16 +43,16 @@
}
}
-static pb::SymbolStatus_Visibility SerializeVisibilityToPb(SymbolState state) {
+static pb::Visibility::Level SerializeVisibilityToPb(Visibility::Level state) {
switch (state) {
- case SymbolState::kPrivate:
- return pb::SymbolStatus_Visibility_PRIVATE;
- case SymbolState::kPublic:
- return pb::SymbolStatus_Visibility_PUBLIC;
+ case Visibility::Level::kPrivate:
+ return pb::Visibility::PRIVATE;
+ case Visibility::Level::kPublic:
+ return pb::Visibility::PUBLIC;
default:
break;
}
- return pb::SymbolStatus_Visibility_UNKNOWN;
+ return pb::Visibility::UNKNOWN;
}
void SerializeConfig(const ConfigDescription& config, pb::Configuration* out_pb_config) {
@@ -293,12 +293,26 @@
}
pb_entry->set_name(entry->name);
- // Write the SymbolStatus struct.
- pb::SymbolStatus* pb_status = pb_entry->mutable_symbol_status();
- pb_status->set_visibility(SerializeVisibilityToPb(entry->symbol_status.state));
- SerializeSourceToPb(entry->symbol_status.source, &source_pool, pb_status->mutable_source());
- pb_status->set_comment(entry->symbol_status.comment);
- pb_status->set_allow_new(entry->symbol_status.allow_new);
+ // Write the Visibility struct.
+ pb::Visibility* pb_visibility = pb_entry->mutable_visibility();
+ pb_visibility->set_level(SerializeVisibilityToPb(entry->visibility.level));
+ SerializeSourceToPb(entry->visibility.source, &source_pool,
+ pb_visibility->mutable_source());
+ pb_visibility->set_comment(entry->visibility.comment);
+
+ if (entry->allow_new) {
+ pb::AllowNew* pb_allow_new = pb_entry->mutable_allow_new();
+ SerializeSourceToPb(entry->allow_new.value().source, &source_pool,
+ pb_allow_new->mutable_source());
+ pb_allow_new->set_comment(entry->allow_new.value().comment);
+ }
+
+ if (entry->overlayable) {
+ pb::Overlayable* pb_overlayable = pb_entry->mutable_overlayable();
+ SerializeSourceToPb(entry->overlayable.value().source, &source_pool,
+ pb_overlayable->mutable_source());
+ pb_overlayable->set_comment(entry->overlayable.value().comment);
+ }
for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
pb::ConfigValue* pb_config_value = pb_entry->add_config_value();
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 9649a4d..d7f83fd 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -44,14 +44,15 @@
.AddReference("com.app.a:layout/other", ResourceId(0x7f020001), "com.app.a:layout/main")
.AddString("com.app.a:string/text", {}, "hi")
.AddValue("com.app.a:id/foo", {}, util::make_unique<Id>())
- .SetSymbolState("com.app.a:bool/foo", {}, SymbolState::kUndefined, true /*allow_new*/)
+ .SetSymbolState("com.app.a:bool/foo", {}, Visibility::Level::kUndefined,
+ true /*allow_new*/)
.Build();
- Symbol public_symbol;
- public_symbol.state = SymbolState::kPublic;
- ASSERT_TRUE(table->SetSymbolState(test::ParseNameOrDie("com.app.a:layout/main"),
- ResourceId(0x7f020000), public_symbol,
- context->GetDiagnostics()));
+ Visibility public_symbol;
+ public_symbol.level = Visibility::Level::kPublic;
+ ASSERT_TRUE(table->SetVisibilityWithId(test::ParseNameOrDie("com.app.a:layout/main"),
+ public_symbol, ResourceId(0x7f020000),
+ context->GetDiagnostics()));
Id* id = test::GetValue<Id>(table.get(), "com.app.a:id/foo");
ASSERT_THAT(id, NotNull());
@@ -89,6 +90,10 @@
test::ParseNameOrDie("com.app.a:layout/abc"), ConfigDescription::DefaultConfig(), {},
util::make_unique<Reference>(expected_ref), context->GetDiagnostics()));
+ // Make an overlayable resource.
+ ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"),
+ Overlayable{}, test::GetDiagnostics()));
+
pb::ResourceTable pb_table;
SerializeTableToPb(*table, &pb_table);
@@ -110,13 +115,13 @@
new_table.FindResource(test::ParseNameOrDie("com.app.a:layout/main"));
ASSERT_TRUE(result);
- EXPECT_THAT(result.value().type->symbol_status.state, Eq(SymbolState::kPublic));
- EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kPublic));
+ EXPECT_THAT(result.value().type->visibility_level, Eq(Visibility::Level::kPublic));
+ EXPECT_THAT(result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
ASSERT_TRUE(result);
- EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kUndefined));
- EXPECT_TRUE(result.value().entry->symbol_status.allow_new);
+ EXPECT_THAT(result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+ EXPECT_TRUE(result.value().entry->allow_new);
// Find the product-dependent values
BinaryPrimitive* prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
@@ -148,6 +153,12 @@
EXPECT_THAT(*actual_styled_str->value->spans[0].name, Eq("b"));
EXPECT_THAT(actual_styled_str->value->spans[0].first_char, Eq(0u));
EXPECT_THAT(actual_styled_str->value->spans[0].last_char, Eq(4u));
+
+ Maybe<ResourceTable::SearchResult> search_result =
+ new_table.FindResource(test::ParseNameOrDie("com.app.a:integer/overlayable"));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ EXPECT_TRUE(search_result.value().entry->overlayable);
}
TEST(ProtoSerializeTest, SerializeAndDeserializeXml) {
diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
index 94686c0..2da294c 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
@@ -20,6 +20,7 @@
LOCAL_USE_AAPT2 := true
LOCAL_AAPT_NAMESPACES := true
LOCAL_PACKAGE_NAME := AaptTestNamespace_App
+LOCAL_EXPORT_PACKAGE_RESOURCES := true
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_STATIC_ANDROID_LIBRARIES := \
diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml b/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml
index 1b80d95..174e746 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml
@@ -26,4 +26,4 @@
@com.android.aapt.namespace.libtwo:string/public_string
</item>
</style>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk
new file mode 100644
index 0000000..a35f6ed
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_AAPT_NAMESPACES := true
+LOCAL_PACKAGE_NAME := AaptTestNamespace_Split
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_APK_LIBRARIES := AaptTestNamespace_App
+LOCAL_RES_LIBRARIES := AaptTestNamespace_App
+LOCAL_AAPT_FLAGS := --package-id 0x80 --rename-manifest-package com.android.aapt.namespace.app
+include $(BUILD_PACKAGE)
diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/AndroidManifest.xml b/tools/aapt2/integration-tests/NamespaceTest/Split/AndroidManifest.xml
new file mode 100644
index 0000000..bc9583b
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/Split/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.aapt.namespace.split"
+ featureSplit="split">
+
+ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
+
+ <application>
+ <activity android:name=".SplitActivity" android:theme="@style/ActivityTheme">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/res/values/values.xml b/tools/aapt2/integration-tests/NamespaceTest/Split/res/values/values.xml
new file mode 100644
index 0000000..63da552
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/Split/res/values/values.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <string name="activity_name">Namespace Split</string>
+
+ <style name="ActivityTheme" parent="com.android.aapt.namespace.libone:style/Theme">
+ <item name="android:colorPrimary">#FF9800</item>
+ <item name="android:colorPrimaryDark">#E65100</item>
+ <item name="android:colorAccent">#FFD180</item>
+ </style>
+</resources>
diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/src/com/android/aapt/namespace/split/SplitActivity.java b/tools/aapt2/integration-tests/NamespaceTest/Split/src/com/android/aapt/namespace/split/SplitActivity.java
new file mode 100644
index 0000000..3fff3cf
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/Split/src/com/android/aapt/namespace/split/SplitActivity.java
@@ -0,0 +1,26 @@
+/*
+ * 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.aapt.namespace.split;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SplitActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+}
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 9861770..6b07b1e 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -191,14 +191,14 @@
const JavaClassGeneratorOptions& options)
: context_(context), table_(table), options_(options) {}
-bool JavaClassGenerator::SkipSymbol(SymbolState state) {
+bool JavaClassGenerator::SkipSymbol(Visibility::Level level) {
switch (options_.types) {
case JavaClassGeneratorOptions::SymbolTypes::kAll:
return false;
case JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate:
- return state == SymbolState::kUndefined;
+ return level == Visibility::Level::kUndefined;
case JavaClassGeneratorOptions::SymbolTypes::kPublic:
- return state != SymbolState::kPublic;
+ return level != Visibility::Level::kPublic;
}
return true;
}
@@ -272,7 +272,7 @@
// Build the JavaDoc comment for the Styleable array. This has references to child attributes
// and what possible values can be used for them.
const size_t attr_count = sorted_attributes.size();
- if (attr_count > 0) {
+ if (out_class_def != nullptr && attr_count > 0) {
std::stringstream styleable_comment;
if (!styleable.GetComment().empty()) {
styleable_comment << styleable.GetComment() << "\n";
@@ -356,54 +356,56 @@
continue;
}
- StringPiece comment = styleable_attr.attr_ref->GetComment();
- if (styleable_attr.symbol.value().attribute && comment.empty()) {
- comment = styleable_attr.symbol.value().attribute->GetComment();
+ if (out_class_def != nullptr) {
+ StringPiece comment = styleable_attr.attr_ref->GetComment();
+ if (styleable_attr.symbol.value().attribute && comment.empty()) {
+ comment = styleable_attr.symbol.value().attribute->GetComment();
+ }
+
+ if (comment.contains("@removed")) {
+ // Removed attributes are public but hidden from the documentation, so
+ // don't emit them as part of the class documentation.
+ continue;
+ }
+
+ const ResourceName& attr_name = styleable_attr.attr_ref->name.value();
+
+ StringPiece package_name = attr_name.package;
+ if (package_name.empty()) {
+ package_name = context_->GetCompilationPackage();
+ }
+
+ std::unique_ptr<IntMember> index_member =
+ util::make_unique<IntMember>(sorted_attributes[i].field_name, static_cast<uint32_t>(i));
+
+ AnnotationProcessor* attr_processor = index_member->GetCommentBuilder();
+
+ if (!comment.empty()) {
+ attr_processor->AppendComment("<p>\n@attr description");
+ attr_processor->AppendComment(comment);
+ } else {
+ std::stringstream default_comment;
+ default_comment << "<p>This symbol is the offset where the "
+ << "{@link " << package_name << ".R.attr#"
+ << TransformToFieldName(attr_name.entry) << "}\n"
+ << "attribute's value can be found in the "
+ << "{@link #" << array_field_name << "} array.";
+ attr_processor->AppendComment(default_comment.str());
+ }
+
+ attr_processor->AppendNewLine();
+ AddAttributeFormatDoc(attr_processor, styleable_attr.symbol.value().attribute.get());
+ attr_processor->AppendNewLine();
+ attr_processor->AppendComment(
+ StringPrintf("@attr name %s:%s", package_name.data(), attr_name.entry.data()));
+
+ out_class_def->AddMember(std::move(index_member));
}
- if (comment.contains("@removed")) {
- // Removed attributes are public but hidden from the documentation, so
- // don't emit them as part of the class documentation.
- continue;
- }
-
- const ResourceName& attr_name = styleable_attr.attr_ref->name.value();
-
- StringPiece package_name = attr_name.package;
- if (package_name.empty()) {
- package_name = context_->GetCompilationPackage();
- }
-
- std::unique_ptr<IntMember> index_member = util::make_unique<IntMember>(
- sorted_attributes[i].field_name, static_cast<uint32_t>(i));
-
- AnnotationProcessor* attr_processor = index_member->GetCommentBuilder();
-
- if (!comment.empty()) {
- attr_processor->AppendComment("<p>\n@attr description");
- attr_processor->AppendComment(comment);
- } else {
- std::stringstream default_comment;
- default_comment << "<p>This symbol is the offset where the "
- << "{@link " << package_name << ".R.attr#"
- << TransformToFieldName(attr_name.entry) << "}\n"
- << "attribute's value can be found in the "
- << "{@link #" << array_field_name << "} array.";
- attr_processor->AppendComment(default_comment.str());
- }
-
- attr_processor->AppendNewLine();
- AddAttributeFormatDoc(attr_processor, styleable_attr.symbol.value().attribute.get());
- attr_processor->AppendNewLine();
- attr_processor->AppendComment(
- StringPrintf("@attr name %s:%s", package_name.data(), attr_name.entry.data()));
-
if (r_txt_printer != nullptr) {
r_txt_printer->Println(
StringPrintf("int styleable %s %zd", sorted_attributes[i].field_name.c_str(), i));
}
-
- out_class_def->AddMember(std::move(index_member));
}
// If there is a rewrite method to generate, add the statements that rewrite package IDs
@@ -434,31 +436,33 @@
}
const std::string field_name = TransformToFieldName(name.entry);
- std::unique_ptr<ResourceMember> resource_member =
- util::make_unique<ResourceMember>(field_name, real_id);
+ if (out_class_def != nullptr) {
+ std::unique_ptr<ResourceMember> resource_member =
+ util::make_unique<ResourceMember>(field_name, real_id);
- // Build the comments and annotations for this entry.
- AnnotationProcessor* processor = resource_member->GetCommentBuilder();
+ // Build the comments and annotations for this entry.
+ AnnotationProcessor* processor = resource_member->GetCommentBuilder();
- // Add the comments from any <public> tags.
- if (entry.symbol_status.state != SymbolState::kUndefined) {
- processor->AppendComment(entry.symbol_status.comment);
- }
-
- // Add the comments from all configurations of this entry.
- for (const auto& config_value : entry.values) {
- processor->AppendComment(config_value->value->GetComment());
- }
-
- // If this is an Attribute, append the format Javadoc.
- if (!entry.values.empty()) {
- if (Attribute* attr = ValueCast<Attribute>(entry.values.front()->value.get())) {
- // We list out the available values for the given attribute.
- AddAttributeFormatDoc(processor, attr);
+ // Add the comments from any <public> tags.
+ if (entry.visibility.level != Visibility::Level::kUndefined) {
+ processor->AppendComment(entry.visibility.comment);
}
- }
- out_class_def->AddMember(std::move(resource_member));
+ // Add the comments from all configurations of this entry.
+ for (const auto& config_value : entry.values) {
+ processor->AppendComment(config_value->value->GetComment());
+ }
+
+ // If this is an Attribute, append the format Javadoc.
+ if (!entry.values.empty()) {
+ if (Attribute* attr = ValueCast<Attribute>(entry.values.front()->value.get())) {
+ // We list out the available values for the given attribute.
+ AddAttributeFormatDoc(processor, attr);
+ }
+ }
+
+ out_class_def->AddMember(std::move(resource_member));
+ }
if (r_txt_printer != nullptr) {
r_txt_printer->Print("int ")
@@ -480,7 +484,7 @@
Maybe<std::string> JavaClassGenerator::UnmangleResource(const StringPiece& package_name,
const StringPiece& package_name_to_generate,
const ResourceEntry& entry) {
- if (SkipSymbol(entry.symbol_status.state)) {
+ if (SkipSymbol(entry.visibility.level)) {
return {};
}
@@ -576,7 +580,7 @@
}
// Generate an onResourcesLoaded() callback if requested.
- if (options_.rewrite_callback_options) {
+ if (out != nullptr && options_.rewrite_callback_options) {
rewrite_method =
util::make_unique<MethodDefinition>("public static void onResourcesLoaded(int p)");
for (const std::string& package_to_callback :
@@ -597,8 +601,12 @@
const bool force_creation_if_empty =
(options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
- std::unique_ptr<ClassDefinition> class_def = util::make_unique<ClassDefinition>(
- to_string(type->type), ClassQualifier::kStatic, force_creation_if_empty);
+ std::unique_ptr<ClassDefinition> class_def;
+ if (out != nullptr) {
+ class_def = util::make_unique<ClassDefinition>(
+ to_string(type->type), ClassQualifier::kStatic, force_creation_if_empty);
+ }
+
if (!ProcessType(package_name_to_generate, *package, *type, class_def.get(),
rewrite_method.get(), r_txt_printer.get())) {
return false;
@@ -615,16 +623,17 @@
}
}
- if (type->type == ResourceType::kStyleable &&
+ if (out != nullptr && type->type == ResourceType::kStyleable &&
options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) {
// When generating a public R class, we don't want Styleable to be part
// of the API. It is only emitted for documentation purposes.
class_def->GetCommentBuilder()->AppendComment("@doconly");
}
- AppendJavaDocAnnotations(options_.javadoc_annotations, class_def->GetCommentBuilder());
-
- r_class.AddMember(std::move(class_def));
+ if (out != nullptr) {
+ AppendJavaDocAnnotations(options_.javadoc_annotations, class_def->GetCommentBuilder());
+ r_class.AddMember(std::move(class_def));
+ }
}
}
@@ -632,8 +641,10 @@
r_class.AddMember(std::move(rewrite_method));
}
- AppendJavaDocAnnotations(options_.javadoc_annotations, r_class.GetCommentBuilder());
- ClassDefinition::WriteJavaFile(&r_class, out_package_name, options_.use_final, out);
+ if (out != nullptr) {
+ AppendJavaDocAnnotations(options_.javadoc_annotations, r_class.GetCommentBuilder());
+ ClassDefinition::WriteJavaFile(&r_class, out_package_name, options_.use_final, out);
+ }
return true;
}
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 4992f07..853120b 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -82,7 +82,7 @@
static std::string TransformToFieldName(const android::StringPiece& symbol);
private:
- bool SkipSymbol(SymbolState state);
+ bool SkipSymbol(Visibility::Level state);
bool SkipSymbol(const Maybe<SymbolTable::Symbol>& symbol);
// Returns the unmangled resource entry name if the unmangled package is the same as
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 02f4cb1..5beb594 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -139,8 +139,8 @@
.AddSimple("android:id/one", ResourceId(0x01020000))
.AddSimple("android:id/two", ResourceId(0x01020001))
.AddSimple("android:id/three", ResourceId(0x01020002))
- .SetSymbolState("android:id/one", ResourceId(0x01020000), SymbolState::kPublic)
- .SetSymbolState("android:id/two", ResourceId(0x01020001), SymbolState::kPrivate)
+ .SetSymbolState("android:id/one", ResourceId(0x01020000), Visibility::Level::kPublic)
+ .SetSymbolState("android:id/two", ResourceId(0x01020001), Visibility::Level::kPrivate)
.Build();
std::unique_ptr<IAaptContext> context =
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index a68df1d..da05dc3 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -430,7 +430,10 @@
return false;
}
- if (!executor.Execute(xml::XmlActionExecutorPolicy::kWhitelist, context->GetDiagnostics(), doc)) {
+ xml::XmlActionExecutorPolicy policy = options_.warn_validation
+ ? xml::XmlActionExecutorPolicy::kWhitelistWarning
+ : xml::XmlActionExecutorPolicy::kWhitelist;
+ if (!executor.Execute(policy, context->GetDiagnostics(), doc)) {
return false;
}
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index f5715f6..0caa52e 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -57,6 +57,11 @@
// The version codename of the framework being compiled against to set for
// 'android:compileSdkVersionCodename' in the <manifest> tag.
Maybe<std::string> compile_sdk_version_codename;
+
+ // Wether validation errors should be treated only as warnings. If this is 'true', then an
+ // incorrect node will not result in an error, but only as a warning, and the parsing will
+ // continue.
+ bool warn_validation = false;
};
// Verifies that the manifest is correctly formed and inserts defaults where specified with
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 1320dcd..c6f895b 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -112,7 +112,9 @@
}
TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
- ManifestFixerOptions options = {std::string("8"), std::string("22")};
+ ManifestFixerOptions options;
+ options.min_sdk_version_default = std::string("8");
+ options.target_sdk_version_default = std::string("22");
std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
@@ -193,7 +195,9 @@
}
TEST_F(ManifestFixerTest, UsesSdkMustComeBeforeApplication) {
- ManifestFixerOptions options = {std::string("8"), std::string("22")};
+ ManifestFixerOptions options;
+ options.min_sdk_version_default = std::string("8");
+ options.target_sdk_version_default = std::string("22");
std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
@@ -467,4 +471,27 @@
EXPECT_THAT(attr->value, StrEq("P"));
}
+TEST_F(ManifestFixerTest, UnexpectedElementsInManifest) {
+ std::string input = R"(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+ <beep/>
+ </manifest>)";
+ ManifestFixerOptions options;
+ options.warn_validation = true;
+
+ // Unexpected element should result in a warning if the flag is set to 'true'.
+ std::unique_ptr<xml::XmlResource> manifest = VerifyWithOptions(input, options);
+ ASSERT_THAT(manifest, NotNull());
+
+ // Unexpected element should result in an error if the flag is set to 'false'.
+ options.warn_validation = false;
+ manifest = VerifyWithOptions(input, options);
+ ASSERT_THAT(manifest, IsNull());
+
+ // By default the flag should be set to 'false'.
+ manifest = Verify(input);
+ ASSERT_THAT(manifest, IsNull());
+}
+
} // namespace aapt
diff --git a/tools/aapt2/link/PrivateAttributeMover.cpp b/tools/aapt2/link/PrivateAttributeMover.cpp
index eee4b60..675b02a 100644
--- a/tools/aapt2/link/PrivateAttributeMover.cpp
+++ b/tools/aapt2/link/PrivateAttributeMover.cpp
@@ -62,7 +62,7 @@
continue;
}
- if (type->symbol_status.state != SymbolState::kPublic) {
+ if (type->visibility_level != Visibility::Level::kPublic) {
// No public attributes, so we can safely leave these private attributes
// where they are.
continue;
@@ -72,7 +72,7 @@
move_if(type->entries, std::back_inserter(private_attr_entries),
[](const std::unique_ptr<ResourceEntry>& entry) -> bool {
- return entry->symbol_status.state != SymbolState::kPublic;
+ return entry->visibility.level != Visibility::Level::kPublic;
});
if (private_attr_entries.empty()) {
diff --git a/tools/aapt2/link/PrivateAttributeMover_test.cpp b/tools/aapt2/link/PrivateAttributeMover_test.cpp
index 7fcf6e7..168234b 100644
--- a/tools/aapt2/link/PrivateAttributeMover_test.cpp
+++ b/tools/aapt2/link/PrivateAttributeMover_test.cpp
@@ -30,9 +30,9 @@
.AddSimple("android:attr/publicB")
.AddSimple("android:attr/privateB")
.SetSymbolState("android:attr/publicA", ResourceId(0x01010000),
- SymbolState::kPublic)
+ Visibility::Level::kPublic)
.SetSymbolState("android:attr/publicB", ResourceId(0x01010000),
- SymbolState::kPublic)
+ Visibility::Level::kPublic)
.Build();
PrivateAttributeMover mover;
@@ -81,7 +81,7 @@
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.AddSimple("android:attr/pub")
- .SetSymbolState("android:attr/pub", ResourceId(0x01010000), SymbolState::kPublic)
+ .SetSymbolState("android:attr/pub", ResourceId(0x01010000), Visibility::Level::kPublic)
.Build();
ResourceTablePackage* package = table->FindPackage("android");
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index ad7d8b6..b8f8804 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -363,8 +363,8 @@
NameMangler::Unmangle(&name.entry, &name.package);
// Symbol state information may be lost if there is no value for the resource.
- if (entry->symbol_status.state != SymbolState::kUndefined && entry->values.empty()) {
- context->GetDiagnostics()->Error(DiagMessage(entry->symbol_status.source)
+ if (entry->visibility.level != Visibility::Level::kUndefined && entry->values.empty()) {
+ context->GetDiagnostics()->Error(DiagMessage(entry->visibility.source)
<< "no definition for declared symbol '" << name << "'");
error = true;
}
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 58d0607..e819f51 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -83,44 +83,58 @@
static bool MergeType(IAaptContext* context, const Source& src, ResourceTableType* dst_type,
ResourceTableType* src_type) {
- if (dst_type->symbol_status.state < src_type->symbol_status.state) {
+ if (src_type->visibility_level > dst_type->visibility_level) {
// The incoming type's visibility is stronger, so we should override the visibility.
- if (src_type->symbol_status.state == SymbolState::kPublic) {
+ if (src_type->visibility_level == Visibility::Level::kPublic) {
// Only copy the ID if the source is public, or else the ID is meaningless.
dst_type->id = src_type->id;
}
- dst_type->symbol_status = std::move(src_type->symbol_status);
- } else if (dst_type->symbol_status.state == SymbolState::kPublic &&
- src_type->symbol_status.state == SymbolState::kPublic &&
- dst_type->id && src_type->id &&
- dst_type->id.value() != src_type->id.value()) {
+ dst_type->visibility_level = src_type->visibility_level;
+ } else if (dst_type->visibility_level == Visibility::Level::kPublic &&
+ src_type->visibility_level == Visibility::Level::kPublic && dst_type->id &&
+ src_type->id && dst_type->id.value() != src_type->id.value()) {
// Both types are public and have different IDs.
- context->GetDiagnostics()->Error(DiagMessage(src)
- << "cannot merge type '" << src_type->type
- << "': conflicting public IDs");
+ context->GetDiagnostics()->Error(DiagMessage(src) << "cannot merge type '" << src_type->type
+ << "': conflicting public IDs");
return false;
}
return true;
}
-static bool MergeEntry(IAaptContext* context, const Source& src, ResourceEntry* dst_entry,
- ResourceEntry* src_entry) {
- if (dst_entry->symbol_status.state < src_entry->symbol_status.state) {
- // The incoming type's visibility is stronger, so we should override the visibility.
- if (src_entry->symbol_status.state == SymbolState::kPublic) {
- // Only copy the ID if the source is public, or else the ID is meaningless.
+static bool MergeEntry(IAaptContext* context, const Source& src, bool overlay,
+ ResourceEntry* dst_entry, ResourceEntry* src_entry) {
+ // Copy over the strongest visibility.
+ if (src_entry->visibility.level > dst_entry->visibility.level) {
+ // Only copy the ID if the source is public, or else the ID is meaningless.
+ if (src_entry->visibility.level == Visibility::Level::kPublic) {
dst_entry->id = src_entry->id;
}
- dst_entry->symbol_status = std::move(src_entry->symbol_status);
- } else if (src_entry->symbol_status.state == SymbolState::kPublic &&
- dst_entry->symbol_status.state == SymbolState::kPublic &&
- dst_entry->id && src_entry->id &&
- dst_entry->id.value() != src_entry->id.value()) {
+ dst_entry->visibility = std::move(src_entry->visibility);
+ } else if (src_entry->visibility.level == Visibility::Level::kPublic &&
+ dst_entry->visibility.level == Visibility::Level::kPublic && dst_entry->id &&
+ src_entry->id && src_entry->id != dst_entry->id) {
// Both entries are public and have different IDs.
context->GetDiagnostics()->Error(DiagMessage(src) << "cannot merge entry '" << src_entry->name
<< "': conflicting public IDs");
return false;
}
+
+ // Copy over the rest of the properties, if needed.
+ if (src_entry->allow_new) {
+ dst_entry->allow_new = std::move(src_entry->allow_new);
+ }
+
+ if (src_entry->overlayable) {
+ if (dst_entry->overlayable && !overlay) {
+ context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable.value().source)
+ << "duplicate overlayable declaration for resource '"
+ << src_entry->name << "'");
+ context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable.value().source)
+ << "previous declaration here");
+ return false;
+ }
+ dst_entry->overlayable = std::move(src_entry->overlayable);
+ }
return true;
}
@@ -202,7 +216,7 @@
}
ResourceEntry* dst_entry;
- if (allow_new_resources || src_entry->symbol_status.allow_new) {
+ if (allow_new_resources || src_entry->allow_new) {
dst_entry = dst_type->FindOrCreateEntry(entry_name);
} else {
dst_entry = dst_type->FindEntry(entry_name);
@@ -220,7 +234,7 @@
continue;
}
- if (!MergeEntry(context_, src, dst_entry, src_entry.get())) {
+ if (!MergeEntry(context_, src, overlay, dst_entry, src_entry.get())) {
error = true;
continue;
}
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 6aab8de..34461c6 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -182,14 +182,12 @@
std::unique_ptr<ResourceTable> base =
test::ResourceTableBuilder()
.SetPackageId("", 0x7f)
- .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001),
- SymbolState::kPublic)
+ .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), Visibility::Level::kPublic)
.Build();
std::unique_ptr<ResourceTable> overlay =
test::ResourceTableBuilder()
.SetPackageId("", 0x7f)
- .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001),
- SymbolState::kPublic)
+ .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), Visibility::Level::kPublic)
.Build();
ResourceTable final_table;
@@ -205,14 +203,12 @@
std::unique_ptr<ResourceTable> base =
test::ResourceTableBuilder()
.SetPackageId("", 0x7f)
- .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001),
- SymbolState::kPublic)
+ .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), Visibility::Level::kPublic)
.Build();
std::unique_ptr<ResourceTable> overlay =
test::ResourceTableBuilder()
.SetPackageId("", 0x7f)
- .SetSymbolState("bool/foo", ResourceId(0x7f, 0x02, 0x0001),
- SymbolState::kPublic)
+ .SetSymbolState("bool/foo", ResourceId(0x7f, 0x02, 0x0001), Visibility::Level::kPublic)
.Build();
ResourceTable final_table;
@@ -228,14 +224,12 @@
std::unique_ptr<ResourceTable> base =
test::ResourceTableBuilder()
.SetPackageId("", 0x7f)
- .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001),
- SymbolState::kPublic)
+ .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), Visibility::Level::kPublic)
.Build();
std::unique_ptr<ResourceTable> overlay =
test::ResourceTableBuilder()
.SetPackageId("", 0x7f)
- .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0002),
- SymbolState::kPublic)
+ .SetSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0002), Visibility::Level::kPublic)
.Build();
ResourceTable final_table;
@@ -253,7 +247,7 @@
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("", 0x7f)
- .SetSymbolState("bool/foo", {}, SymbolState::kUndefined, true /*allow new overlay*/)
+ .SetSymbolState("bool/foo", {}, Visibility::Level::kUndefined, true /*allow new overlay*/)
.AddValue("bool/foo", ResourceUtils::TryParseBool("true"))
.Build();
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
index da3b879..991faad 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -17,6 +17,7 @@
#include "MultiApkGenerator.h"
#include <algorithm>
+#include <regex>
#include <string>
#include "androidfw/StringPiece.h"
@@ -39,33 +40,11 @@
namespace aapt {
using ::aapt::configuration::AndroidSdk;
-using ::aapt::configuration::Artifact;
-using ::aapt::configuration::PostProcessingConfiguration;
+using ::aapt::configuration::OutputArtifact;
using ::aapt::xml::kSchemaAndroid;
using ::aapt::xml::XmlResource;
using ::android::StringPiece;
-namespace {
-
-Maybe<AndroidSdk> GetAndroidSdk(const Artifact& artifact, const PostProcessingConfiguration& config,
- IDiagnostics* diag) {
- if (!artifact.android_sdk_group) {
- return {};
- }
-
- const std::string& group_name = artifact.android_sdk_group.value();
- auto group = config.android_sdk_groups.find(group_name);
- // TODO: Remove validation when configuration parser ensures referential integrity.
- if (group == config.android_sdk_groups.end()) {
- diag->Error(DiagMessage() << "could not find referenced group '" << group_name << "'");
- return {};
- }
-
- return group->second;
-}
-
-} // namespace
-
/**
* Context wrapper that allows the min Android SDK value to be overridden.
*/
@@ -114,8 +93,9 @@
min_sdk_ = min_sdk;
}
- void SetSource(const Source& source) {
- source_diag_ = util::make_unique<SourcePathDiagnostics>(source, context_->GetDiagnostics());
+ void SetSource(const std::string& source) {
+ source_diag_ =
+ util::make_unique<SourcePathDiagnostics>(Source{source}, context_->GetDiagnostics());
}
private:
@@ -125,90 +105,77 @@
int min_sdk_ = -1;
};
+class SignatureFilter : public IPathFilter {
+ bool Keep(const std::string& path) override {
+ static std::regex signature_regex(R"regex(^META-INF/.*\.(RSA|DSA|EC|SF)$)regex");
+ if (std::regex_search(path, signature_regex)) {
+ return false;
+ }
+ return !(path == "META-INF/MANIFEST.MF");
+ }
+};
+
MultiApkGenerator::MultiApkGenerator(LoadedApk* apk, IAaptContext* context)
: apk_(apk), context_(context) {
}
bool MultiApkGenerator::FromBaseApk(const MultiApkGeneratorOptions& options) {
- // TODO(safarmer): Handle APK version codes for the generated APKs.
- const PostProcessingConfiguration& config = options.config;
-
- const std::string& apk_name = file::GetFilename(apk_->GetSource().path).to_string();
- const StringPiece ext = file::GetExtension(apk_name);
- const std::string base_name = apk_name.substr(0, apk_name.rfind(ext.to_string()));
-
std::unordered_set<std::string> artifacts_to_keep = options.kept_artifacts;
std::unordered_set<std::string> filtered_artifacts;
std::unordered_set<std::string> kept_artifacts;
// For now, just write out the stripped APK since ABI splitting doesn't modify anything else.
- for (const Artifact& artifact : config.artifacts) {
- SourcePathDiagnostics diag{{apk_name}, context_->GetDiagnostics()};
-
+ for (const OutputArtifact& artifact : options.apk_artifacts) {
FilterChain filters;
- if (!artifact.name && !config.artifact_format) {
- diag.Error(
- DiagMessage() << "Artifact does not have a name and no global name template defined");
- return false;
- }
-
- Maybe<std::string> maybe_artifact_name =
- (artifact.name) ? artifact.Name(apk_name, &diag)
- : artifact.ToArtifactName(config.artifact_format.value(), apk_name, &diag);
-
- if (!maybe_artifact_name) {
- diag.Error(DiagMessage() << "Could not determine split APK artifact name");
- return false;
- }
-
- const std::string& artifact_name = maybe_artifact_name.value();
-
ContextWrapper wrapped_context{context_};
- wrapped_context.SetSource({artifact_name});
+ wrapped_context.SetSource(artifact.name);
if (!options.kept_artifacts.empty()) {
- const auto& it = artifacts_to_keep.find(artifact_name);
+ const auto& it = artifacts_to_keep.find(artifact.name);
if (it == artifacts_to_keep.end()) {
- filtered_artifacts.insert(artifact_name);
+ filtered_artifacts.insert(artifact.name);
if (context_->IsVerbose()) {
- context_->GetDiagnostics()->Note(DiagMessage(artifact_name) << "skipping artifact");
+ context_->GetDiagnostics()->Note(DiagMessage(artifact.name) << "skipping artifact");
}
continue;
} else {
artifacts_to_keep.erase(it);
- kept_artifacts.insert(artifact_name);
+ kept_artifacts.insert(artifact.name);
}
}
std::unique_ptr<ResourceTable> table =
- FilterTable(artifact, config, *apk_->GetResourceTable(), &wrapped_context, &filters);
+ FilterTable(context_, artifact, *apk_->GetResourceTable(), &filters);
if (!table) {
return false;
}
+ IDiagnostics* diag = wrapped_context.GetDiagnostics();
+
std::unique_ptr<XmlResource> manifest;
- if (!UpdateManifest(artifact, config, &manifest, &diag)) {
- diag.Error(DiagMessage() << "could not update AndroidManifest.xml for output artifact");
+ if (!UpdateManifest(artifact, &manifest, diag)) {
+ diag->Error(DiagMessage() << "could not update AndroidManifest.xml for output artifact");
return false;
}
std::string out = options.out_dir;
if (!file::mkdirs(out)) {
- diag.Warn(DiagMessage() << "could not create out dir: " << out);
+ diag->Warn(DiagMessage() << "could not create out dir: " << out);
}
- file::AppendPath(&out, artifact_name);
+ file::AppendPath(&out, artifact.name);
if (context_->IsVerbose()) {
- diag.Note(DiagMessage() << "Generating split: " << out);
+ diag->Note(DiagMessage() << "Generating split: " << out);
}
- std::unique_ptr<IArchiveWriter> writer = CreateZipFileArchiveWriter(&diag, out);
+ std::unique_ptr<IArchiveWriter> writer = CreateZipFileArchiveWriter(diag, out);
if (context_->IsVerbose()) {
- diag.Note(DiagMessage() << "Writing output: " << out);
+ diag->Note(DiagMessage() << "Writing output: " << out);
}
+ filters.AddFilter(util::make_unique<SignatureFilter>());
if (!apk_->WriteToArchive(&wrapped_context, table.get(), options.table_flattener_options,
&filters, writer.get(), manifest.get())) {
return false;
@@ -242,64 +209,34 @@
return true;
}
-std::unique_ptr<ResourceTable> MultiApkGenerator::FilterTable(
- const configuration::Artifact& artifact,
- const configuration::PostProcessingConfiguration& config, const ResourceTable& old_table,
- IAaptContext* context, FilterChain* filters) {
+std::unique_ptr<ResourceTable> MultiApkGenerator::FilterTable(IAaptContext* context,
+ const OutputArtifact& artifact,
+ const ResourceTable& old_table,
+ FilterChain* filters) {
TableSplitterOptions splits;
AxisConfigFilter axis_filter;
ContextWrapper wrapped_context{context};
+ wrapped_context.SetSource(artifact.name);
- if (artifact.abi_group) {
- const std::string& group_name = artifact.abi_group.value();
-
- auto group = config.abi_groups.find(group_name);
- // TODO: Remove validation when configuration parser ensures referential integrity.
- if (group == config.abi_groups.end()) {
- context->GetDiagnostics()->Error(DiagMessage() << "could not find referenced ABI group '"
- << group_name << "'");
- return {};
- }
- filters->AddFilter(AbiFilter::FromAbiList(group->second));
+ if (!artifact.abis.empty()) {
+ filters->AddFilter(AbiFilter::FromAbiList(artifact.abis));
}
- if (artifact.screen_density_group) {
- const std::string& group_name = artifact.screen_density_group.value();
-
- auto group = config.screen_density_groups.find(group_name);
- // TODO: Remove validation when configuration parser ensures referential integrity.
- if (group == config.screen_density_groups.end()) {
- context->GetDiagnostics()->Error(DiagMessage()
- << "could not find referenced group '" << group_name << "'");
- return {};
- }
-
- const std::vector<ConfigDescription>& densities = group->second;
- for (const auto& density_config : densities) {
+ if (!artifact.screen_densities.empty()) {
+ for (const auto& density_config : artifact.screen_densities) {
splits.preferred_densities.push_back(density_config.density);
}
}
- if (artifact.locale_group) {
- const std::string& group_name = artifact.locale_group.value();
- auto group = config.locale_groups.find(group_name);
- // TODO: Remove validation when configuration parser ensures referential integrity.
- if (group == config.locale_groups.end()) {
- context->GetDiagnostics()->Error(DiagMessage()
- << "could not find referenced group '" << group_name << "'");
- return {};
- }
-
- const std::vector<ConfigDescription>& locales = group->second;
- for (const auto& locale : locales) {
+ if (!artifact.locales.empty()) {
+ for (const auto& locale : artifact.locales) {
axis_filter.AddConfig(locale);
}
splits.config_filter = &axis_filter;
}
- Maybe<AndroidSdk> sdk = GetAndroidSdk(artifact, config, context->GetDiagnostics());
- if (sdk && sdk.value().min_sdk_version) {
- wrapped_context.SetMinSdkVersion(sdk.value().min_sdk_version.value());
+ if (artifact.android_sdk) {
+ wrapped_context.SetMinSdkVersion(artifact.android_sdk.value().min_sdk_version);
}
std::unique_ptr<ResourceTable> table = old_table.Clone();
@@ -315,8 +252,7 @@
return table;
}
-bool MultiApkGenerator::UpdateManifest(const Artifact& artifact,
- const PostProcessingConfiguration& config,
+bool MultiApkGenerator::UpdateManifest(const OutputArtifact& artifact,
std::unique_ptr<XmlResource>* updated_manifest,
IDiagnostics* diag) {
const xml::XmlResource* apk_manifest = apk_->GetManifest();
@@ -355,16 +291,15 @@
versionCode->compiled_value = ResourceUtils::TryParseInt(std::to_string(new_version));
// Check to see if the minSdkVersion needs to be updated.
- Maybe<AndroidSdk> maybe_sdk = GetAndroidSdk(artifact, config, diag);
- if (maybe_sdk) {
+ if (artifact.android_sdk) {
// TODO(safarmer): Handle the rest of the Android SDK.
- const AndroidSdk& android_sdk = maybe_sdk.value();
+ const AndroidSdk& android_sdk = artifact.android_sdk.value();
if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) {
if (xml::Attribute* min_sdk_attr =
uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) {
// Populate with a pre-compiles attribute to we don't need to relink etc.
- const std::string& min_sdk_str = std::to_string(android_sdk.min_sdk_version.value());
+ const std::string& min_sdk_str = std::to_string(android_sdk.min_sdk_version);
min_sdk_attr->compiled_value = ResourceUtils::TryParseInt(min_sdk_str);
} else {
// There was no minSdkVersion. This is strange since at this point we should have been
@@ -380,10 +315,7 @@
}
}
- if (artifact.screen_density_group) {
- auto densities = config.screen_density_groups.find(artifact.screen_density_group.value());
- CHECK(densities != config.screen_density_groups.end()) << "Missing density group";
-
+ if (!artifact.screen_densities.empty()) {
xml::Element* screens_el = manifest_el->FindChild({}, "compatible-screens");
if (!screens_el) {
// create a new element.
@@ -396,7 +328,7 @@
screens_el->GetChildElements().clear();
}
- for (const auto& density : densities->second) {
+ for (const auto& density : artifact.screen_densities) {
std::unique_ptr<xml::Element> screen_el = util::make_unique<xml::Element>();
screen_el->name = "screen";
const char* density_str = density.toString().string();
diff --git a/tools/aapt2/optimize/MultiApkGenerator.h b/tools/aapt2/optimize/MultiApkGenerator.h
index dedb610..19f64cc 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.h
+++ b/tools/aapt2/optimize/MultiApkGenerator.h
@@ -20,6 +20,7 @@
#include <memory>
#include <string>
#include <unordered_set>
+#include <vector>
#include "Diagnostics.h"
#include "LoadedApk.h"
@@ -29,7 +30,7 @@
struct MultiApkGeneratorOptions {
std::string out_dir;
- configuration::PostProcessingConfiguration config;
+ std::vector<configuration::OutputArtifact> apk_artifacts;
TableFlattenerOptions table_flattener_options;
std::unordered_set<std::string> kept_artifacts;
};
@@ -49,18 +50,17 @@
bool FromBaseApk(const MultiApkGeneratorOptions& options);
protected:
- virtual std::unique_ptr<ResourceTable> FilterTable(
- const configuration::Artifact& artifact,
- const configuration::PostProcessingConfiguration& config, const ResourceTable& old_table,
- IAaptContext* context, FilterChain* chain);
+ virtual std::unique_ptr<ResourceTable> FilterTable(IAaptContext* context,
+ const configuration::OutputArtifact& artifact,
+ const ResourceTable& old_table,
+ FilterChain* chain);
private:
IDiagnostics* GetDiagnostics() {
return context_->GetDiagnostics();
}
- bool UpdateManifest(const configuration::Artifact& artifact,
- const configuration::PostProcessingConfiguration& config,
+ bool UpdateManifest(const configuration::OutputArtifact& artifact,
std::unique_ptr<xml::XmlResource>* updated_manifest, IDiagnostics* diag);
LoadedApk* apk_;
diff --git a/tools/aapt2/optimize/MultiApkGenerator_test.cpp b/tools/aapt2/optimize/MultiApkGenerator_test.cpp
index 2d303e2..e1d951f 100644
--- a/tools/aapt2/optimize/MultiApkGenerator_test.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator_test.cpp
@@ -36,8 +36,7 @@
using ::aapt::configuration::Abi;
using ::aapt::configuration::AndroidSdk;
-using ::aapt::configuration::Artifact;
-using ::aapt::configuration::PostProcessingConfiguration;
+using ::aapt::configuration::OutputArtifact;
using ::aapt::test::GetValue;
using ::aapt::test::GetValueForConfig;
using ::aapt::test::ParseConfigOrDie;
@@ -48,7 +47,6 @@
using ::testing::PrintToString;
using ::testing::Return;
using ::testing::Test;
-using ::testing::_;
/**
* Subclass the MultiApkGenerator class so that we can access the protected FilterTable method to
@@ -60,11 +58,11 @@
: MultiApkGenerator(apk, context) {
}
- std::unique_ptr<ResourceTable> FilterTable(
- const configuration::Artifact& artifact,
- const configuration::PostProcessingConfiguration& config, const ResourceTable& old_table,
- IAaptContext* context, FilterChain* filter_chain) override {
- return MultiApkGenerator::FilterTable(artifact, config, old_table, context, filter_chain);
+ std::unique_ptr<ResourceTable> FilterTable(IAaptContext* context,
+ const configuration::OutputArtifact& artifact,
+ const ResourceTable& old_table,
+ FilterChain* filter_chain) override {
+ return MultiApkGenerator::FilterTable(context, artifact, old_table, filter_chain);
}
};
@@ -108,27 +106,13 @@
LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}};
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(19).Build();
- PostProcessingConfiguration empty_config;
- TableFlattenerOptions table_flattener_options;
FilterChain chain;
- Artifact x64 = test::ArtifactBuilder()
- .SetName("${basename}.${sdk}.apk")
- .SetDensityGroup("xhdpi")
- .SetAndroidSdk("v23")
- .Build();
-
- auto config = test::PostProcessingConfigurationBuilder()
- .SetLocaleGroup("en", {"en"})
- .SetAbiGroup("x64", {Abi::kX86_64})
- .SetDensityGroup("xhdpi", {"xhdpi"})
- .SetAndroidSdk("v23", AndroidSdk::ForMinSdk(23))
- .AddArtifact(x64)
- .Build();
+ OutputArtifact artifact = test::ArtifactBuilder().AddDensity(xhdpi_).SetAndroidSdk(23).Build();
MultiApkGeneratorWrapper generator{&apk, ctx.get()};
std::unique_ptr<ResourceTable> split =
- generator.FilterTable(x64, config, *apk.GetResourceTable(), ctx.get(), &chain);
+ generator.FilterTable(ctx.get(), artifact, *apk.GetResourceTable(), &chain);
ResourceTable* new_table = split.get();
EXPECT_THAT(ValueForConfig(new_table, mdpi_), IsNull());
@@ -149,27 +133,13 @@
LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}};
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build();
- PostProcessingConfiguration empty_config;
- TableFlattenerOptions table_flattener_options;
FilterChain chain;
- Artifact x64 = test::ArtifactBuilder()
- .SetName("${basename}.${sdk}.apk")
- .SetDensityGroup("xhdpi")
- .SetAndroidSdk("v4")
- .Build();
-
- auto config = test::PostProcessingConfigurationBuilder()
- .SetLocaleGroup("en", {"en"})
- .SetAbiGroup("x64", {Abi::kX86_64})
- .SetDensityGroup("xhdpi", {"xhdpi"})
- .SetAndroidSdk("v4", AndroidSdk::ForMinSdk(4))
- .AddArtifact(x64)
- .Build();
+ OutputArtifact artifact = test::ArtifactBuilder().AddDensity(xhdpi_).SetAndroidSdk(4).Build();
MultiApkGeneratorWrapper generator{&apk, ctx.get()};;
std::unique_ptr<ResourceTable> split =
- generator.FilterTable(x64, config, *apk.GetResourceTable(), ctx.get(), &chain);
+ generator.FilterTable(ctx.get(), artifact, *apk.GetResourceTable(), &chain);
ResourceTable* new_table = split.get();
EXPECT_THAT(ValueForConfig(new_table, mdpi_), IsNull());
@@ -188,23 +158,13 @@
LoadedApk apk = {{"test.apk"}, {}, std::move(table), {}};
std::unique_ptr<IAaptContext> ctx = test::ContextBuilder().SetMinSdkVersion(1).Build();
- PostProcessingConfiguration empty_config;
- TableFlattenerOptions table_flattener_options;
FilterChain chain;
- Artifact x64 =
- test::ArtifactBuilder().SetName("${basename}.${sdk}.apk").SetDensityGroup("xhdpi").Build();
-
- auto config = test::PostProcessingConfigurationBuilder()
- .SetLocaleGroup("en", {"en"})
- .SetAbiGroup("x64", {Abi::kX86_64})
- .SetDensityGroup("xhdpi", {"xhdpi"})
- .AddArtifact(x64)
- .Build();
+ OutputArtifact artifact = test::ArtifactBuilder().AddDensity(xhdpi_).Build();
MultiApkGeneratorWrapper generator{&apk, ctx.get()};
std::unique_ptr<ResourceTable> split =
- generator.FilterTable(x64, config, *apk.GetResourceTable(), ctx.get(), &chain);
+ generator.FilterTable(ctx.get(), artifact, *apk.GetResourceTable(), &chain);
ResourceTable* new_table = split.get();
EXPECT_THAT(ValueForConfig(new_table, mdpi_), IsNull());
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 2d517c7..3cae0e8 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -30,7 +30,8 @@
#include "ValueVisitor.h"
#include "util/Util.h"
-using android::StringPiece;
+using ::android::StringPiece;
+using ::android::StringPiece16;
namespace aapt {
@@ -189,7 +190,7 @@
ResourceTable::SearchResult sr = result.value();
std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>();
- symbol->is_public = (sr.entry->symbol_status.state == SymbolState::kPublic);
+ symbol->is_public = (sr.entry->visibility.level == Visibility::Level::kPublic);
if (sr.package->id && sr.type->id && sr.entry->id) {
symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value());
@@ -291,11 +292,34 @@
const std::u16string package16 = util::Utf8ToUtf16(name.package);
const std::u16string type16 = util::Utf8ToUtf16(to_string(name.type));
const std::u16string entry16 = util::Utf8ToUtf16(name.entry);
+ const std::u16string mangled_entry16 =
+ util::Utf8ToUtf16(NameMangler::MangleEntry(name.package, name.entry));
- uint32_t type_spec_flags = 0;
- ResourceId res_id = table.identifierForName(
- entry16.data(), entry16.size(), type16.data(), type16.size(),
- package16.data(), package16.size(), &type_spec_flags);
+ uint32_t type_spec_flags;
+ ResourceId res_id;
+
+ // There can be mangled resources embedded within other packages. Here we will
+ // look into each package and look-up the mangled name until we find the resource.
+ const size_t count = table.getBasePackageCount();
+ for (size_t i = 0; i < count; i++) {
+ const android::String16 package_name = table.getBasePackageName(i);
+ StringPiece16 real_package16 = package16;
+ StringPiece16 real_entry16 = entry16;
+ std::u16string scratch_entry16;
+ if (StringPiece16(package_name) != package16) {
+ real_entry16 = mangled_entry16;
+ real_package16 = package_name.string();
+ }
+
+ type_spec_flags = 0;
+ res_id = table.identifierForName(real_entry16.data(), real_entry16.size(), type16.data(),
+ type16.size(), real_package16.data(), real_package16.size(),
+ &type_spec_flags);
+ if (res_id.is_valid()) {
+ break;
+ }
+ }
+
if (!res_id.is_valid()) {
return {};
}
diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp
index fd8a508..1f59d70 100644
--- a/tools/aapt2/process/SymbolTable_test.cpp
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -16,7 +16,15 @@
#include "process/SymbolTable.h"
+#include "SdkConstants.h"
+#include "format/binary/TableFlattener.h"
#include "test/Test.h"
+#include "util/BigBuffer.h"
+
+using ::testing::Eq;
+using ::testing::IsNull;
+using ::testing::Ne;
+using ::testing::NotNull;
namespace aapt {
@@ -30,13 +38,13 @@
.Build();
ResourceTableSymbolSource symbol_source(table.get());
- EXPECT_NE(nullptr, symbol_source.FindByName(test::ParseNameOrDie("android:id/foo")));
- EXPECT_NE(nullptr, symbol_source.FindByName(test::ParseNameOrDie("android:id/bar")));
+ EXPECT_THAT(symbol_source.FindByName(test::ParseNameOrDie("android:id/foo")), NotNull());
+ EXPECT_THAT(symbol_source.FindByName(test::ParseNameOrDie("android:id/bar")), NotNull());
std::unique_ptr<SymbolTable::Symbol> s =
symbol_source.FindByName(test::ParseNameOrDie("android:attr/foo"));
- ASSERT_NE(nullptr, s);
- EXPECT_NE(nullptr, s->attribute);
+ ASSERT_THAT(s, NotNull());
+ EXPECT_THAT(s->attribute, NotNull());
}
TEST(ResourceTableSymbolSourceTest, FindPrivateAttrSymbol) {
@@ -49,8 +57,8 @@
ResourceTableSymbolSource symbol_source(table.get());
std::unique_ptr<SymbolTable::Symbol> s =
symbol_source.FindByName(test::ParseNameOrDie("android:attr/foo"));
- ASSERT_NE(nullptr, s);
- EXPECT_NE(nullptr, s->attribute);
+ ASSERT_THAT(s, NotNull());
+ EXPECT_THAT(s->attribute, NotNull());
}
TEST(SymbolTableTest, FindByName) {
@@ -64,8 +72,52 @@
SymbolTable symbol_table(&mangler);
symbol_table.AppendSource(util::make_unique<ResourceTableSymbolSource>(table.get()));
- EXPECT_NE(nullptr, symbol_table.FindByName(test::ParseNameOrDie("id/foo")));
- EXPECT_NE(nullptr, symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")));
+ EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("id/foo")), NotNull());
+ EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")), NotNull());
+}
+
+TEST(SymbolTableTest, FindByNameWhenSymbolIsMangledInResTable) {
+ using namespace android;
+
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.android.app")
+ .SetPackageId(0x7f)
+ .SetPackageType(PackageType::kApp)
+ .SetMinSdkVersion(SDK_LOLLIPOP_MR1)
+ .SetNameManglerPolicy(NameManglerPolicy{"com.android.app"})
+ .Build();
+
+ // Create a ResourceTable with a mangled resource, simulating a static library being merged into
+ // the main application package.
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddSimple("com.android.app:id/" + NameMangler::MangleEntry("com.android.lib", "foo"),
+ ResourceId(0x7f020000))
+ .AddSimple("com.android.app:id/bar", ResourceId(0x7f020001))
+ .Build();
+
+ BigBuffer buffer(1024u);
+ TableFlattener flattener({}, &buffer);
+ ASSERT_TRUE(flattener.Consume(context.get(), table.get()));
+
+ std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
+
+ // Construct the test AssetManager.
+ auto asset_manager_source = util::make_unique<AssetManagerSymbolSource>();
+ ResTable& res_table = const_cast<ResTable&>(
+ asset_manager_source->GetAssetManager()->getResources(false /*required*/));
+ ASSERT_THAT(res_table.add(data.get(), buffer.size()), Eq(NO_ERROR));
+
+ SymbolTable symbol_table(context->GetNameMangler());
+ symbol_table.AppendSource(std::move(asset_manager_source));
+
+ EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")), NotNull());
+ EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.app:id/bar")), NotNull());
+
+ EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.app:id/foo")), IsNull());
+ EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/bar")), IsNull());
+ EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.other:id/foo")), IsNull());
}
} // namespace aapt
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index 9d49ca6..e991743 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -233,13 +233,13 @@
ResourceTableType* split_type = split_pkg->FindOrCreateType(type->type);
if (!split_type->id) {
split_type->id = type->id;
- split_type->symbol_status = type->symbol_status;
+ split_type->visibility_level = type->visibility_level;
}
ResourceEntry* split_entry = split_type->FindOrCreateEntry(entry->name);
if (!split_entry->id) {
split_entry->id = entry->id;
- split_entry->symbol_status = entry->symbol_status;
+ split_entry->visibility = entry->visibility;
}
// Copy the selected values into the new Split Entry.
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index ecec63f..495a48a 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -25,7 +25,8 @@
using ::aapt::configuration::Abi;
using ::aapt::configuration::AndroidSdk;
-using ::aapt::configuration::Artifact;
+using ::aapt::configuration::ConfiguredArtifact;
+using ::aapt::configuration::GetOrCreateGroup;
using ::aapt::io::StringInputStream;
using ::android::StringPiece;
@@ -116,19 +117,20 @@
const ResourceId& id,
std::unique_ptr<Value> value) {
ResourceName res_name = ParseNameOrDie(name);
- CHECK(table_->AddResourceAllowMangled(res_name, id, config, {}, std::move(value),
- GetDiagnostics()));
+ CHECK(table_->AddResourceWithIdMangled(res_name, id, config, {}, std::move(value),
+ GetDiagnostics()));
return *this;
}
ResourceTableBuilder& ResourceTableBuilder::SetSymbolState(const StringPiece& name,
- const ResourceId& id, SymbolState state,
+ const ResourceId& id,
+ Visibility::Level level,
bool allow_new) {
ResourceName res_name = ParseNameOrDie(name);
- Symbol symbol;
- symbol.state = state;
- symbol.allow_new = allow_new;
- CHECK(table_->SetSymbolStateAllowMangled(res_name, id, symbol, GetDiagnostics()));
+ Visibility visibility;
+ visibility.level = level;
+ CHECK(table_->SetVisibilityWithIdMangled(res_name, visibility, id, GetDiagnostics()));
+ CHECK(table_->SetAllowNewMangled(res_name, AllowNew{}, GetDiagnostics()));
return *this;
}
@@ -221,39 +223,82 @@
return doc;
}
-PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::SetAbiGroup(
- const std::string& name, const std::vector<Abi>& abis) {
- config_.abi_groups[name] = abis;
+ArtifactBuilder& ArtifactBuilder::SetName(const std::string& name) {
+ artifact_.name = name;
return *this;
}
-PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::SetLocaleGroup(
- const std::string& name, const std::vector<std::string>& locales) {
- auto& group = config_.locale_groups[name];
- for (const auto& locale : locales) {
- group.push_back(ParseConfigOrDie(locale));
- }
+ArtifactBuilder& ArtifactBuilder::SetVersion(int version) {
+ artifact_.version = version;
return *this;
}
-PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::SetDensityGroup(
- const std::string& name, const std::vector<std::string>& densities) {
- auto& group = config_.screen_density_groups[name];
+ArtifactBuilder& ArtifactBuilder::AddAbi(configuration::Abi abi) {
+ artifact_.abis.push_back(abi);
+ return *this;
+}
+
+ArtifactBuilder& ArtifactBuilder::AddDensity(const ConfigDescription& density) {
+ artifact_.screen_densities.push_back(density);
+ return *this;
+}
+
+ArtifactBuilder& ArtifactBuilder::AddLocale(const ConfigDescription& locale) {
+ artifact_.locales.push_back(locale);
+ return *this;
+}
+
+ArtifactBuilder& ArtifactBuilder::SetAndroidSdk(int min_sdk) {
+ artifact_.android_sdk = {AndroidSdk::ForMinSdk(min_sdk)};
+ return *this;
+}
+
+configuration::OutputArtifact ArtifactBuilder::Build() {
+ return artifact_;
+}
+
+PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::AddAbiGroup(
+ const std::string& label, std::vector<configuration::Abi> abis) {
+ return AddGroup(label, &config_.abi_groups, std::move(abis));
+}
+
+PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::AddDensityGroup(
+ const std::string& label, std::vector<std::string> densities) {
+ std::vector<ConfigDescription> configs;
for (const auto& density : densities) {
- group.push_back(ParseConfigOrDie(density));
+ configs.push_back(test::ParseConfigOrDie(density));
}
- return *this;
+ return AddGroup(label, &config_.screen_density_groups, configs);
}
-PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::SetAndroidSdk(
- const std::string& name, const AndroidSdk& sdk) {
- config_.android_sdk_groups[name] = sdk;
+PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::AddLocaleGroup(
+ const std::string& label, std::vector<std::string> locales) {
+ std::vector<ConfigDescription> configs;
+ for (const auto& locale : locales) {
+ configs.push_back(test::ParseConfigOrDie(locale));
+ }
+ return AddGroup(label, &config_.locale_groups, configs);
+}
+
+PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::AddDeviceFeatureGroup(
+ const std::string& label) {
+ return AddGroup(label, &config_.device_feature_groups);
+}
+
+PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::AddGlTextureGroup(
+ const std::string& label) {
+ return AddGroup(label, &config_.gl_texture_groups);
+}
+
+PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::AddAndroidSdk(
+ std::string label, int min_sdk) {
+ config_.android_sdks[label] = AndroidSdk::ForMinSdk(min_sdk);
return *this;
}
PostProcessingConfigurationBuilder& PostProcessingConfigurationBuilder::AddArtifact(
- const Artifact& artifact) {
- config_.artifacts.push_back(artifact);
+ configuration::ConfiguredArtifact artifact) {
+ config_.artifacts.push_back(std::move(artifact));
return *this;
}
@@ -261,34 +306,5 @@
return config_;
}
-ArtifactBuilder& ArtifactBuilder::SetName(const std::string& name) {
- artifact_.name = {name};
- return *this;
-}
-
-ArtifactBuilder& ArtifactBuilder::SetAbiGroup(const std::string& name) {
- artifact_.abi_group = {name};
- return *this;
-}
-
-ArtifactBuilder& ArtifactBuilder::SetDensityGroup(const std::string& name) {
- artifact_.screen_density_group = {name};
- return *this;
-}
-
-ArtifactBuilder& ArtifactBuilder::SetLocaleGroup(const std::string& name) {
- artifact_.locale_group = {name};
- return *this;
-}
-
-ArtifactBuilder& ArtifactBuilder::SetAndroidSdk(const std::string& name) {
- artifact_.android_sdk_group = {name};
- return *this;
-}
-
-configuration::Artifact ArtifactBuilder::Build() {
- return artifact_;
-}
-
} // namespace test
} // namespace aapt
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 4cdfc33..0d7451b 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -25,6 +25,7 @@
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "configuration/ConfigurationParser.h"
+#include "configuration/ConfigurationParser.internal.h"
#include "process/IResourceTableConsumer.h"
#include "test/Common.h"
#include "util/Maybe.h"
@@ -67,7 +68,7 @@
ResourceTableBuilder& AddValue(const android::StringPiece& name, const ConfigDescription& config,
const ResourceId& id, std::unique_ptr<Value> value);
ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id,
- SymbolState state, bool allow_new = false);
+ Visibility::Level level, bool allow_new = false);
StringPool* string_pool();
std::unique_ptr<ResourceTable> Build();
@@ -154,38 +155,52 @@
std::unique_ptr<xml::XmlResource> BuildXmlDomForPackageName(IAaptContext* context,
const android::StringPiece& str);
-class PostProcessingConfigurationBuilder {
- public:
- PostProcessingConfigurationBuilder() = default;
-
- PostProcessingConfigurationBuilder& SetAbiGroup(const std::string& name,
- const std::vector<configuration::Abi>& abis);
- PostProcessingConfigurationBuilder& SetLocaleGroup(const std::string& name,
- const std::vector<std::string>& locales);
- PostProcessingConfigurationBuilder& SetDensityGroup(const std::string& name,
- const std::vector<std::string>& densities);
- PostProcessingConfigurationBuilder& SetAndroidSdk(const std::string& name,
- const configuration::AndroidSdk& sdk);
- PostProcessingConfigurationBuilder& AddArtifact(const configuration::Artifact& artifact);
- configuration::PostProcessingConfiguration Build();
-
- private:
- configuration::PostProcessingConfiguration config_;
-};
-
class ArtifactBuilder {
public:
ArtifactBuilder() = default;
ArtifactBuilder& SetName(const std::string& name);
- ArtifactBuilder& SetAbiGroup(const std::string& name);
- ArtifactBuilder& SetDensityGroup(const std::string& name);
- ArtifactBuilder& SetLocaleGroup(const std::string& name);
- ArtifactBuilder& SetAndroidSdk(const std::string& name);
- configuration::Artifact Build();
+ ArtifactBuilder& SetVersion(int version);
+ ArtifactBuilder& AddAbi(configuration::Abi abi);
+ ArtifactBuilder& AddDensity(const ConfigDescription& density);
+ ArtifactBuilder& AddLocale(const ConfigDescription& locale);
+ ArtifactBuilder& SetAndroidSdk(int min_sdk);
+ configuration::OutputArtifact Build();
private:
- configuration::Artifact artifact_;
+ DISALLOW_COPY_AND_ASSIGN(ArtifactBuilder);
+
+ configuration::OutputArtifact artifact_;
+};
+
+class PostProcessingConfigurationBuilder {
+ public:
+ PostProcessingConfigurationBuilder() = default;
+
+ PostProcessingConfigurationBuilder& AddAbiGroup(const std::string& label,
+ std::vector<configuration::Abi> abis = {});
+ PostProcessingConfigurationBuilder& AddDensityGroup(const std::string& label,
+ std::vector<std::string> densities = {});
+ PostProcessingConfigurationBuilder& AddLocaleGroup(const std::string& label,
+ std::vector<std::string> locales = {});
+ PostProcessingConfigurationBuilder& AddDeviceFeatureGroup(const std::string& label);
+ PostProcessingConfigurationBuilder& AddGlTextureGroup(const std::string& label);
+ PostProcessingConfigurationBuilder& AddAndroidSdk(std::string label, int min_sdk);
+ PostProcessingConfigurationBuilder& AddArtifact(configuration::ConfiguredArtifact artrifact);
+
+ configuration::PostProcessingConfiguration Build();
+
+ private:
+ template <typename T>
+ inline PostProcessingConfigurationBuilder& AddGroup(const std::string& label,
+ configuration::Group<T>* group,
+ std::vector<T> to_add = {}) {
+ auto& values = GetOrCreateGroup(label, group);
+ values.insert(std::begin(values), std::begin(to_add), std::end(to_add));
+ return *this;
+ }
+
+ configuration::PostProcessingConfiguration config_;
};
} // namespace test
diff --git a/tools/aapt2/xml/XmlActionExecutor.cpp b/tools/aapt2/xml/XmlActionExecutor.cpp
index 602a902..cb844f0 100644
--- a/tools/aapt2/xml/XmlActionExecutor.cpp
+++ b/tools/aapt2/xml/XmlActionExecutor.cpp
@@ -66,7 +66,7 @@
continue;
}
- if (policy == XmlActionExecutorPolicy::kWhitelist) {
+ if (policy != XmlActionExecutorPolicy::kNone) {
DiagMessage error_msg(child_el->line_number);
error_msg << "unexpected element ";
PrintElementToDiagMessage(child_el, &error_msg);
@@ -74,8 +74,14 @@
for (const StringPiece& element : *bread_crumb) {
error_msg << "<" << element << ">";
}
- diag->Error(error_msg);
- error = true;
+ if (policy == XmlActionExecutorPolicy::kWhitelistWarning) {
+ // Treat the error only as a warning.
+ diag->Warn(error_msg);
+ } else {
+ // Policy is XmlActionExecutorPolicy::kWhitelist, we should fail.
+ diag->Error(error_msg);
+ error = true;
+ }
}
}
}
diff --git a/tools/aapt2/xml/XmlActionExecutor.h b/tools/aapt2/xml/XmlActionExecutor.h
index df70100..f689b2a 100644
--- a/tools/aapt2/xml/XmlActionExecutor.h
+++ b/tools/aapt2/xml/XmlActionExecutor.h
@@ -34,10 +34,15 @@
// Actions are run if elements are matched, errors occur only when actions return false.
kNone,
- // The actions defined must match and run. If an element is found that does
- // not match an action, an error occurs.
+ // The actions defined must match and run. If an element is found that does not match an action,
+ // an error occurs.
// Note: namespaced elements are always ignored.
kWhitelist,
+
+ // The actions defined should match and run. if an element is found that does not match an
+ // action, a warning is printed.
+ // Note: namespaced elements are always ignored.
+ kWhitelistWarning,
};
// Contains the actions to perform at this XML node. This is a recursive data structure that
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index 468864b..e301eb1 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -97,6 +97,7 @@
cflags: [
"-Wall",
"-Werror",
+ "-fexceptions",
],
export_generated_headers: ["statslog.h"],
shared_libs: [
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index f76196d..a25784d 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -111,8 +111,9 @@
return JAVA_TYPE_UNKNOWN;
case FieldDescriptor::TYPE_MESSAGE:
// TODO: not the final package name
- if (field->message_type()->full_name() == "android.os.statsd.WorkSource") {
- return JAVA_TYPE_WORK_SOURCE;
+ if (field->message_type()->full_name() ==
+ "android.os.statsd.AttributionChain") {
+ return JAVA_TYPE_ATTRIBUTION_CHAIN;
} else {
return JAVA_TYPE_OBJECT;
}
@@ -136,138 +137,153 @@
}
/**
+ * Gather the info about an atom proto.
+ */
+int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
+ vector<java_type_t> *signature) {
+
+ int errorCount = 0;
+ // Build a sorted list of the fields. Descriptor has them in source file
+ // order.
+ map<int, const FieldDescriptor *> fields;
+ for (int j = 0; j < atom->field_count(); j++) {
+ const FieldDescriptor *field = atom->field(j);
+ fields[field->number()] = field;
+ }
+
+ // Check that the parameters start at 1 and go up sequentially.
+ int expectedNumber = 1;
+ for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
+ it != fields.end(); it++) {
+ const int number = it->first;
+ const FieldDescriptor *field = it->second;
+ if (number != expectedNumber) {
+ print_error(field,
+ "Fields must be numbered consecutively starting at 1:"
+ " '%s' is %d but should be %d\n",
+ field->name().c_str(), number, expectedNumber);
+ errorCount++;
+ expectedNumber = number;
+ continue;
+ }
+ expectedNumber++;
+ }
+
+ // Check that only allowed types are present. Remove any invalid ones.
+ for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
+ it != fields.end(); it++) {
+ const FieldDescriptor *field = it->second;
+
+ java_type_t javaType = java_type(field);
+
+ if (javaType == JAVA_TYPE_UNKNOWN) {
+ print_error(field, "Unkown type for field: %s\n", field->name().c_str());
+ errorCount++;
+ continue;
+ } else if (javaType == JAVA_TYPE_OBJECT) {
+ // Allow attribution chain, but only at position 1.
+ print_error(field, "Message type not allowed for field: %s\n",
+ field->name().c_str());
+ errorCount++;
+ continue;
+ } else if (javaType == JAVA_TYPE_BYTE_ARRAY) {
+ print_error(field, "Raw bytes type not allowed for field: %s\n",
+ field->name().c_str());
+ errorCount++;
+ continue;
+ }
+ }
+
+ // Check that if there's an attribution chain, it's at position 1.
+ for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
+ it != fields.end(); it++) {
+ int number = it->first;
+ if (number != 1) {
+ const FieldDescriptor *field = it->second;
+ java_type_t javaType = java_type(field);
+ if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ print_error(
+ field,
+ "AttributionChain fields must have field id 1, in message: '%s'\n",
+ atom->name().c_str());
+ errorCount++;
+ }
+ }
+ }
+
+ // Build the type signature and the atom data.
+ for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
+ it != fields.end(); it++) {
+ const FieldDescriptor *field = it->second;
+ java_type_t javaType = java_type(field);
+
+ AtomField atField(field->name(), javaType);
+ if (javaType == JAVA_TYPE_ENUM) {
+ // All enums are treated as ints when it comes to function signatures.
+ signature->push_back(JAVA_TYPE_INT);
+ const EnumDescriptor *enumDescriptor = field->enum_type();
+ for (int i = 0; i < enumDescriptor->value_count(); i++) {
+ atField.enumValues[enumDescriptor->value(i)->number()] =
+ enumDescriptor->value(i)->name().c_str();
+ }
+ } else {
+ signature->push_back(javaType);
+ }
+ atomDecl->fields.push_back(atField);
+ }
+
+ return errorCount;
+}
+
+/**
* Gather the info about the atoms.
*/
-int
-collate_atoms(const Descriptor* descriptor, Atoms* atoms)
-{
- int errorCount = 0;
- const bool dbg = false;
+int collate_atoms(const Descriptor *descriptor, Atoms *atoms) {
+ int errorCount = 0;
+ const bool dbg = false;
- for (int i=0; i<descriptor->field_count(); i++) {
- const FieldDescriptor* atomField = descriptor->field(i);
-
- if (dbg) {
- printf(" %s (%d)\n", atomField->name().c_str(), atomField->number());
- }
-
- // StatsEvent only has one oneof, which contains only messages. Don't allow other types.
- if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
- print_error(atomField,
- "Bad type for atom. StatsEvent can only have message type fields: %s\n",
- atomField->name().c_str());
- errorCount++;
- continue;
- }
-
- const Descriptor* atom = atomField->message_type();
-
- // Build a sorted list of the fields. Descriptor has them in source file order.
- map<int,const FieldDescriptor*> fields;
- for (int j=0; j<atom->field_count(); j++) {
- const FieldDescriptor* field = atom->field(j);
- fields[field->number()] = field;
- }
-
- // Check that the parameters start at 1 and go up sequentially.
- int expectedNumber = 1;
- for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
- it != fields.end(); it++) {
- const int number = it->first;
- const FieldDescriptor* field = it->second;
- if (number != expectedNumber) {
- print_error(field, "Fields must be numbered consecutively starting at 1:"
- " '%s' is %d but should be %d\n",
- field->name().c_str(), number, expectedNumber);
- errorCount++;
- expectedNumber = number;
- continue;
- }
- expectedNumber++;
- }
-
- // Check that only allowed types are present. Remove any invalid ones.
- for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
- it != fields.end(); it++) {
- const FieldDescriptor* field = it->second;
-
- java_type_t javaType = java_type(field);
-
- if (javaType == JAVA_TYPE_UNKNOWN) {
- print_error(field, "Unkown type for field: %s\n", field->name().c_str());
- errorCount++;
- continue;
- } else if (javaType == JAVA_TYPE_OBJECT) {
- // Allow WorkSources, but only at position 1.
- print_error(field, "Message type not allowed for field: %s\n",
- field->name().c_str());
- errorCount++;
- continue;
- } else if (javaType == JAVA_TYPE_BYTE_ARRAY) {
- print_error(field, "Raw bytes type not allowed for field: %s\n",
- field->name().c_str());
- errorCount++;
- continue;
- }
- }
-
- // Check that if there's a WorkSource, it's at position 1.
- for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
- it != fields.end(); it++) {
- int number = it->first;
- if (number != 1) {
- const FieldDescriptor* field = it->second;
- java_type_t javaType = java_type(field);
- if (javaType == JAVA_TYPE_WORK_SOURCE) {
- print_error(field, "WorkSource fields must have field id 1, in message: '%s'\n",
- atom->name().c_str());
- errorCount++;
- }
- }
- }
-
- AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
-
- // Build the type signature and the atom data.
- vector<java_type_t> signature;
- for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
- it != fields.end(); it++) {
- const FieldDescriptor* field = it->second;
- java_type_t javaType = java_type(field);
-
- AtomField atField(field->name(), javaType);
- if (javaType == JAVA_TYPE_ENUM) {
- // All enums are treated as ints when it comes to function signatures.
- signature.push_back(JAVA_TYPE_INT);
- const EnumDescriptor* enumDescriptor = field->enum_type();
- for (int i = 0; i < enumDescriptor->value_count(); i++) {
- atField.enumValues[enumDescriptor->value(i)->number()] =
- enumDescriptor->value(i)->name().c_str();
- }
- } else {
- signature.push_back(javaType);
- }
- atomDecl.fields.push_back(atField);
- }
-
- atoms->signatures.insert(signature);
- atoms->decls.insert(atomDecl);
- }
+ for (int i = 0; i < descriptor->field_count(); i++) {
+ const FieldDescriptor *atomField = descriptor->field(i);
if (dbg) {
- printf("signatures = [\n");
- for (set<vector<java_type_t>>::const_iterator it = atoms->signatures.begin();
- it != atoms->signatures.end(); it++) {
- printf(" ");
- for (vector<java_type_t>::const_iterator jt = it->begin(); jt != it->end(); jt++) {
- printf(" %d", (int)*jt);
- }
- printf("\n");
- }
- printf("]\n");
+ printf(" %s (%d)\n", atomField->name().c_str(), atomField->number());
}
- return errorCount;
+ // StatsEvent only has one oneof, which contains only messages. Don't allow
+ // other types.
+ if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
+ print_error(atomField,
+ "Bad type for atom. StatsEvent can only have message type "
+ "fields: %s\n",
+ atomField->name().c_str());
+ errorCount++;
+ continue;
+ }
+
+ const Descriptor *atom = atomField->message_type();
+ AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
+ vector<java_type_t> signature;
+ errorCount += collate_atom(atom, &atomDecl, &signature);
+ atoms->signatures.insert(signature);
+ atoms->decls.insert(atomDecl);
+ }
+
+ if (dbg) {
+ printf("signatures = [\n");
+ for (set<vector<java_type_t>>::const_iterator it =
+ atoms->signatures.begin();
+ it != atoms->signatures.end(); it++) {
+ printf(" ");
+ for (vector<java_type_t>::const_iterator jt = it->begin();
+ jt != it->end(); jt++) {
+ printf(" %d", (int)*jt);
+ }
+ printf("\n");
+ }
+ printf("]\n");
+ }
+
+ return errorCount;
}
} // namespace stats_log_api_gen
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index 2f840d7..cd0625c 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -37,22 +37,21 @@
* The types for atom parameters.
*/
typedef enum {
- JAVA_TYPE_UNKNOWN = 0,
+ JAVA_TYPE_UNKNOWN = 0,
- JAVA_TYPE_WORK_SOURCE = 1,
- JAVA_TYPE_BOOLEAN = 2,
- JAVA_TYPE_INT = 3,
- JAVA_TYPE_LONG = 4,
- JAVA_TYPE_FLOAT = 5,
- JAVA_TYPE_DOUBLE = 6,
- JAVA_TYPE_STRING = 7,
- JAVA_TYPE_ENUM = 8,
+ JAVA_TYPE_ATTRIBUTION_CHAIN = 1,
+ JAVA_TYPE_BOOLEAN = 2,
+ JAVA_TYPE_INT = 3,
+ JAVA_TYPE_LONG = 4,
+ JAVA_TYPE_FLOAT = 5,
+ JAVA_TYPE_DOUBLE = 6,
+ JAVA_TYPE_STRING = 7,
+ JAVA_TYPE_ENUM = 8,
- JAVA_TYPE_OBJECT = -1,
- JAVA_TYPE_BYTE_ARRAY = -2,
+ JAVA_TYPE_OBJECT = -1,
+ JAVA_TYPE_BYTE_ARRAY = -2,
} java_type_t;
-
/**
* The name and type for an atom field.
*/
@@ -100,9 +99,11 @@
* Gather the information about the atoms. Returns the number of errors.
*/
int collate_atoms(const Descriptor* descriptor, Atoms* atoms);
+int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
+ vector<java_type_t> *signature);
} // namespace stats_log_api_gen
} // namespace android
-#endif // ANDROID_STATS_LOG_API_GEN_COLLATION_H
+#endif // ANDROID_STATS_LOG_API_GEN_COLLATION_H
\ No newline at end of file
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 423d028..5e93c08 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -24,8 +24,6 @@
using android::os::statsd::Atom;
-// TODO: Support WorkSources
-
/**
* Turn lower and camel case into upper case with underscores.
*/
@@ -97,13 +95,13 @@
}
}
-static int
-write_stats_log_cpp(FILE* out, const Atoms& atoms)
-{
+static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
+ const AtomDecl &attributionDecl) {
// Print prelude
fprintf(out, "// This file is autogenerated\n");
fprintf(out, "\n");
+ fprintf(out, "#include <exception>\n");
fprintf(out, "#include <log/log_event_list.h>\n");
fprintf(out, "#include <log/log.h>\n");
fprintf(out, "#include <statslog.h>\n");
@@ -117,15 +115,29 @@
// Print write methods
fprintf(out, "\n");
for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
- signature != atoms.signatures.end(); signature++) {
+ signature != atoms.signatures.end(); signature++) {
int argIndex;
fprintf(out, "void\n");
fprintf(out, "stats_write(int32_t code");
argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
- arg != signature->end(); arg++) {
- fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+ arg != signature->end(); arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, ", const std::vector<%s>& %s",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str());
+ } else {
+ fprintf(out, ", const %s* %s, size_t %s_length",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str(), chainField.name.c_str());
+ }
+ }
+ } else {
+ fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+ }
argIndex++;
}
fprintf(out, ")\n");
@@ -133,15 +145,40 @@
fprintf(out, "{\n");
argIndex = 1;
fprintf(out, " android_log_event_list event(kStatsEventTag);\n");
- fprintf(out, " event << code;\n");
+ fprintf(out, " event << code;\n\n");
for (vector<java_type_t>::const_iterator arg = signature->begin();
- arg != signature->end(); arg++) {
- if (*arg == JAVA_TYPE_STRING) {
- fprintf(out, " if (arg%d == NULL) {\n", argIndex);
- fprintf(out, " arg%d = \"\";\n", argIndex);
+ arg != signature->end(); arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (const auto &chainField : attributionDecl.fields) {
+ if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, " if (%s_length != %s.size()) {\n",
+ attributionDecl.fields.front().name.c_str(), chainField.name.c_str());
+ fprintf(out, " throw std::invalid_argument(\"attribution fields with"
+ " diff length: %s vs %s\");\n",
+ attributionDecl.fields.front().name.c_str(),
+ chainField.name.c_str());
+ fprintf(out, " return;\n");
+ fprintf(out, " }\n");
+ }
+ }
+ fprintf(out, "\n event.begin();\n");
+ fprintf(out, " for (size_t i = 0; i < %s_length; ++i) {\n",
+ attributionDecl.fields.front().name.c_str());
+ fprintf(out, " event.begin();\n");
+ for (const auto &chainField : attributionDecl.fields) {
+ fprintf(out, " event << %s[i];\n", chainField.name.c_str());
+ }
+ fprintf(out, " event.end();\n");
fprintf(out, " }\n");
+ fprintf(out, " event.end();\n\n");
+ } else {
+ if (*arg == JAVA_TYPE_STRING) {
+ fprintf(out, " if (arg%d == NULL) {\n", argIndex);
+ fprintf(out, " arg%d = \"\";\n", argIndex);
+ fprintf(out, " }\n");
+ }
+ fprintf(out, " event << arg%d;\n", argIndex);
}
- fprintf(out, " event << arg%d;\n", argIndex);
argIndex++;
}
@@ -160,7 +197,7 @@
static int
-write_stats_log_header(FILE* out, const Atoms& atoms)
+write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
{
// Print prelude
fprintf(out, "// This file is autogenerated\n");
@@ -168,6 +205,7 @@
fprintf(out, "#pragma once\n");
fprintf(out, "\n");
fprintf(out, "#include <stdint.h>\n");
+ fprintf(out, "#include <vector>\n");
fprintf(out, "\n");
fprintf(out, "namespace android {\n");
@@ -185,7 +223,7 @@
size_t i = 0;
// Print constants
for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
- atom != atoms.decls.end(); atom++) {
+ atom != atoms.decls.end(); atom++) {
string constant = make_constant_name(atom->name);
fprintf(out, "\n");
fprintf(out, " /**\n");
@@ -193,7 +231,21 @@
fprintf(out, " * Usage: stats_write(StatsLog.%s", constant.c_str());
for (vector<AtomField>::const_iterator field = atom->fields.begin();
field != atom->fields.end(); field++) {
- fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
+ if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, ", const std::vector<%s>& %s",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str());
+ } else {
+ fprintf(out, ", const %s* %s, size_t %s_length",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str(), chainField.name.c_str());
+ }
+ }
+ } else {
+ fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
+ }
}
fprintf(out, ");\n");
fprintf(out, " */\n");
@@ -208,8 +260,7 @@
fprintf(out, "};\n");
fprintf(out, "\n");
- fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n",
- maxPushedAtomId);
+ fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", maxPushedAtomId);
// Print write methods
fprintf(out, "//\n");
@@ -220,8 +271,21 @@
fprintf(out, "void stats_write(int32_t code ");
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
- arg != signature->end(); arg++) {
- fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+ arg != signature->end(); arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, ", const std::vector<%s>& %s",
+ cpp_type_name(chainField.javaType), chainField.name.c_str());
+ } else {
+ fprintf(out, ", const %s* %s, size_t %s_length",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str(), chainField.name.c_str());
+ }
+ }
+ } else {
+ fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+ }
argIndex++;
}
fprintf(out, ");\n");
@@ -235,7 +299,7 @@
}
static int
-write_stats_log_java(FILE* out, const Atoms& atoms)
+write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
{
// Print prelude
fprintf(out, "// This file is autogenerated\n");
@@ -247,7 +311,7 @@
fprintf(out, " * API For logging statistics events.\n");
fprintf(out, " * @hide\n");
fprintf(out, " */\n");
- fprintf(out, "public final class StatsLog {\n");
+ fprintf(out, "public class StatsLogInternal {\n");
fprintf(out, " // Constants for atom codes.\n");
// Print constants for the atom codes.
@@ -259,8 +323,15 @@
fprintf(out, " * %s %s\n", atom->message.c_str(), atom->name.c_str());
fprintf(out, " * Usage: StatsLog.write(StatsLog.%s", constant.c_str());
for (vector<AtomField>::const_iterator field = atom->fields.begin();
- field != atom->fields.end(); field++) {
- fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
+ field != atom->fields.end(); field++) {
+ if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ fprintf(out, ", %s[] %s",
+ java_type_name(chainField.javaType), chainField.name.c_str());
+ }
+ } else {
+ fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
+ }
}
fprintf(out, ");\n");
fprintf(out, " */\n");
@@ -271,33 +342,41 @@
// Print constants for the enum values.
fprintf(out, " // Constants for enum values.\n\n");
for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
- atom != atoms.decls.end(); atom++) {
+ atom != atoms.decls.end(); atom++) {
for (vector<AtomField>::const_iterator field = atom->fields.begin();
- field != atom->fields.end(); field++) {
- if (field->javaType == JAVA_TYPE_ENUM) {
- fprintf(out, " // Values for %s.%s\n", atom->message.c_str(), field->name.c_str());
- for (map<int, string>::const_iterator value = field->enumValues.begin();
- value != field->enumValues.end(); value++) {
- fprintf(out, " public static final int %s__%s__%s = %d;\n",
- make_constant_name(atom->message).c_str(),
- make_constant_name(field->name).c_str(),
- make_constant_name(value->second).c_str(),
- value->first);
+ field != atom->fields.end(); field++) {
+ if (field->javaType == JAVA_TYPE_ENUM) {
+ fprintf(out, " // Values for %s.%s\n", atom->message.c_str(),
+ field->name.c_str());
+ for (map<int, string>::const_iterator value = field->enumValues.begin();
+ value != field->enumValues.end(); value++) {
+ fprintf(out, " public static final int %s__%s__%s = %d;\n",
+ make_constant_name(atom->message).c_str(),
+ make_constant_name(field->name).c_str(),
+ make_constant_name(value->second).c_str(),
+ value->first);
+ }
+ fprintf(out, "\n");
}
- fprintf(out, "\n");
- }
}
}
// Print write methods
fprintf(out, " // Write methods\n");
for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
- signature != atoms.signatures.end(); signature++) {
+ signature != atoms.signatures.end(); signature++) {
fprintf(out, " public static native void write(int code");
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
- arg != signature->end(); arg++) {
- fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
+ arg != signature->end(); arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ fprintf(out, ", %s[] %s",
+ java_type_name(chainField.javaType), chainField.name.c_str());
+ }
+ } else {
+ fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
+ }
argIndex++;
}
fprintf(out, ");\n");
@@ -330,12 +409,25 @@
}
}
+static const char*
+jni_array_type_name(java_type_t type)
+{
+ switch (type) {
+ case JAVA_TYPE_INT:
+ return "jintArray";
+ case JAVA_TYPE_STRING:
+ return "jobjectArray";
+ default:
+ return "UNKNOWN";
+ }
+}
+
static string
jni_function_name(const vector<java_type_t>& signature)
{
string result("StatsLog_write");
for (vector<java_type_t>::const_iterator arg = signature.begin();
- arg != signature.end(); arg++) {
+ arg != signature.end(); arg++) {
switch (*arg) {
case JAVA_TYPE_BOOLEAN:
result += "_boolean";
@@ -356,6 +448,9 @@
case JAVA_TYPE_STRING:
result += "_String";
break;
+ case JAVA_TYPE_ATTRIBUTION_CHAIN:
+ result += "_AttributionChain";
+ break;
default:
result += "_UNKNOWN";
break;
@@ -387,19 +482,26 @@
}
static string
-jni_function_signature(const vector<java_type_t>& signature)
+jni_function_signature(const vector<java_type_t>& signature, const AtomDecl &attributionDecl)
{
string result("(I");
for (vector<java_type_t>::const_iterator arg = signature.begin();
- arg != signature.end(); arg++) {
- result += java_type_signature(*arg);
+ arg != signature.end(); arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ result += "[";
+ result += java_type_signature(chainField.javaType);
+ }
+ } else {
+ result += java_type_signature(*arg);
+ }
}
result += ")V";
return result;
}
static int
-write_stats_log_jni(FILE* out, const Atoms& atoms)
+write_stats_log_jni(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
{
// Print prelude
fprintf(out, "// This file is autogenerated\n");
@@ -408,6 +510,8 @@
fprintf(out, "#include <statslog.h>\n");
fprintf(out, "\n");
fprintf(out, "#include <nativehelper/JNIHelp.h>\n");
+ fprintf(out, "#include <nativehelper/ScopedUtfChars.h>\n");
+ fprintf(out, "#include <utils/Vector.h>\n");
fprintf(out, "#include \"core_jni_helpers.h\"\n");
fprintf(out, "#include \"jni.h\"\n");
fprintf(out, "\n");
@@ -419,7 +523,7 @@
// Print write methods
for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
- signature != atoms.signatures.end(); signature++) {
+ signature != atoms.signatures.end(); signature++) {
int argIndex;
fprintf(out, "static void\n");
@@ -428,7 +532,14 @@
argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
- fprintf(out, ", %s arg%d", jni_type_name(*arg), argIndex);
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ fprintf(out, ", %s %s", jni_array_type_name(chainField.javaType),
+ chainField.name.c_str());
+ }
+ } else {
+ fprintf(out, ", %s arg%d", jni_type_name(*arg), argIndex);
+ }
argIndex++;
}
fprintf(out, ")\n");
@@ -437,10 +548,11 @@
// Prepare strings
argIndex = 1;
- bool hadString = false;
+ bool hadStringOrChain = false;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
if (*arg == JAVA_TYPE_STRING) {
+ hadStringOrChain = true;
fprintf(out, " const char* str%d;\n", argIndex);
fprintf(out, " if (arg%d != NULL) {\n", argIndex);
fprintf(out, " str%d = env->GetStringUTFChars(arg%d, NULL);\n",
@@ -448,13 +560,52 @@
fprintf(out, " } else {\n");
fprintf(out, " str%d = NULL;\n", argIndex);
fprintf(out, " }\n");
- hadString = true;
+ } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ hadStringOrChain = true;
+ for (auto chainField : attributionDecl.fields) {
+ fprintf(out, " size_t %s_length = env->GetArrayLength(%s);\n",
+ chainField.name.c_str(), chainField.name.c_str());
+ if (chainField.name != attributionDecl.fields.front().name) {
+ fprintf(out, " if (%s_length != %s_length) {\n",
+ chainField.name.c_str(),
+ attributionDecl.fields.front().name.c_str());
+ fprintf(out, " jniThrowException(env, "
+ "\"java/lang/IllegalArgumentException\", "
+ "\"invalid attribution field(%s) length.\");\n",
+ chainField.name.c_str());
+ fprintf(out, " return;\n");
+ fprintf(out, " }\n");
+ }
+ if (chainField.javaType == JAVA_TYPE_INT) {
+ fprintf(out, " jint* %s_array = env->GetIntArrayElements(%s, NULL);\n",
+ chainField.name.c_str(), chainField.name.c_str());
+ } else if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, " std::vector<%s> %s_vec;\n",
+ cpp_type_name(chainField.javaType), chainField.name.c_str());
+ fprintf(out, " std::vector<ScopedUtfChars*> scoped_%s_vec;\n",
+ chainField.name.c_str());
+ fprintf(out, " for (size_t i = 0; i < %s_length; ++i) {\n",
+ chainField.name.c_str());
+ fprintf(out, " jstring jstr = "
+ "(jstring)env->GetObjectArrayElement(%s, i);\n",
+ chainField.name.c_str());
+ fprintf(out, " ScopedUtfChars* scoped_%s = "
+ "new ScopedUtfChars(env, jstr);\n",
+ chainField.name.c_str());
+ fprintf(out, " %s_vec.push_back(scoped_%s->c_str());\n",
+ chainField.name.c_str(), chainField.name.c_str());
+ fprintf(out, " scoped_%s_vec.push_back(scoped_%s);\n",
+ chainField.name.c_str(), chainField.name.c_str());
+ fprintf(out, " }\n");
+ }
+ fprintf(out, "\n");
+ }
}
argIndex++;
}
-
- // Emit this to quiet the unused parameter warning if there were no strings.
- if (!hadString) {
+ // Emit this to quiet the unused parameter warning if there were no strings or attribution
+ // chains.
+ if (!hadStringOrChain) {
fprintf(out, " (void)env;\n");
}
@@ -463,11 +614,24 @@
fprintf(out, " android::util::stats_write(code");
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
- const char* argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
- fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ if (chainField.javaType == JAVA_TYPE_INT) {
+ fprintf(out, ", (const %s*)%s_array, %s_length",
+ cpp_type_name(chainField.javaType),
+ chainField.name.c_str(), chainField.name.c_str());
+ } else if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, ", %s_vec", chainField.name.c_str());
+ }
+ }
+ } else {
+ const char *argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
+ fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
+ }
argIndex++;
}
fprintf(out, ");\n");
+ fprintf(out, "\n");
// Clean up strings
argIndex = 1;
@@ -478,6 +642,18 @@
fprintf(out, " env->ReleaseStringUTFChars(arg%d, str%d);\n",
argIndex, argIndex);
fprintf(out, " }\n");
+ } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (auto chainField : attributionDecl.fields) {
+ if (chainField.javaType == JAVA_TYPE_INT) {
+ fprintf(out, " env->ReleaseIntArrayElements(%s, %s_array, 0);\n",
+ chainField.name.c_str(), chainField.name.c_str());
+ } else if (chainField.javaType == JAVA_TYPE_STRING) {
+ fprintf(out, " for (size_t i = 0; i < %s_length; ++i) {\n",
+ chainField.name.c_str());
+ fprintf(out, " delete scoped_%s_vec[i];\n", chainField.name.c_str());
+ fprintf(out, " }\n");
+ }
+ }
}
argIndex++;
}
@@ -494,8 +670,8 @@
for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
signature != atoms.signatures.end(); signature++) {
fprintf(out, " { \"write\", \"%s\", (void*)%s },\n",
- jni_function_signature(*signature).c_str(),
- jni_function_name(*signature).c_str());
+ jni_function_signature(*signature, attributionDecl).c_str(),
+ jni_function_name(*signature).c_str());
}
fprintf(out, "};\n");
fprintf(out, "\n");
@@ -591,6 +767,11 @@
return 1;
}
+ AtomDecl attributionDecl;
+ vector<java_type_t> attributionSignature;
+ collate_atom(android::os::statsd::Attribution::descriptor(),
+ &attributionDecl, &attributionSignature);
+
// Write the .cpp file
if (cppFilename.size() != 0) {
FILE* out = fopen(cppFilename.c_str(), "w");
@@ -598,7 +779,8 @@
fprintf(stderr, "Unable to open file for write: %s\n", cppFilename.c_str());
return 1;
}
- errorCount = android::stats_log_api_gen::write_stats_log_cpp(out, atoms);
+ errorCount = android::stats_log_api_gen::write_stats_log_cpp(
+ out, atoms, attributionDecl);
fclose(out);
}
@@ -609,7 +791,8 @@
fprintf(stderr, "Unable to open file for write: %s\n", headerFilename.c_str());
return 1;
}
- errorCount = android::stats_log_api_gen::write_stats_log_header(out, atoms);
+ errorCount = android::stats_log_api_gen::write_stats_log_header(
+ out, atoms, attributionDecl);
fclose(out);
}
@@ -620,7 +803,8 @@
fprintf(stderr, "Unable to open file for write: %s\n", javaFilename.c_str());
return 1;
}
- errorCount = android::stats_log_api_gen::write_stats_log_java(out, atoms);
+ errorCount = android::stats_log_api_gen::write_stats_log_java(
+ out, atoms, attributionDecl);
fclose(out);
}
@@ -631,7 +815,8 @@
fprintf(stderr, "Unable to open file for write: %s\n", jniFilename.c_str());
return 1;
}
- errorCount = android::stats_log_api_gen::write_stats_log_jni(out, atoms);
+ errorCount = android::stats_log_api_gen::write_stats_log_jni(
+ out, atoms, attributionDecl);
fclose(out);
}
@@ -650,4 +835,4 @@
GOOGLE_PROTOBUF_VERIFY_VERSION;
return android::stats_log_api_gen::run(argc, argv);
-}
+}
\ No newline at end of file
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index 6686158..84b22cda 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -39,22 +39,22 @@
}
message AllTypesAtom {
- optional android.os.statsd.WorkSource attribution = 1;
- optional double double_field = 2;
- optional float float_field = 3;
- optional int64 int64_field = 4;
- optional uint64 uint64_field = 5;
- optional int32 int32_field = 6;
- optional fixed64 fixed64_field = 7;
- optional fixed32 fixed32_field = 8;
- optional bool bool_field = 9;
- optional string string_field = 10;
- optional uint32 uint32_field = 11;
- optional AnEnum enum_field = 12;
- optional sfixed32 sfixed32_field = 13;
- optional sfixed64 sfixed64_field = 14;
- optional sint32 sint32_field = 15;
- optional sint64 sint64_field = 16;
+ optional android.os.statsd.AttributionChain attribution_chain = 1;
+ optional double double_field = 2;
+ optional float float_field = 3;
+ optional int64 int64_field = 4;
+ optional uint64 uint64_field = 5;
+ optional int32 int32_field = 6;
+ optional fixed64 fixed64_field = 7;
+ optional fixed32 fixed32_field = 8;
+ optional bool bool_field = 9;
+ optional string string_field = 10;
+ optional uint32 uint32_field = 11;
+ optional AnEnum enum_field = 12;
+ optional sfixed32 sfixed32_field = 13;
+ optional sfixed64 sfixed64_field = 14;
+ optional sint32 sint32_field = 15;
+ optional sint64 sint64_field = 16;
}
message Event {
@@ -99,14 +99,12 @@
}
}
-message BadWorkSourcePositionAtom {
- optional int32 field1 = 1;
- optional android.os.statsd.WorkSource attribution = 2;
+message BadAttributionChainPositionAtom {
+ optional int32 field1 = 1;
+ optional android.os.statsd.AttributionChain attribution = 2;
}
-message BadWorkSourcePosition {
- oneof event {
- BadWorkSourcePositionAtom bad = 1;
- }
+message BadAttributionChainPosition {
+ oneof event { BadAttributionChainPositionAtom bad = 1; }
}
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index 073f2cf..b2b7298 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -64,7 +64,7 @@
} \
} while(0)
-/** Expects that exactly one specific field has expected enum values. */
+/** Expects that exactly one specific field has expected enum values. */
#define EXPECT_HAS_ENUM_FIELD(atom, field_name, values) \
do { \
for (vector<AtomField>::const_iterator field = atom->fields.begin(); \
@@ -95,24 +95,25 @@
EXPECT_SET_CONTAINS_SIGNATURE(atoms.signatures, JAVA_TYPE_INT, JAVA_TYPE_INT);
// AllTypesAtom
- EXPECT_SET_CONTAINS_SIGNATURE(atoms.signatures,
- JAVA_TYPE_WORK_SOURCE, // WorkSource
- JAVA_TYPE_DOUBLE, // double
- JAVA_TYPE_FLOAT, // float
- JAVA_TYPE_LONG, // int64
- JAVA_TYPE_LONG, // uint64
- JAVA_TYPE_INT, // int32
- JAVA_TYPE_LONG, // fixed64
- JAVA_TYPE_INT, // fixed32
- JAVA_TYPE_BOOLEAN, // bool
- JAVA_TYPE_STRING, // string
- JAVA_TYPE_INT, // uint32
- JAVA_TYPE_INT, // AnEnum
- JAVA_TYPE_INT, // sfixed32
- JAVA_TYPE_LONG, // sfixed64
- JAVA_TYPE_INT, // sint32
- JAVA_TYPE_LONG // sint64
- );
+ EXPECT_SET_CONTAINS_SIGNATURE(
+ atoms.signatures,
+ JAVA_TYPE_ATTRIBUTION_CHAIN, // AttributionChain
+ JAVA_TYPE_DOUBLE, // double
+ JAVA_TYPE_FLOAT, // float
+ JAVA_TYPE_LONG, // int64
+ JAVA_TYPE_LONG, // uint64
+ JAVA_TYPE_INT, // int32
+ JAVA_TYPE_LONG, // fixed64
+ JAVA_TYPE_INT, // fixed32
+ JAVA_TYPE_BOOLEAN, // bool
+ JAVA_TYPE_STRING, // string
+ JAVA_TYPE_INT, // uint32
+ JAVA_TYPE_INT, // AnEnum
+ JAVA_TYPE_INT, // sfixed32
+ JAVA_TYPE_LONG, // sfixed64
+ JAVA_TYPE_INT, // sint32
+ JAVA_TYPE_LONG // sint64
+ );
set<AtomDecl>::const_iterator atom = atoms.decls.begin();
EXPECT_EQ(1, atom->code);
@@ -187,14 +188,16 @@
}
/**
- * Test that atoms that have a WorkSource not in the first position are rejected.
+ * Test that atoms that have an attribution chain not in the first position are
+ * rejected.
*/
-TEST(CollationTest, FailBadWorkSourcePosition) {
- Atoms atoms;
- int errorCount = collate_atoms(BadWorkSourcePosition::descriptor(), &atoms);
+TEST(CollationTest, FailBadAttributionChainPosition) {
+ Atoms atoms;
+ int errorCount =
+ collate_atoms(BadAttributionChainPosition::descriptor(), &atoms);
- EXPECT_EQ(1, errorCount);
+ EXPECT_EQ(1, errorCount);
}
} // namespace stats_log_api_gen
-} // namespace android
+} // namespace android
\ No newline at end of file
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index 4e9391d..1121ead 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -32,26 +32,6 @@
shared_libs: ["libprotoc"],
}
-cc_library {
- name: "streamingflags",
- host_supported: true,
- proto: {
- export_proto_headers: true,
- include_dirs: ["external/protobuf/src"],
- },
-
- target: {
- host: {
- proto: {
- type: "full",
- },
- srcs: [
- "stream.proto",
- ],
- },
- },
-}
-
cc_binary_host {
name: "protoc-gen-javastream",
srcs: [
@@ -68,5 +48,4 @@
],
defaults: ["protoc-gen-stream-defaults"],
- static_libs: ["streamingflags"],
}
diff --git a/tools/streaming_proto/cpp/main.cpp b/tools/streaming_proto/cpp/main.cpp
index 4779020..d6b9d81 100644
--- a/tools/streaming_proto/cpp/main.cpp
+++ b/tools/streaming_proto/cpp/main.cpp
@@ -2,8 +2,6 @@
#include "stream_proto_utils.h"
#include "string_utils.h"
-#include <frameworks/base/tools/streaming_proto/stream.pb.h>
-
#include <iomanip>
#include <iostream>
#include <sstream>
@@ -12,18 +10,14 @@
using namespace google::protobuf::io;
using namespace std;
+const bool GENERATE_MAPPING = true;
+
static string
make_filename(const FileDescriptorProto& file_descriptor)
{
return file_descriptor.name() + ".h";
}
-static inline bool
-should_generate_enums_mapping(const EnumDescriptorProto& enu)
-{
- return enu.options().GetExtension(stream_enum).enable_enums_mapping();
-}
-
static void
write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent)
{
@@ -36,7 +30,7 @@
<< " = " << value.number() << ";" << endl;
}
- if (should_generate_enums_mapping(enu)) {
+ if (GENERATE_MAPPING) {
string name = make_constant_name(enu.name());
string prefix = name + "_";
text << indent << "const int _ENUM_" << name << "_COUNT = " << N << ";" << endl;
@@ -79,12 +73,6 @@
text << endl;
}
-static inline bool
-should_generate_fields_mapping(const DescriptorProto& message)
-{
- return message.options().GetExtension(stream_msg).enable_fields_mapping();
-}
-
static void
write_message(stringstream& text, const DescriptorProto& message, const string& indent)
{
@@ -112,7 +100,7 @@
write_field(text, message.field(i), indented);
}
- if (should_generate_fields_mapping(message)) {
+ if (GENERATE_MAPPING) {
N = message.field_size();
text << indented << "const int _FIELD_COUNT = " << N << ";" << endl;
text << indented << "const char* _FIELD_NAMES[" << N << "] = {" << endl;
diff --git a/tools/streaming_proto/stream.proto b/tools/streaming_proto/stream.proto
deleted file mode 100644
index c081209..0000000
--- a/tools/streaming_proto/stream.proto
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-
-import "google/protobuf/descriptor.proto";
-
-package android.stream_proto;
-
-// This option tells streaming proto plugin to compile .proto files with extra features.
-message MessageOptions {
- // creates a mapping of field names of the message to its field ids
- optional bool enable_fields_mapping = 1;
-}
-
-extend google.protobuf.MessageOptions {
- // Flags used by streaming proto plugins
- optional MessageOptions stream_msg = 126856794;
-}
-
-message EnumOptions {
- // creates a mapping of enum names to its values, strip its prefix enum type for each value
- optional bool enable_enums_mapping = 1;
-}
-
-extend google.protobuf.EnumOptions {
- // Flags used by streaming proto plugins
- optional EnumOptions stream_enum = 126856794;
-}
diff --git a/wifi/java/android/net/wifi/aware/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java
index e60f52f..7a5049d 100644
--- a/wifi/java/android/net/wifi/aware/PublishConfig.java
+++ b/wifi/java/android/net/wifi/aware/PublishConfig.java
@@ -182,7 +182,7 @@
*
* @hide
*/
- public void assertValid(Characteristics characteristics)
+ public void assertValid(Characteristics characteristics, boolean rttSupported)
throws IllegalArgumentException {
WifiAwareUtils.validateServiceName(mServiceName);
@@ -216,6 +216,10 @@
"Match filter longer than supported by device characteristics");
}
}
+
+ if (!rttSupported && mEnableRanging) {
+ throw new IllegalArgumentException("Ranging is not supported");
+ }
}
/**
@@ -364,6 +368,9 @@
* Optional. Disabled by default - i.e. any peer which attempts to measure distance to this
* device will be refused. If the peer has ranging enabled (using the
* {@link SubscribeConfig} APIs listed above, it will never discover this device.
+ * <p>
+ * The device must support Wi-Fi RTT for this feature to be used. Feature support is checked
+ * as described in {@link android.net.wifi.rtt}.
*
* @param enable If true, ranging is supported on request of the peer.
*
diff --git a/wifi/java/android/net/wifi/aware/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
index f6552a7..91f8e520 100644
--- a/wifi/java/android/net/wifi/aware/SubscribeConfig.java
+++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
@@ -224,7 +224,7 @@
*
* @hide
*/
- public void assertValid(Characteristics characteristics)
+ public void assertValid(Characteristics characteristics, boolean rttSupported)
throws IllegalArgumentException {
WifiAwareUtils.validateServiceName(mServiceName);
@@ -269,6 +269,10 @@
throw new IllegalArgumentException(
"Maximum distance must be greater than minimum distance");
}
+
+ if (!rttSupported && (mMinDistanceMmSet || mMaxDistanceMmSet)) {
+ throw new IllegalArgumentException("Ranging is not supported");
+ }
}
/**
@@ -422,6 +426,9 @@
* peer must enable ranging using
* {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will
* never be triggered.
+ * <p>
+ * The device must support Wi-Fi RTT for this feature to be used. Feature support is checked
+ * as described in {@link android.net.wifi.rtt}.
*
* @param minDistanceMm Minimum distance, in mm, to the publisher above which to trigger
* discovery.
@@ -450,6 +457,9 @@
* peer must enable ranging using
* {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will
* never be triggered.
+ * <p>
+ * The device must support Wi-Fi RTT for this feature to be used. Feature support is checked
+ * as described in {@link android.net.wifi.rtt}.
*
* @param maxDistanceMm Maximum distance, in mm, to the publisher below which to trigger
* discovery.