diff --git a/Android.bp b/Android.bp index 8a3f76c40e868cc3015798e0905af60ef2f42859..b9b1bd87e80d3682da061f305fe44d36e4d44451 100644 --- a/Android.bp +++ b/Android.bp @@ -721,6 +721,7 @@ java_defaults { "frameworks/av/camera/aidl", "frameworks/av/media/libaudioclient/aidl", "frameworks/native/aidl/gui", + "frameworks/native/libs/incidentcompanion/binder", "system/core/storaged/binder", "system/vold/binder", "system/gsid/aidl", @@ -968,7 +969,10 @@ java_library { output_params: ["store_unknown_fields=true"], include_dirs: ["external/protobuf/src"], }, - + exclude_srcs: [ + "core/proto/android/privacy.proto", + "core/proto/android/section.proto", + ], sdk_version: "current", srcs: [ "core/proto/**/*.proto", @@ -988,6 +992,10 @@ java_library { "core/proto/**/*.proto", "libs/incident/proto/android/os/**/*.proto", ], + exclude_srcs: [ + "core/proto/android/privacy.proto", + "core/proto/android/section.proto", + ], // Protos have lots of MissingOverride and similar. errorprone: { javacflags: ["-XepDisableAllChecks"], @@ -995,9 +1003,9 @@ java_library { } // ==== c++ proto device library ============================== -cc_library { - name: "libplatformprotos", - host_supported: true, +cc_defaults { + name: "libplatformprotos-defaults", + proto: { export_proto_headers: true, include_dirs: ["external/protobuf/src"], @@ -1011,8 +1019,13 @@ cc_library { srcs: [ "core/proto/**/*.proto", - "libs/incident/**/*.proto", ], +} + +cc_library { + name: "libplatformprotos", + defaults: ["libplatformprotos-defaults"], + host_supported: true, target: { host: { @@ -1024,6 +1037,9 @@ cc_library { proto: { type: "lite", }, + shared_libs: [ + "libprotobuf-cpp-lite", + ], shared: { enabled: false, }, @@ -1031,6 +1047,26 @@ cc_library { }, } +// This is the full proto version of libplatformprotos. It may only +// be used by test code that is not shipped on the device. +cc_library { + name: "libplatformprotos-test", + defaults: ["libplatformprotos-defaults"], + host_supported: false, + + target: { + android: { + proto: { + type: "full", + }, + shared: { + enabled: false, + }, + }, + }, +} + + gensrcs { name: "gen-platform-proto-constants", depfile: true, @@ -1068,6 +1104,7 @@ gensrcs { output_extension: "proto.h", } + subdirs = [ "cmds/*", "core/*", diff --git a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java index 0c30302d63a3f815c884a3dcca1845d431e529c8..1f261882b2d3a416f9918a8790219ff85f86c8bc 100644 --- a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java +++ b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java @@ -47,7 +47,7 @@ public class KernelCpuThreadReaderPerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); assertNotNull(mKernelCpuThreadReader); while (state.keepRunning()) { - this.mKernelCpuThreadReader.getCurrentProcessCpuUsage(); + this.mKernelCpuThreadReader.getProcessCpuUsage(); } } } diff --git a/apct-tests/perftests/textclassifier/Android.bp b/apct-tests/perftests/textclassifier/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..49952dc1d009d20cf137f0a173cbab699a8d7a60 --- /dev/null +++ b/apct-tests/perftests/textclassifier/Android.bp @@ -0,0 +1,25 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_test { + name: "TextClassifierPerfTests", + srcs: ["src/**/*.java"], + static_libs: [ + "androidx.test.rules", + "androidx.annotation_annotation", + "apct-perftests-utils", + ], + platform_apis: true, + test_suites: ["device-tests"], +} diff --git a/packages/SystemUI/res/drawable/ic_qs_brightness_auto_on.xml b/apct-tests/perftests/textclassifier/AndroidManifest.xml similarity index 58% rename from packages/SystemUI/res/drawable/ic_qs_brightness_auto_on.xml rename to apct-tests/perftests/textclassifier/AndroidManifest.xml index e17b5335c55d62072b57c12e9c089ae708026a71..7cf487f2eb7efb93049d60e5c5c6691891b12d70 100644 --- a/packages/SystemUI/res/drawable/ic_qs_brightness_auto_on.xml +++ b/apct-tests/perftests/textclassifier/AndroidManifest.xml @@ -1,6 +1,5 @@ - + - + + + + + + diff --git a/apct-tests/perftests/textclassifier/AndroidTest.xml b/apct-tests/perftests/textclassifier/AndroidTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..3df51b8ae67af8e018d5441cbf6fdefddafb477b --- /dev/null +++ b/apct-tests/perftests/textclassifier/AndroidTest.xml @@ -0,0 +1,28 @@ + + + + diff --git a/apct-tests/perftests/textclassifier/run.sh b/apct-tests/perftests/textclassifier/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..c6782d1a72f26122527925b22f4b775dabc807c1 --- /dev/null +++ b/apct-tests/perftests/textclassifier/run.sh @@ -0,0 +1,4 @@ +set -e +make TextClassifierPerfTests +adb shell cmd package compile -m speed -f com.android.perftests.textclassifier +adb shell am instrument -w -e class android.view.textclassifier.TextClassifierPerfTest com.android.perftests.textclassifier/androidx.test.runner.AndroidJUnitRunner diff --git a/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassifierPerfTest.java similarity index 94% rename from apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java rename to apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassifierPerfTest.java index c5d89b234209d282b67e8b181c53cd9ba98e57d7..14a121d60c2e585ef6f4c39796e5e0d283e25a9d 100644 --- a/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java +++ b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassifierPerfTest.java @@ -13,15 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.textclassifier; +package android.view.textclassifier; import android.content.Context; import android.perftests.utils.BenchmarkState; import android.perftests.utils.PerfStatusReporter; -import android.view.textclassifier.ConversationActions; -import android.view.textclassifier.TextClassificationManager; -import android.view.textclassifier.TextClassifier; -import android.view.textclassifier.TextLanguage; import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; diff --git a/api/current.txt b/api/current.txt index 5664d4f65cc4ff50d45889ae6a11c8178b0d67f8..1394ba33dddd9a13da8f7e4e0b9c95be84b04e73 100644 --- a/api/current.txt +++ b/api/current.txt @@ -102,16 +102,13 @@ package android { field public static final String NFC_TRANSACTION_EVENT = "android.permission.NFC_TRANSACTION_EVENT"; field public static final String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS"; field @Deprecated public static final String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY"; - field public static final String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS"; + field @Deprecated public static final String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS"; field public static final String READ_CALENDAR = "android.permission.READ_CALENDAR"; field public static final String READ_CALL_LOG = "android.permission.READ_CALL_LOG"; field public static final String READ_CONTACTS = "android.permission.READ_CONTACTS"; - field @Deprecated public static final String READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE"; + field public static final String READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE"; field @Deprecated public static final String READ_INPUT_STATE = "android.permission.READ_INPUT_STATE"; field public static final String READ_LOGS = "android.permission.READ_LOGS"; - field public static final String READ_MEDIA_AUDIO = "android.permission.READ_MEDIA_AUDIO"; - field public static final String READ_MEDIA_IMAGES = "android.permission.READ_MEDIA_IMAGES"; - field public static final String READ_MEDIA_VIDEO = "android.permission.READ_MEDIA_VIDEO"; field public static final String READ_PHONE_NUMBERS = "android.permission.READ_PHONE_NUMBERS"; field public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE"; field public static final String READ_SMS = "android.permission.READ_SMS"; @@ -161,7 +158,7 @@ package android { field public static final String WRITE_CALENDAR = "android.permission.WRITE_CALENDAR"; field public static final String WRITE_CALL_LOG = "android.permission.WRITE_CALL_LOG"; field public static final String WRITE_CONTACTS = "android.permission.WRITE_CONTACTS"; - field @Deprecated public static final String WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE"; + field public static final String WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE"; field public static final String WRITE_GSERVICES = "android.permission.WRITE_GSERVICES"; field public static final String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS"; field public static final String WRITE_SETTINGS = "android.permission.WRITE_SETTINGS"; @@ -177,13 +174,11 @@ package android { field public static final String CAMERA = "android.permission-group.CAMERA"; field public static final String CONTACTS = "android.permission-group.CONTACTS"; field public static final String LOCATION = "android.permission-group.LOCATION"; - field public static final String MEDIA_AURAL = "android.permission-group.MEDIA_AURAL"; - field public static final String MEDIA_VISUAL = "android.permission-group.MEDIA_VISUAL"; field public static final String MICROPHONE = "android.permission-group.MICROPHONE"; field public static final String PHONE = "android.permission-group.PHONE"; field public static final String SENSORS = "android.permission-group.SENSORS"; field public static final String SMS = "android.permission-group.SMS"; - field @Deprecated public static final String STORAGE = "android.permission-group.STORAGE"; + field public static final String STORAGE = "android.permission-group.STORAGE"; } public final class R { @@ -291,6 +286,7 @@ package android { field public static final int allowBackup = 16843392; // 0x1010280 field public static final int allowClearUserData = 16842757; // 0x1010005 field public static final int allowEmbedded = 16843765; // 0x10103f5 + field public static final int allowExternalStorageSandbox = 16844201; // 0x10105a9 field public static final int allowParallelSyncs = 16843570; // 0x1010332 field public static final int allowSingleTap = 16843353; // 0x1010259 field public static final int allowTaskReparenting = 16843268; // 0x1010204 @@ -5480,7 +5476,7 @@ package android.app { method public int describeContents(); method public boolean getAutoExpandBubble(); method @Nullable public android.app.PendingIntent getDeleteIntent(); - method public int getDesiredHeight(); + method @Dimension(unit=android.annotation.Dimension.DP) public int getDesiredHeight(); method @DimenRes public int getDesiredHeightResId(); method @NonNull public android.graphics.drawable.Icon getIcon(); method @NonNull public android.app.PendingIntent getIntent(); @@ -5494,7 +5490,7 @@ package android.app { method @NonNull public android.app.Notification.BubbleMetadata build(); method @NonNull public android.app.Notification.BubbleMetadata.Builder setAutoExpandBubble(boolean); method @NonNull public android.app.Notification.BubbleMetadata.Builder setDeleteIntent(@Nullable android.app.PendingIntent); - method @NonNull public android.app.Notification.BubbleMetadata.Builder setDesiredHeight(int); + method @NonNull public android.app.Notification.BubbleMetadata.Builder setDesiredHeight(@Dimension(unit=android.annotation.Dimension.DP) int); method @NonNull public android.app.Notification.BubbleMetadata.Builder setDesiredHeightResId(@DimenRes int); method @NonNull public android.app.Notification.BubbleMetadata.Builder setIcon(@NonNull android.graphics.drawable.Icon); method @NonNull public android.app.Notification.BubbleMetadata.Builder setIntent(@NonNull android.app.PendingIntent); @@ -6273,7 +6269,7 @@ package android.app { public final class UiAutomation { method public void adoptShellPermissionIdentity(); - method public void adoptShellPermissionIdentity(java.lang.String...); + method public void adoptShellPermissionIdentity(@Nullable java.lang.String...); method public void clearWindowAnimationFrameStats(); method public boolean clearWindowContentFrameStats(int); method public void dropShellPermissionIdentity(); @@ -6963,7 +6959,7 @@ package android.app.admin { public abstract static class DevicePolicyManager.InstallSystemUpdateCallback { ctor public DevicePolicyManager.InstallSystemUpdateCallback(); - method public void onInstallUpdateError(int, String); + method public void onInstallUpdateError(int, @NonNull String); field public static final int UPDATE_ERROR_BATTERY_LOW = 5; // 0x5 field public static final int UPDATE_ERROR_FILE_NOT_FOUND = 4; // 0x4 field public static final int UPDATE_ERROR_INCORRECT_OS_VERSION = 2; // 0x2 @@ -8604,7 +8600,6 @@ package android.bluetooth { } @Deprecated public final class BluetoothHealth implements android.bluetooth.BluetoothProfile { - ctor @Deprecated public BluetoothHealth(); method @Deprecated public boolean connectChannelToSource(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration); method @Deprecated public boolean disconnectChannel(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration, int); method @Deprecated public java.util.List getConnectedDevices(); @@ -8628,7 +8623,6 @@ package android.bluetooth { } @Deprecated public final class BluetoothHealthAppConfiguration implements android.os.Parcelable { - ctor @Deprecated public BluetoothHealthAppConfiguration(); method @Deprecated public int describeContents(); method @Deprecated public int getDataType(); method @Deprecated public String getName(); @@ -11251,7 +11245,6 @@ package android.content.pm { public class LauncherApps { method public java.util.List getActivityList(String, android.os.UserHandle); method @NonNull public java.util.List getAllPackageInstallerSessions(); - method @Nullable public android.content.pm.LauncherApps.AppUsageLimit getAppUsageLimit(@NonNull String, @NonNull android.os.UserHandle); method public android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent); method public java.util.List getProfiles(); @@ -11282,14 +11275,6 @@ package android.content.pm { field public static final String EXTRA_PIN_ITEM_REQUEST = "android.content.pm.extra.PIN_ITEM_REQUEST"; } - public static final class LauncherApps.AppUsageLimit implements android.os.Parcelable { - method public int describeContents(); - method public long getTotalUsageLimit(); - method public long getUsageRemaining(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator CREATOR; - } - public abstract static class LauncherApps.Callback { ctor public LauncherApps.Callback(); method public abstract void onPackageAdded(String, android.os.UserHandle); @@ -11453,7 +11438,7 @@ package android.content.pm { method @Nullable public android.graphics.Bitmap getAppIcon(); method @Nullable public CharSequence getAppLabel(); method @Nullable public String getAppPackageName(); - method public int[] getChildSessionIds(); + method @NonNull public int[] getChildSessionIds(); method public int getInstallLocation(); method public int getInstallReason(); method @Nullable public String getInstallerPackageName(); @@ -11534,107 +11519,107 @@ package android.content.pm { public abstract class PackageManager { ctor public PackageManager(); - method @Deprecated public abstract void addPackageToPreferred(String); - method public abstract boolean addPermission(android.content.pm.PermissionInfo); - method public abstract boolean addPermissionAsync(android.content.pm.PermissionInfo); - method @Deprecated public abstract void addPreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName); + method @Deprecated public abstract void addPackageToPreferred(@NonNull String); + method public abstract boolean addPermission(@NonNull android.content.pm.PermissionInfo); + method public abstract boolean addPermissionAsync(@NonNull android.content.pm.PermissionInfo); + method @Deprecated public abstract void addPreferredActivity(@NonNull android.content.IntentFilter, int, @Nullable android.content.ComponentName[], @NonNull android.content.ComponentName); method public abstract boolean canRequestPackageInstalls(); - method public abstract String[] canonicalToCurrentPackageNames(String[]); - method @CheckResult public abstract int checkPermission(String, String); - method @CheckResult public abstract int checkSignatures(String, String); + method public abstract String[] canonicalToCurrentPackageNames(@NonNull String[]); + method @CheckResult public abstract int checkPermission(@NonNull String, @NonNull String); + method @CheckResult public abstract int checkSignatures(@NonNull String, @NonNull String); method @CheckResult public abstract int checkSignatures(int, int); method public abstract void clearInstantAppCookie(); - method @Deprecated public abstract void clearPackagePreferredActivities(String); - method public abstract String[] currentToCanonicalPackageNames(String[]); + method @Deprecated public abstract void clearPackagePreferredActivities(@NonNull String); + method public abstract String[] currentToCanonicalPackageNames(@NonNull String[]); method public abstract void extendVerificationTimeout(int, int, long); - method public abstract android.graphics.drawable.Drawable getActivityBanner(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.graphics.drawable.Drawable getActivityBanner(android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.graphics.drawable.Drawable getActivityIcon(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.graphics.drawable.Drawable getActivityIcon(android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.content.pm.ActivityInfo getActivityInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.graphics.drawable.Drawable getActivityLogo(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.graphics.drawable.Drawable getActivityLogo(android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract java.util.List getAllPermissionGroups(int); - method public abstract android.graphics.drawable.Drawable getApplicationBanner(android.content.pm.ApplicationInfo); - method public abstract android.graphics.drawable.Drawable getApplicationBanner(String) throws android.content.pm.PackageManager.NameNotFoundException; + method @Nullable public abstract android.graphics.drawable.Drawable getActivityBanner(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException; + method @Nullable public abstract android.graphics.drawable.Drawable getActivityBanner(@NonNull android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public abstract android.graphics.drawable.Drawable getActivityIcon(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public abstract android.graphics.drawable.Drawable getActivityIcon(@NonNull android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public abstract android.content.pm.ActivityInfo getActivityInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; + method @Nullable public abstract android.graphics.drawable.Drawable getActivityLogo(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException; + method @Nullable public abstract android.graphics.drawable.Drawable getActivityLogo(@NonNull android.content.Intent) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public abstract java.util.List getAllPermissionGroups(int); + method @Nullable public abstract android.graphics.drawable.Drawable getApplicationBanner(@NonNull android.content.pm.ApplicationInfo); + method @Nullable public abstract android.graphics.drawable.Drawable getApplicationBanner(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract int getApplicationEnabledSetting(@NonNull String); - method public abstract android.graphics.drawable.Drawable getApplicationIcon(android.content.pm.ApplicationInfo); - method public abstract android.graphics.drawable.Drawable getApplicationIcon(String) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.content.pm.ApplicationInfo getApplicationInfo(String, int) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract CharSequence getApplicationLabel(android.content.pm.ApplicationInfo); - method public abstract android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo); - method public abstract android.graphics.drawable.Drawable getApplicationLogo(String) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public abstract android.graphics.drawable.Drawable getApplicationIcon(@NonNull android.content.pm.ApplicationInfo); + method @NonNull public abstract android.graphics.drawable.Drawable getApplicationIcon(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public abstract android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public abstract CharSequence getApplicationLabel(@NonNull android.content.pm.ApplicationInfo); + method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull android.content.pm.ApplicationInfo); + method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int); method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName); - method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon(); - method public abstract android.graphics.drawable.Drawable getDrawable(String, @DrawableRes int, android.content.pm.ApplicationInfo); - method public abstract java.util.List getInstalledApplications(int); + method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon(); + method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo); + method @NonNull public abstract java.util.List getInstalledApplications(int); method @NonNull public java.util.List getInstalledModules(int); - method public abstract java.util.List getInstalledPackages(int); - method @Nullable public abstract String getInstallerPackageName(String); + method @NonNull public abstract java.util.List getInstalledPackages(int); + method @Nullable public abstract String getInstallerPackageName(@NonNull String); method @NonNull public abstract byte[] getInstantAppCookie(); method public abstract int getInstantAppCookieMaxBytes(); - method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; method @Nullable public abstract android.content.Intent getLaunchIntentForPackage(@NonNull String); method @Nullable public abstract android.content.Intent getLeanbackLaunchIntentForPackage(@NonNull String); - method public android.content.pm.ModuleInfo getModuleInfo(String, int) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public android.content.pm.ModuleInfo getModuleInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException; method @Nullable public abstract String getNameForUid(int); - method public android.content.pm.PackageInfo getPackageArchiveInfo(String, int); + method @Nullable public android.content.pm.PackageInfo getPackageArchiveInfo(@NonNull String, int); method public abstract int[] getPackageGids(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract int[] getPackageGids(String, int) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.content.pm.PackageInfo getPackageInfo(String, int) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.content.pm.PackageInfo getPackageInfo(android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException; + method public abstract int[] getPackageGids(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException; + method public abstract android.content.pm.PackageInfo getPackageInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException; + method public abstract android.content.pm.PackageInfo getPackageInfo(@NonNull android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull public abstract android.content.pm.PackageInstaller getPackageInstaller(); - method public abstract int getPackageUid(String, int) throws android.content.pm.PackageManager.NameNotFoundException; + method public abstract int getPackageUid(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException; method @Nullable public abstract String[] getPackagesForUid(int); - method public abstract java.util.List getPackagesHoldingPermissions(String[], int); - method public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(String, int) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.content.pm.PermissionInfo getPermissionInfo(String, int) throws android.content.pm.PackageManager.NameNotFoundException; - method @Deprecated public abstract int getPreferredActivities(@NonNull java.util.List, @NonNull java.util.List, String); - method @Deprecated public abstract java.util.List getPreferredPackages(int); - method public abstract android.content.pm.ProviderInfo getProviderInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.content.pm.ActivityInfo getReceiverInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.content.res.Resources getResourcesForActivity(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.content.res.Resources getResourcesForApplication(String) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public abstract java.util.List getPackagesHoldingPermissions(@NonNull String[], int); + method @NonNull public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException; + method public abstract android.content.pm.PermissionInfo getPermissionInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException; + method @Deprecated public abstract int getPreferredActivities(@NonNull java.util.List, @NonNull java.util.List, @Nullable String); + method @Deprecated @NonNull public abstract java.util.List getPreferredPackages(int); + method @NonNull public abstract android.content.pm.ProviderInfo getProviderInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public abstract android.content.pm.ActivityInfo getReceiverInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public abstract android.content.res.Resources getResourcesForActivity(@NonNull android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public abstract android.content.res.Resources getResourcesForApplication(@NonNull android.content.pm.ApplicationInfo) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public abstract android.content.res.Resources getResourcesForApplication(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull public abstract android.content.pm.ServiceInfo getServiceInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull public abstract java.util.List getSharedLibraries(int); method @Nullable public android.os.Bundle getSuspendedPackageAppExtras(); method public boolean getSyntheticAppDetailsActivityEnabled(@NonNull String); - method public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures(); - method public abstract String[] getSystemSharedLibraryNames(); - method public abstract CharSequence getText(String, @StringRes int, android.content.pm.ApplicationInfo); - method public abstract android.graphics.drawable.Drawable getUserBadgedDrawableForDensity(android.graphics.drawable.Drawable, android.os.UserHandle, android.graphics.Rect, int); - method public abstract android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle); - method public abstract CharSequence getUserBadgedLabel(CharSequence, android.os.UserHandle); - method public abstract android.content.res.XmlResourceParser getXml(String, @XmlRes int, android.content.pm.ApplicationInfo); - method public boolean hasSigningCertificate(String, byte[], int); - method public boolean hasSigningCertificate(int, byte[], int); - method public abstract boolean hasSystemFeature(String); - method public abstract boolean hasSystemFeature(String, int); + method @NonNull public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures(); + method @Nullable public abstract String[] getSystemSharedLibraryNames(); + method @Nullable public abstract CharSequence getText(@NonNull String, @StringRes int, @Nullable android.content.pm.ApplicationInfo); + method @NonNull public abstract android.graphics.drawable.Drawable getUserBadgedDrawableForDensity(@NonNull android.graphics.drawable.Drawable, @NonNull android.os.UserHandle, @Nullable android.graphics.Rect, int); + method @NonNull public abstract android.graphics.drawable.Drawable getUserBadgedIcon(@NonNull android.graphics.drawable.Drawable, @NonNull android.os.UserHandle); + method @NonNull public abstract CharSequence getUserBadgedLabel(@NonNull CharSequence, @NonNull android.os.UserHandle); + method @Nullable public abstract android.content.res.XmlResourceParser getXml(@NonNull String, @XmlRes int, @Nullable android.content.pm.ApplicationInfo); + method public boolean hasSigningCertificate(@NonNull String, @NonNull byte[], int); + method public boolean hasSigningCertificate(int, @NonNull byte[], int); + method public abstract boolean hasSystemFeature(@NonNull String); + method public abstract boolean hasSystemFeature(@NonNull String, int); method public abstract boolean isInstantApp(); - method public abstract boolean isInstantApp(String); - method public boolean isPackageSuspended(String) throws android.content.pm.PackageManager.NameNotFoundException; + method public abstract boolean isInstantApp(@NonNull String); + method public boolean isPackageSuspended(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; method public boolean isPackageSuspended(); method @CheckResult public abstract boolean isPermissionRevokedByPolicy(@NonNull String, @NonNull String); method public abstract boolean isSafeMode(); - method public abstract java.util.List queryBroadcastReceivers(android.content.Intent, int); - method public abstract java.util.List queryContentProviders(String, int, int); - method public abstract java.util.List queryInstrumentation(String, int); - method public abstract java.util.List queryIntentActivities(android.content.Intent, int); - method public abstract java.util.List queryIntentActivityOptions(@Nullable android.content.ComponentName, @Nullable android.content.Intent[], android.content.Intent, int); - method public abstract java.util.List queryIntentContentProviders(android.content.Intent, int); - method public abstract java.util.List queryIntentServices(android.content.Intent, int); - method public abstract java.util.List queryPermissionsByGroup(String, int) throws android.content.pm.PackageManager.NameNotFoundException; - method @Deprecated public abstract void removePackageFromPreferred(String); - method public abstract void removePermission(String); - method public abstract android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int); - method public abstract android.content.pm.ProviderInfo resolveContentProvider(String, int); - method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int); + method @NonNull public abstract java.util.List queryBroadcastReceivers(@NonNull android.content.Intent, int); + method @NonNull public abstract java.util.List queryContentProviders(@Nullable String, int, int); + method @NonNull public abstract java.util.List queryInstrumentation(@NonNull String, int); + method @Nullable public abstract java.util.List queryIntentActivities(@NonNull android.content.Intent, int); + method @NonNull public abstract java.util.List queryIntentActivityOptions(@Nullable android.content.ComponentName, @Nullable android.content.Intent[], @NonNull android.content.Intent, int); + method @NonNull public abstract java.util.List queryIntentContentProviders(@NonNull android.content.Intent, int); + method @NonNull public abstract java.util.List queryIntentServices(@NonNull android.content.Intent, int); + method @NonNull public abstract java.util.List queryPermissionsByGroup(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException; + method @Deprecated public abstract void removePackageFromPreferred(@NonNull String); + method public abstract void removePermission(@NonNull String); + method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int); + method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int); + method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int); method public abstract void setApplicationCategoryHint(@NonNull String, int); method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public abstract void setApplicationEnabledSetting(@NonNull String, int, int); method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public abstract void setComponentEnabledSetting(@NonNull android.content.ComponentName, int, int); - method public abstract void setInstallerPackageName(String, String); + method public abstract void setInstallerPackageName(@NonNull String, @Nullable String); method public abstract void updateInstantAppCookie(@Nullable byte[]); method public abstract void verifyPendingInstall(int, int); field public static final int CERT_INPUT_RAW_X509 = 0; // 0x0 @@ -13089,10 +13074,10 @@ package android.database.sqlite { method @Deprecated public String buildUnionSubQuery(String, String[], java.util.Set, int, String, String, String[], String, String); method public int delete(@NonNull android.database.sqlite.SQLiteDatabase, @Nullable String, @Nullable String[]); method @Nullable public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory(); - method public boolean getDistinct(); method @Nullable public java.util.Map getProjectionMap(); - method public boolean getStrict(); method @Nullable public String getTables(); + method public boolean isDistinct(); + method public boolean isStrict(); method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String); method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String, String); method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String, String, android.os.CancellationSignal); @@ -14100,8 +14085,9 @@ package android.graphics { } public class ComposeShader extends android.graphics.Shader { - ctor public ComposeShader(@NonNull android.graphics.Shader, @NonNull android.graphics.Shader, @NonNull android.graphics.Xfermode); - ctor public ComposeShader(@NonNull android.graphics.Shader, @NonNull android.graphics.Shader, @NonNull android.graphics.PorterDuff.Mode); + ctor @Deprecated public ComposeShader(@NonNull android.graphics.Shader, @NonNull android.graphics.Shader, @NonNull android.graphics.Xfermode); + ctor @Deprecated public ComposeShader(@NonNull android.graphics.Shader, @NonNull android.graphics.Shader, @NonNull android.graphics.PorterDuff.Mode); + ctor public ComposeShader(@NonNull android.graphics.Shader, @NonNull android.graphics.Shader, @NonNull android.graphics.BlendMode); } public class CornerPathEffect extends android.graphics.PathEffect { @@ -14795,37 +14781,37 @@ package android.graphics { field public float y; } - public class PorterDuff { - ctor public PorterDuff(); + @Deprecated public class PorterDuff { + ctor @Deprecated public PorterDuff(); } - public enum PorterDuff.Mode { - enum_constant public static final android.graphics.PorterDuff.Mode ADD; - enum_constant public static final android.graphics.PorterDuff.Mode CLEAR; - enum_constant public static final android.graphics.PorterDuff.Mode DARKEN; - enum_constant public static final android.graphics.PorterDuff.Mode DST; - enum_constant public static final android.graphics.PorterDuff.Mode DST_ATOP; - enum_constant public static final android.graphics.PorterDuff.Mode DST_IN; - enum_constant public static final android.graphics.PorterDuff.Mode DST_OUT; - enum_constant public static final android.graphics.PorterDuff.Mode DST_OVER; - enum_constant public static final android.graphics.PorterDuff.Mode LIGHTEN; - enum_constant public static final android.graphics.PorterDuff.Mode MULTIPLY; - enum_constant public static final android.graphics.PorterDuff.Mode OVERLAY; - enum_constant public static final android.graphics.PorterDuff.Mode SCREEN; - enum_constant public static final android.graphics.PorterDuff.Mode SRC; - enum_constant public static final android.graphics.PorterDuff.Mode SRC_ATOP; - enum_constant public static final android.graphics.PorterDuff.Mode SRC_IN; - enum_constant public static final android.graphics.PorterDuff.Mode SRC_OUT; - enum_constant public static final android.graphics.PorterDuff.Mode SRC_OVER; - enum_constant public static final android.graphics.PorterDuff.Mode XOR; + @Deprecated public enum PorterDuff.Mode { + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode ADD; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode CLEAR; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode DARKEN; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode DST; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode DST_ATOP; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode DST_IN; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode DST_OUT; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode DST_OVER; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode LIGHTEN; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode MULTIPLY; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode OVERLAY; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode SCREEN; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode SRC; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode SRC_ATOP; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode SRC_IN; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode SRC_OUT; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode SRC_OVER; + enum_constant @Deprecated public static final android.graphics.PorterDuff.Mode XOR; } @Deprecated public class PorterDuffColorFilter extends android.graphics.ColorFilter { ctor @Deprecated public PorterDuffColorFilter(@ColorInt int, @NonNull android.graphics.PorterDuff.Mode); } - public class PorterDuffXfermode extends android.graphics.Xfermode { - ctor public PorterDuffXfermode(android.graphics.PorterDuff.Mode); + @Deprecated public class PorterDuffXfermode extends android.graphics.Xfermode { + ctor @Deprecated public PorterDuffXfermode(android.graphics.PorterDuff.Mode); } public interface PostProcessor { @@ -15387,7 +15373,8 @@ package android.graphics.drawable { method public boolean setState(@NonNull int[]); method public void setTint(@ColorInt int); method public void setTintList(@Nullable android.content.res.ColorStateList); - method public void setTintMode(@NonNull android.graphics.PorterDuff.Mode); + method @Deprecated public void setTintMode(@Nullable android.graphics.PorterDuff.Mode); + method public void setTintMode(@Nullable android.graphics.BlendMode); method public boolean setVisible(boolean, boolean); method public void unscheduleSelf(@NonNull Runnable); } @@ -15545,7 +15532,8 @@ package android.graphics.drawable { method public void loadDrawableAsync(android.content.Context, android.graphics.drawable.Icon.OnDrawableLoadedListener, android.os.Handler); method public android.graphics.drawable.Icon setTint(@ColorInt int); method public android.graphics.drawable.Icon setTintList(android.content.res.ColorStateList); - method public android.graphics.drawable.Icon setTintMode(android.graphics.PorterDuff.Mode); + method @Deprecated @NonNull public android.graphics.drawable.Icon setTintMode(@NonNull android.graphics.PorterDuff.Mode); + method @NonNull public android.graphics.drawable.Icon setTintMode(@NonNull android.graphics.BlendMode); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; field public static final int TYPE_ADAPTIVE_BITMAP = 5; // 0x5 @@ -17054,6 +17042,7 @@ package android.hardware.camera2 { public class CaptureFailure { method public long getFrameNumber(); + method @Nullable public String getPhysicalCameraId(); method public int getReason(); method @NonNull public android.hardware.camera2.CaptureRequest getRequest(); method public int getSequenceId(); @@ -23041,6 +23030,9 @@ package android.media { method public int getUsage(); method public int getVolumeControlStream(); method public void writeToParcel(android.os.Parcel, int); + field public static final int ALLOW_CAPTURE_BY_ALL = 1; // 0x1 + field public static final int ALLOW_CAPTURE_BY_NONE = 3; // 0x3 + field public static final int ALLOW_CAPTURE_BY_SYSTEM = 2; // 0x2 field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3 field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2 field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4 @@ -23072,7 +23064,7 @@ package android.media { ctor public AudioAttributes.Builder(); ctor public AudioAttributes.Builder(android.media.AudioAttributes); method public android.media.AudioAttributes build(); - method @NonNull public android.media.AudioAttributes.Builder setAllowCapture(boolean); + method @NonNull public android.media.AudioAttributes.Builder setAllowedCapturePolicy(int); method public android.media.AudioAttributes.Builder setContentType(int); method public android.media.AudioAttributes.Builder setFlags(int); method public android.media.AudioAttributes.Builder setLegacyStreamType(int); @@ -23273,6 +23265,7 @@ package android.media { method @Deprecated public boolean registerRemoteController(android.media.RemoteController); method @Deprecated public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int); method public int requestAudioFocus(@NonNull android.media.AudioFocusRequest); + method public void setAllowedCapturePolicy(int); method @Deprecated public void setBluetoothA2dpOn(boolean); method public void setBluetoothScoOn(boolean); method public void setMicrophoneMute(boolean); @@ -25069,7 +25062,7 @@ package android.media { field public static final String KEY_LANGUAGE = "language"; field public static final String KEY_LATENCY = "latency"; field public static final String KEY_LEVEL = "level"; - field public static final String KEY_MAX_BFRAMES = "max-bframes"; + field public static final String KEY_MAX_B_FRAMES = "max-bframes"; field public static final String KEY_MAX_FPS_TO_ENCODER = "max-fps-to-encoder"; field public static final String KEY_MAX_HEIGHT = "max-height"; field public static final String KEY_MAX_INPUT_SIZE = "max-input-size"; @@ -28712,12 +28705,12 @@ package android.net { public static class ConnectivityManager.NetworkCallback { ctor public ConnectivityManager.NetworkCallback(); - method public void onAvailable(android.net.Network); + method public void onAvailable(@NonNull android.net.Network); method public void onBlockedStatusChanged(@NonNull android.net.Network, boolean); - method public void onCapabilitiesChanged(android.net.Network, android.net.NetworkCapabilities); - method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties); - method public void onLosing(android.net.Network, int); - method public void onLost(android.net.Network); + method public void onCapabilitiesChanged(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities); + method public void onLinkPropertiesChanged(@NonNull android.net.Network, @NonNull android.net.LinkProperties); + method public void onLosing(@NonNull android.net.Network, int); + method public void onLost(@NonNull android.net.Network); method public void onUnavailable(); } @@ -28749,6 +28742,7 @@ package android.net { method @NonNull public static android.net.DnsResolver getInstance(); method public void query(@Nullable android.net.Network, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.AnswerCallback); method public void query(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.AnswerCallback); + method public void query(@Nullable android.net.Network, @NonNull String, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.InetAddressAnswerCallback); field public static final int CLASS_IN = 1; // 0x1 field public static final int FLAG_EMPTY = 0; // 0x0 field public static final int FLAG_NO_CACHE_LOOKUP = 4; // 0x4 @@ -28783,11 +28777,11 @@ package android.net { } public final class IpPrefix implements android.os.Parcelable { - method public boolean contains(java.net.InetAddress); + method public boolean contains(@NonNull java.net.InetAddress); method public int describeContents(); - method public java.net.InetAddress getAddress(); - method public int getPrefixLength(); - method public byte[] getRawAddress(); + method @NonNull public java.net.InetAddress getAddress(); + method @IntRange(from=0, to=128) public int getPrefixLength(); + method @NonNull public byte[] getRawAddress(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; } @@ -28860,7 +28854,7 @@ package android.net { method public int describeContents(); method public java.net.InetAddress getAddress(); method public int getFlags(); - method public int getPrefixLength(); + method @IntRange(from=0, to=128) public int getPrefixLength(); method public int getScope(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; @@ -29130,9 +29124,9 @@ package android.net { public final class RouteInfo implements android.os.Parcelable { method public int describeContents(); - method public android.net.IpPrefix getDestination(); - method public java.net.InetAddress getGateway(); - method public String getInterface(); + method @NonNull public android.net.IpPrefix getDestination(); + method @Nullable public java.net.InetAddress getGateway(); + method @Nullable public String getInterface(); method public boolean hasGateway(); method public boolean isDefaultRoute(); method public boolean matches(java.net.InetAddress); @@ -29267,7 +29261,6 @@ package android.net { method public abstract boolean isRelative(); method public android.net.Uri normalizeScheme(); method public static android.net.Uri parse(String); - method @NonNull public String toSafeString(); method public abstract String toString(); method public static android.net.Uri withAppendedPath(android.net.Uri, String); method public static void writeToParcel(android.os.Parcel, android.net.Uri); @@ -34652,9 +34645,11 @@ package android.os { method @NonNull public static java.io.File getRootDirectory(); method @Deprecated public static String getStorageState(java.io.File); method public static boolean isExternalStorageEmulated(); - method public static boolean isExternalStorageEmulated(java.io.File); + method public static boolean isExternalStorageEmulated(@NonNull java.io.File); method public static boolean isExternalStorageRemovable(); - method public static boolean isExternalStorageRemovable(java.io.File); + method public static boolean isExternalStorageRemovable(@NonNull java.io.File); + method public static boolean isExternalStorageSandboxed(); + method public static boolean isExternalStorageSandboxed(@NonNull java.io.File); field public static String DIRECTORY_ALARMS; field public static String DIRECTORY_AUDIOBOOKS; field public static String DIRECTORY_DCIM; @@ -38852,6 +38847,7 @@ package android.provider { field public static final String ACTION_APPLICATION_DETAILS_SETTINGS = "android.settings.APPLICATION_DETAILS_SETTINGS"; field public static final String ACTION_APPLICATION_DEVELOPMENT_SETTINGS = "android.settings.APPLICATION_DEVELOPMENT_SETTINGS"; field public static final String ACTION_APPLICATION_SETTINGS = "android.settings.APPLICATION_SETTINGS"; + field public static final String ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS = "android.settings.APP_NOTIFICATION_BUBBLE_SETTINGS"; field public static final String ACTION_APP_NOTIFICATION_SETTINGS = "android.settings.APP_NOTIFICATION_SETTINGS"; field public static final String ACTION_APP_SEARCH_SETTINGS = "android.settings.APP_SEARCH_SETTINGS"; field public static final String ACTION_APP_USAGE_SETTINGS = "android.settings.action.APP_USAGE_SETTINGS"; @@ -42523,6 +42519,7 @@ package android.system { method public static int getpid(); method public static int getppid(); method public static java.net.SocketAddress getsockname(java.io.FileDescriptor) throws android.system.ErrnoException; + method @NonNull public static android.system.StructTimeval getsockoptTimeval(@NonNull java.io.FileDescriptor, int, int) throws android.system.ErrnoException; method public static int gettid(); method public static int getuid(); method public static byte[] getxattr(String, String) throws android.system.ErrnoException; @@ -42573,6 +42570,7 @@ package android.system { method @Deprecated public static void setgid(int) throws android.system.ErrnoException; method public static int setsid() throws android.system.ErrnoException; method public static void setsockoptInt(java.io.FileDescriptor, int, int, int) throws android.system.ErrnoException; + method public static void setsockoptTimeval(@NonNull java.io.FileDescriptor, int, int, @NonNull android.system.StructTimeval) throws android.system.ErrnoException; method @Deprecated public static void setuid(int) throws android.system.ErrnoException; method public static void setxattr(String, String, byte[], int) throws android.system.ErrnoException; method public static void shutdown(java.io.FileDescriptor, int) throws android.system.ErrnoException; @@ -42778,6 +42776,10 @@ package android.system { field public static final int F_SETOWN; field public static final int F_UNLCK; field public static final int F_WRLCK; + field public static final int ICMP6_ECHO_REPLY; + field public static final int ICMP6_ECHO_REQUEST; + field public static final int ICMP_ECHO; + field public static final int ICMP_ECHOREPLY; field public static final int IFA_F_DADFAILED; field public static final int IFA_F_DEPRECATED; field public static final int IFA_F_HOMEADDRESS; @@ -43150,6 +43152,13 @@ package android.system { field public final long tv_sec; } + public final class StructTimeval { + method @NonNull public static android.system.StructTimeval fromMillis(long); + method public long toMillis(); + field public final long tv_sec; + field public final long tv_usec; + } + public final class StructUtsname { ctor public StructUtsname(String, String, String, String, String); field public final String machine; @@ -49719,8 +49728,9 @@ package android.view { method public default CharSequence getContentDescription(); method public int getGroupId(); method public android.graphics.drawable.Drawable getIcon(); + method @Nullable public default android.graphics.BlendMode getIconTintBlendMode(); method @Nullable public default android.content.res.ColorStateList getIconTintList(); - method @Nullable public default android.graphics.PorterDuff.Mode getIconTintMode(); + method @Deprecated @Nullable public default android.graphics.PorterDuff.Mode getIconTintMode(); method public android.content.Intent getIntent(); method public int getItemId(); method public android.view.ContextMenu.ContextMenuInfo getMenuInfo(); @@ -49749,7 +49759,8 @@ package android.view { method public android.view.MenuItem setIcon(android.graphics.drawable.Drawable); method public android.view.MenuItem setIcon(@DrawableRes int); method public default android.view.MenuItem setIconTintList(@Nullable android.content.res.ColorStateList); - method public default android.view.MenuItem setIconTintMode(@Nullable android.graphics.PorterDuff.Mode); + method @Deprecated @NonNull public default android.view.MenuItem setIconTintMode(@Nullable android.graphics.PorterDuff.Mode); + method @NonNull public default android.view.MenuItem setIconTintMode(@Nullable android.graphics.BlendMode); method public android.view.MenuItem setIntent(android.content.Intent); method public android.view.MenuItem setNumericShortcut(char); method public default android.view.MenuItem setNumericShortcut(char, int); @@ -50402,8 +50413,9 @@ package android.view { method public int getAutofillType(); method @Nullable public android.view.autofill.AutofillValue getAutofillValue(); method public android.graphics.drawable.Drawable getBackground(); + method @Nullable public android.graphics.BlendMode getBackgroundBlendMode(); method @Nullable public android.content.res.ColorStateList getBackgroundTintList(); - method @Nullable public android.graphics.PorterDuff.Mode getBackgroundTintMode(); + method @Deprecated @Nullable public android.graphics.PorterDuff.Mode getBackgroundTintMode(); method @android.view.ViewDebug.ExportedProperty(category="layout") public int getBaseline(); method @android.view.ViewDebug.CapturedViewProperty public final int getBottom(); method protected float getBottomFadingEdgeStrength(); @@ -50434,9 +50446,10 @@ package android.view { method public java.util.ArrayList getFocusables(int); method public void getFocusedRect(android.graphics.Rect); method public android.graphics.drawable.Drawable getForeground(); + method @Nullable public android.graphics.BlendMode getForegroundBlendMode(); method public int getForegroundGravity(); method @Nullable public android.content.res.ColorStateList getForegroundTintList(); - method @Nullable public android.graphics.PorterDuff.Mode getForegroundTintMode(); + method @Deprecated @Nullable public android.graphics.PorterDuff.Mode getForegroundTintMode(); method public boolean getGlobalVisibleRect(android.graphics.Rect, android.graphics.Point); method public final boolean getGlobalVisibleRect(android.graphics.Rect); method public android.os.Handler getHandler(); @@ -50752,7 +50765,8 @@ package android.view { method @Deprecated public void setBackgroundDrawable(android.graphics.drawable.Drawable); method public void setBackgroundResource(@DrawableRes int); method public void setBackgroundTintList(@Nullable android.content.res.ColorStateList); - method public void setBackgroundTintMode(@Nullable android.graphics.PorterDuff.Mode); + method @Deprecated public void setBackgroundTintMode(@Nullable android.graphics.PorterDuff.Mode); + method public void setBackgroundTintMode(@Nullable android.graphics.BlendMode); method public final void setBottom(int); method public void setCameraDistance(float); method public void setClickable(boolean); @@ -50779,7 +50793,8 @@ package android.view { method public void setForeground(android.graphics.drawable.Drawable); method public void setForegroundGravity(int); method public void setForegroundTintList(@Nullable android.content.res.ColorStateList); - method public void setForegroundTintMode(@Nullable android.graphics.PorterDuff.Mode); + method @Deprecated public void setForegroundTintMode(@Nullable android.graphics.PorterDuff.Mode); + method public void setForegroundTintMode(@Nullable android.graphics.BlendMode); method public void setHapticFeedbackEnabled(boolean); method public void setHasTransientState(boolean); method public void setHorizontalFadingEdgeEnabled(boolean); @@ -52299,7 +52314,7 @@ package android.view.accessibility { method public java.util.List getActionList(); method @Deprecated public int getActions(); method public java.util.List getAvailableExtraData(); - method public void getBoundsInParent(android.graphics.Rect); + method @Deprecated public void getBoundsInParent(android.graphics.Rect); method public void getBoundsInScreen(android.graphics.Rect); method public android.view.accessibility.AccessibilityNodeInfo getChild(int); method public int getChildCount(); @@ -52368,7 +52383,7 @@ package android.view.accessibility { method public boolean removeChild(android.view.View, int); method public void setAccessibilityFocused(boolean); method public void setAvailableExtraData(java.util.List); - method public void setBoundsInParent(android.graphics.Rect); + method @Deprecated public void setBoundsInParent(android.graphics.Rect); method public void setBoundsInScreen(android.graphics.Rect); method public void setCanOpenPopup(boolean); method public void setCheckable(boolean); @@ -53048,6 +53063,16 @@ package android.view.autofill { package android.view.contentcapture { + public final class ContentCaptureCondition implements android.os.Parcelable { + ctor public ContentCaptureCondition(@NonNull android.content.LocusId, int); + method public int describeContents(); + method public int getFlags(); + method @NonNull public android.content.LocusId getLocusId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + field public static final int FLAG_IS_REGEX = 2; // 0x2 + } + public final class ContentCaptureContext implements android.os.Parcelable { method public int describeContents(); method @NonNull public static android.view.contentcapture.ContentCaptureContext forLocusId(@NonNull String); @@ -53064,6 +53089,7 @@ package android.view.contentcapture { } public final class ContentCaptureManager { + method @Nullable public java.util.Set getContentCaptureConditions(); method @Nullable public android.content.ComponentName getServiceComponentName(); method public boolean isContentCaptureEnabled(); method public void removeUserData(@NonNull android.view.contentcapture.UserDataRemovalRequest); diff --git a/api/system-current.txt b/api/system-current.txt index a4cf10db3f4b7778fe138ca235aa2abf02345cff..b419851f85891f9fe827a86fbf32c090a828f9ae 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -49,6 +49,7 @@ package android { field @Deprecated public static final String BROADCAST_NETWORK_PRIVILEGED = "android.permission.BROADCAST_NETWORK_PRIVILEGED"; field public static final String CAMERA_DISABLE_TRANSMIT_LED = "android.permission.CAMERA_DISABLE_TRANSMIT_LED"; field public static final String CAPTURE_AUDIO_HOTWORD = "android.permission.CAPTURE_AUDIO_HOTWORD"; + field public static final String CAPTURE_MEDIA_OUTPUT = "android.permission.CAPTURE_MEDIA_OUTPUT"; field public static final String CAPTURE_TV_INPUT = "android.permission.CAPTURE_TV_INPUT"; field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE"; field public static final String CHANGE_DEVICE_IDLE_TEMP_WHITELIST = "android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"; @@ -76,6 +77,7 @@ package android { field public static final String HDMI_CEC = "android.permission.HDMI_CEC"; field public static final String HIDE_NON_SYSTEM_OVERLAY_WINDOWS = "android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"; field public static final String INJECT_EVENTS = "android.permission.INJECT_EVENTS"; + field public static final String INSTALL_DYNAMIC_SYSTEM = "android.permission.INSTALL_DYNAMIC_SYSTEM"; field public static final String INSTALL_GRANT_RUNTIME_PERMISSIONS = "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS"; field public static final String INSTALL_PACKAGE_UPDATES = "android.permission.INSTALL_PACKAGE_UPDATES"; field public static final String INSTALL_SELF_UPDATES = "android.permission.INSTALL_SELF_UPDATES"; @@ -117,9 +119,11 @@ package android { field public static final String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS"; field public static final String MODIFY_QUIET_MODE = "android.permission.MODIFY_QUIET_MODE"; field public static final String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE"; + field public static final String NETWORK_CARRIER_PROVISIONING = "android.permission.NETWORK_CARRIER_PROVISIONING"; field public static final String NETWORK_MANAGED_PROVISIONING = "android.permission.NETWORK_MANAGED_PROVISIONING"; field public static final String NETWORK_SCAN = "android.permission.NETWORK_SCAN"; field public static final String NETWORK_SETUP_WIZARD = "android.permission.NETWORK_SETUP_WIZARD"; + field public static final String NETWORK_SIGNAL_STRENGTH_WAKEUP = "android.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP"; field public static final String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP"; field public static final String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS"; field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE"; @@ -402,9 +406,9 @@ package android.app { method public int describeContents(); method public long getBeginTimeMillis(); method public long getEndTimeMillis(); - method public int getUidCount(); + method @IntRange(from=0) public int getUidCount(); method @Nullable public android.app.AppOpsManager.HistoricalUidOps getUidOps(int); - method @NonNull public android.app.AppOpsManager.HistoricalUidOps getUidOpsAt(int); + method @NonNull public android.app.AppOpsManager.HistoricalUidOps getUidOpsAt(@IntRange(from=0) int); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; } @@ -424,8 +428,8 @@ package android.app { public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String); - method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(int); - method public int getOpCount(); + method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int); + method @IntRange(from=0) public int getOpCount(); method @NonNull public String getPackageName(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; @@ -433,9 +437,9 @@ package android.app { public static final class AppOpsManager.HistoricalUidOps implements android.os.Parcelable { method public int describeContents(); - method public int getPackageCount(); + method @IntRange(from=0) public int getPackageCount(); method @Nullable public android.app.AppOpsManager.HistoricalPackageOps getPackageOps(@NonNull String); - method @NonNull public android.app.AppOpsManager.HistoricalPackageOps getPackageOpsAt(int); + method @NonNull public android.app.AppOpsManager.HistoricalPackageOps getPackageOpsAt(@IntRange(from=0) int); method public int getUid(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; @@ -491,12 +495,12 @@ package android.app { ctor public InstantAppResolverService(); method public final void attachBaseContext(android.content.Context); method public final android.os.IBinder onBind(android.content.Intent); - method @Deprecated public void onGetInstantAppIntentFilter(int[], String, android.app.InstantAppResolverService.InstantAppResolutionCallback); - method @Deprecated public void onGetInstantAppIntentFilter(android.content.Intent, int[], String, android.app.InstantAppResolverService.InstantAppResolutionCallback); - method public void onGetInstantAppIntentFilter(android.content.Intent, int[], android.os.UserHandle, String, android.app.InstantAppResolverService.InstantAppResolutionCallback); - method @Deprecated public void onGetInstantAppResolveInfo(int[], String, android.app.InstantAppResolverService.InstantAppResolutionCallback); - method @Deprecated public void onGetInstantAppResolveInfo(android.content.Intent, int[], String, android.app.InstantAppResolverService.InstantAppResolutionCallback); - method public void onGetInstantAppResolveInfo(android.content.Intent, int[], android.os.UserHandle, String, android.app.InstantAppResolverService.InstantAppResolutionCallback); + method @Deprecated public void onGetInstantAppIntentFilter(@Nullable int[], @NonNull String, @NonNull android.app.InstantAppResolverService.InstantAppResolutionCallback); + method @Deprecated public void onGetInstantAppIntentFilter(@NonNull android.content.Intent, @Nullable int[], @NonNull String, @NonNull android.app.InstantAppResolverService.InstantAppResolutionCallback); + method public void onGetInstantAppIntentFilter(@NonNull android.content.Intent, @Nullable int[], @NonNull android.os.UserHandle, @NonNull String, @NonNull android.app.InstantAppResolverService.InstantAppResolutionCallback); + method @Deprecated public void onGetInstantAppResolveInfo(@Nullable int[], @NonNull String, @NonNull android.app.InstantAppResolverService.InstantAppResolutionCallback); + method @Deprecated public void onGetInstantAppResolveInfo(@NonNull android.content.Intent, @Nullable int[], @NonNull String, @NonNull android.app.InstantAppResolverService.InstantAppResolutionCallback); + method public void onGetInstantAppResolveInfo(@NonNull android.content.Intent, @Nullable int[], @NonNull android.os.UserHandle, @NonNull String, @NonNull android.app.InstantAppResolverService.InstantAppResolutionCallback); } public static final class InstantAppResolverService.InstantAppResolutionCallback { @@ -563,7 +567,7 @@ package android.app { method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getStatsMetadata() throws android.app.StatsManager.StatsUnavailableException; method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void removeConfig(long) throws android.app.StatsManager.StatsUnavailableException; method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean removeConfiguration(long); - method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] setActiveConfigsChangedOperation(@Nullable android.app.PendingIntent) throws android.app.StatsManager.StatsUnavailableException; + method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) @NonNull public long[] setActiveConfigsChangedOperation(@Nullable android.app.PendingIntent) throws android.app.StatsManager.StatsUnavailableException; method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setBroadcastSubscriber(android.app.PendingIntent, long, long) throws android.app.StatsManager.StatsUnavailableException; method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent); method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setDataFetchOperation(long, android.app.PendingIntent); @@ -1256,9 +1260,9 @@ package android.bluetooth { public final class BluetoothDevice implements android.os.Parcelable { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelBondProcess(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public String getMetadata(int); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean getSilenceMode(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted(); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInSilenceMode(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean removeBond(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, String); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int); @@ -1267,7 +1271,6 @@ package android.bluetooth { field public static final int ACCESS_REJECTED = 2; // 0x2 field public static final int ACCESS_UNKNOWN = 0; // 0x0 field public static final String ACTION_SILENCE_MODE_CHANGED = "android.bluetooth.device.action.SILENCE_MODE_CHANGED"; - field public static final String EXTRA_SILENCE_ENABLED = "android.bluetooth.device.extra.SILENCE_ENABLED"; field public static final int METADATA_COMPANION_APP = 4; // 0x4 field public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; // 0x10 field public static final int METADATA_HARDWARE_VERSION = 3; // 0x3 @@ -1456,14 +1459,14 @@ package android.content.om { public final class OverlayInfo implements android.os.Parcelable { method public int describeContents(); + method @Nullable public String getCategory(); + method @NonNull public String getPackageName(); + method @Nullable public String getTargetOverlayableName(); + method @Nullable public String getTargetPackageName(); + method public int getUserId(); method public boolean isEnabled(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; - field public final String category; - field public final String packageName; - field public final String targetOverlayableName; - field public final String targetPackageName; - field public final int userId; } public class OverlayManager { @@ -1548,6 +1551,18 @@ package android.content.pm { field @NonNull public static final android.os.Parcelable.Creator CREATOR; } + public class LauncherApps { + method @Nullable public android.content.pm.LauncherApps.AppUsageLimit getAppUsageLimit(@NonNull String, @NonNull android.os.UserHandle); + } + + public static final class LauncherApps.AppUsageLimit implements android.os.Parcelable { + method public int describeContents(); + method public long getTotalUsageLimit(); + method public long getUsageRemaining(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + public class PackageInstaller { method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean); } @@ -1591,53 +1606,52 @@ package android.content.pm { } public abstract class PackageManager { - method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener); + method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void addOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener); method public abstract boolean arePermissionsIndividuallyControlled(); - method public abstract java.util.List getAllIntentFilters(String); + method @NonNull public abstract java.util.List getAllIntentFilters(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull public android.content.pm.dex.ArtManager getArtManager(); method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_SHARED_LIBRARIES) public java.util.List getDeclaredSharedLibraries(@NonNull String, int); - method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract String getDefaultBrowserPackageNameAsUser(int); + method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract String getDefaultBrowserPackageNameAsUser(int); method @Nullable @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public CharSequence getHarmfulAppWarning(@NonNull String); - method public String getIncidentReportApproverPackageName(); - method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract java.util.List getInstalledPackagesAsUser(int, int); + method @Nullable public String getIncidentReportApproverPackageName(); + method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract java.util.List getInstalledPackagesAsUser(int, int); method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_INSTANT_APPS) public abstract android.graphics.drawable.Drawable getInstantAppIcon(String); - method public abstract android.content.ComponentName getInstantAppInstallerComponent(); - method public abstract android.content.ComponentName getInstantAppResolverSettingsComponent(); + method @Nullable public abstract android.content.ComponentName getInstantAppInstallerComponent(); + method @Nullable public abstract android.content.ComponentName getInstantAppResolverSettingsComponent(); method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_INSTANT_APPS) public abstract java.util.List getInstantApps(); - method public abstract java.util.List getIntentFilterVerifications(String); - method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract int getIntentVerificationStatusAsUser(String, int); - method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(String, String, @NonNull android.os.UserHandle); + method @NonNull public abstract java.util.List getIntentFilterVerifications(@NonNull String); + method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract int getIntentVerificationStatusAsUser(@NonNull String, int); + method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] getUnsuspendablePackages(@NonNull String[]); method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); - method @Deprecated public abstract int installExistingPackage(String) throws android.content.pm.PackageManager.NameNotFoundException; - method @Deprecated public abstract int installExistingPackage(String, int) throws android.content.pm.PackageManager.NameNotFoundException; - method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle); + method @Deprecated public abstract int installExistingPackage(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; + method @Deprecated public abstract int installExistingPackage(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List queryBroadcastReceiversAsUser(@NonNull android.content.Intent, int, android.os.UserHandle); method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle); method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List queryIntentContentProvidersAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle); method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List queryIntentServicesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle); - method public abstract void registerDexModule(String, @Nullable android.content.pm.PackageManager.DexModuleRegisterCallback); - method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener); + method public abstract void registerDexModule(@NonNull String, @Nullable android.content.pm.PackageManager.DexModuleRegisterCallback); + method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener); method @Deprecated public void replacePreferredActivity(@NonNull android.content.IntentFilter, int, @NonNull java.util.List, @NonNull android.content.ComponentName); method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); method public void sendDeviceCustomizationReadyBroadcast(); - method @RequiresPermission(allOf={android.Manifest.permission.SET_PREFERRED_APPLICATIONS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public abstract boolean setDefaultBrowserPackageNameAsUser(String, int); + method @RequiresPermission(allOf={android.Manifest.permission.SET_PREFERRED_APPLICATIONS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public abstract boolean setDefaultBrowserPackageNameAsUser(@Nullable String, int); method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setDistractingPackageRestrictions(@NonNull String[], int); method @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public void setHarmfulAppWarning(@NonNull String, @Nullable CharSequence); method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String); method @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo); method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setSyntheticAppDetailsActivityEnabled(@NonNull String, boolean); - method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(String, boolean); - method @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public abstract boolean updateIntentVerificationStatusAsUser(String, int, int); - method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(String, String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle); - method @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int, int, java.util.List); + method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(@NonNull String, boolean); + method @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String, int, int); + method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle); + method @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int, int, @NonNull java.util.List); field public static final String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS"; field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES"; field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS"; field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio"; field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock"; field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20 - field public static final int FLAG_PERMISSION_HIDDEN = 1024; // 0x400 field public static final int FLAG_PERMISSION_POLICY_FIXED = 4; // 0x4 field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40 field public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8 @@ -1708,7 +1722,7 @@ package android.content.pm { method public void onPermissionsChanged(int); } - @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_HIDDEN}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags { + @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags { } public class PermissionGroupInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { @@ -3376,19 +3390,19 @@ package android.location { public class LocationManager { method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch(); + method @Nullable public String getExtraLocationControllerPackage(); method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int getGnssBatchSize(); method @Nullable public android.location.GnssCapabilities getGnssCapabilities(); - method @Nullable public String getLocationControllerExtraPackage(); method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void injectGnssMeasurementCorrections(@NonNull android.location.GnssMeasurementCorrections); - method public boolean isLocationControllerExtraPackageEnabled(); + method public boolean isExtraLocationControllerPackageEnabled(); method public boolean isLocationEnabledForUser(@NonNull android.os.UserHandle); method public boolean isProviderEnabledForUser(@NonNull String, @NonNull android.os.UserHandle); method public boolean isProviderPackage(@NonNull String); method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull android.location.LocationRequest, @NonNull android.app.PendingIntent); - method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setLocationControllerExtraPackage(@NonNull String); - method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setLocationControllerExtraPackageEnabled(boolean); + method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackage(@Nullable String); + method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackageEnabled(boolean); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle); method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setProviderEnabledForUser(@NonNull String, boolean, @NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean unregisterGnssBatchedLocationCallback(@NonNull android.location.BatchedLocationCallback); @@ -3601,6 +3615,7 @@ package android.media.audiopolicy { ctor public AudioMixingRule.Builder(); method public android.media.audiopolicy.AudioMixingRule.Builder addMixRule(int, Object) throws java.lang.IllegalArgumentException; method public android.media.audiopolicy.AudioMixingRule.Builder addRule(android.media.AudioAttributes, int) throws java.lang.IllegalArgumentException; + method @NonNull public android.media.audiopolicy.AudioMixingRule.Builder allowPrivilegedPlaybackCapture(boolean); method public android.media.audiopolicy.AudioMixingRule build(); method public android.media.audiopolicy.AudioMixingRule.Builder excludeMixRule(int, Object) throws java.lang.IllegalArgumentException; method public android.media.audiopolicy.AudioMixingRule.Builder excludeRule(android.media.AudioAttributes, int) throws java.lang.IllegalArgumentException; @@ -4058,7 +4073,7 @@ package android.net { } public final class IpPrefix implements android.os.Parcelable { - ctor public IpPrefix(@NonNull java.net.InetAddress, int); + ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); ctor public IpPrefix(@NonNull String); } @@ -4079,8 +4094,8 @@ package android.net { } public class LinkAddress implements android.os.Parcelable { - ctor public LinkAddress(java.net.InetAddress, int, int, int); - ctor public LinkAddress(@NonNull java.net.InetAddress, int); + ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int); + ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); ctor public LinkAddress(@NonNull String); ctor public LinkAddress(@NonNull String, int, int); method public boolean isGlobalPreferred(); @@ -4144,7 +4159,7 @@ package android.net { } public static class NetworkRequest.Builder { - method @NonNull public android.net.NetworkRequest.Builder setSignalStrength(int); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int); } public class NetworkScoreManager { @@ -4233,6 +4248,10 @@ package android.net { field public static final int TAG_SYSTEM_PROBE = -190; // 0xffffff42 } + public abstract class Uri implements java.lang.Comparable android.os.Parcelable { + method @NonNull public String toSafeString(); + } + public class VpnService extends android.app.Service { method @RequiresPermission(android.Manifest.permission.CONTROL_VPN) public static void prepareAndAuthorize(android.content.Context); } @@ -4257,8 +4276,8 @@ package android.net.apf { public final class ApfCapabilities implements android.os.Parcelable { ctor public ApfCapabilities(int, int, int); method public int describeContents(); - method public static boolean getApfDrop8023Frames(@NonNull android.content.Context); - method @NonNull public static int[] getApfEthTypeBlackList(@NonNull android.content.Context); + method public static boolean getApfDrop8023Frames(); + method @NonNull public static int[] getApfEtherTypeBlackList(); method public boolean hasDataAccess(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; @@ -5643,12 +5662,12 @@ package android.os.image { public class DynamicSystemClient { ctor public DynamicSystemClient(@NonNull android.content.Context); - method @RequiresPermission("android.permission.MANAGE_DYNAMIC_SYSTEM") public void bind(); + method @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void bind(); method public void setOnStatusChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.image.DynamicSystemClient.OnStatusChangedListener); method public void setOnStatusChangedListener(@NonNull android.os.image.DynamicSystemClient.OnStatusChangedListener); - method @RequiresPermission("android.permission.MANAGE_DYNAMIC_SYSTEM") public void start(@NonNull String, long); - method @RequiresPermission("android.permission.MANAGE_DYNAMIC_SYSTEM") public void start(@NonNull String, long, long); - method @RequiresPermission("android.permission.MANAGE_DYNAMIC_SYSTEM") public void unbind(); + method @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void start(@NonNull android.net.Uri, long); + method @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void start(@NonNull android.net.Uri, long, long); + method @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void unbind(); field public static final int CAUSE_ERROR_EXCEPTION = 6; // 0x6 field public static final int CAUSE_ERROR_INVALID_URL = 4; // 0x4 field public static final int CAUSE_ERROR_IO = 3; // 0x3 @@ -5664,7 +5683,7 @@ package android.os.image { } public static interface DynamicSystemClient.OnStatusChangedListener { - method public void onStatusChanged(int, int, long); + method public void onStatusChanged(int, int, long, @Nullable Throwable); } } @@ -6048,6 +6067,7 @@ package android.provider { field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS"; field public static final String ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS = "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS"; field public static final String ACTION_MANAGE_DOMAIN_URLS = "android.settings.MANAGE_DOMAIN_URLS"; + field public static final String ACTION_MANAGE_MORE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_MORE_DEFAULT_APPS_SETTINGS"; field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_DETAIL_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_DETAIL_SETTINGS"; field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE"; field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS"; @@ -6444,6 +6464,7 @@ package android.service.contentcapture { method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId); method public void onDisconnected(); method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest); + method public final void setContentCaptureConditions(@NonNull String, @Nullable java.util.Set); method public final void setContentCaptureWhitelist(@Nullable java.util.Set, @Nullable java.util.Set); field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService"; field public static final String SERVICE_META_DATA = "android.content_capture"; diff --git a/api/system-removed.txt b/api/system-removed.txt index 9780d43624fa77a4a9ef5f49a85f4387efbf6b78..162f212a787eb11d7107b0eebd1757dabbcbf11a 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -67,6 +67,8 @@ package android.location { method @Deprecated public boolean addGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener); method @Deprecated public void removeGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener); method @Deprecated public void removeGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener); + method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setLocationControllerExtraPackage(String); + method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setLocationControllerExtraPackageEnabled(boolean); } } diff --git a/api/test-current.txt b/api/test-current.txt index 9817a9747ff764071bd1850932270c72259bb0d5..cb2dc0715071577d4d3fad8469f6dcc82a158f42 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -238,9 +238,9 @@ package android.app { method public int describeContents(); method public long getBeginTimeMillis(); method public long getEndTimeMillis(); - method public int getUidCount(); + method @IntRange(from=0) public int getUidCount(); method @Nullable public android.app.AppOpsManager.HistoricalUidOps getUidOps(int); - method @NonNull public android.app.AppOpsManager.HistoricalUidOps getUidOpsAt(int); + method @NonNull public android.app.AppOpsManager.HistoricalUidOps getUidOpsAt(@IntRange(from=0) int); method public void increaseAccessCount(int, int, @NonNull String, int, int, long); method public void increaseAccessDuration(int, int, @NonNull String, int, int, long); method public void increaseRejectCount(int, int, @NonNull String, int, int, long); @@ -264,8 +264,8 @@ package android.app { public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String); - method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(int); - method public int getOpCount(); + method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int); + method @IntRange(from=0) public int getOpCount(); method @NonNull public String getPackageName(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; @@ -273,9 +273,9 @@ package android.app { public static final class AppOpsManager.HistoricalUidOps implements android.os.Parcelable { method public int describeContents(); - method public int getPackageCount(); + method @IntRange(from=0) public int getPackageCount(); method @Nullable public android.app.AppOpsManager.HistoricalPackageOps getPackageOps(@NonNull String); - method @NonNull public android.app.AppOpsManager.HistoricalPackageOps getPackageOpsAt(int); + method @NonNull public android.app.AppOpsManager.HistoricalPackageOps getPackageOpsAt(@IntRange(from=0) int); method public int getUid(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; @@ -315,7 +315,9 @@ package android.app { } public final class NotificationChannel implements android.os.Parcelable { + method public boolean isImportanceLockedByCriticalDeviceFunction(); method public boolean isImportanceLockedByOEM(); + method public void setImportanceLockedByCriticalDeviceFunction(boolean); method public void setImportanceLockedByOEM(boolean); } @@ -661,26 +663,27 @@ package android.content.pm { public abstract class PackageManager { method public abstract boolean arePermissionsIndividuallyControlled(); - method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract String getDefaultBrowserPackageNameAsUser(int); - method public String getIncidentReportApproverPackageName(); - method public abstract int getInstallReason(String, @NonNull android.os.UserHandle); - method public abstract java.util.List getInstalledApplicationsAsUser(int, int); - method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract java.util.List getInstalledPackagesAsUser(int, int); + method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract String getDefaultBrowserPackageNameAsUser(int); + method @Nullable public String getIncidentReportApproverPackageName(); + method public abstract int getInstallReason(@NonNull String, @NonNull android.os.UserHandle); + method @NonNull public abstract java.util.List getInstalledApplicationsAsUser(int, int); + method @NonNull @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract java.util.List getInstalledPackagesAsUser(int, int); method @Nullable public abstract String[] getNamesForUids(int[]); - method public abstract String getPermissionControllerPackageName(); - method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS", "android.permission.GET_RUNTIME_PERMISSIONS"}) public abstract int getPermissionFlags(String, String, @NonNull android.os.UserHandle); + method @NonNull public abstract String getPermissionControllerPackageName(); + method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS", "android.permission.GET_RUNTIME_PERMISSIONS"}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); method @NonNull public abstract String getServicesSystemSharedLibraryPackageName(); method @NonNull public abstract String getSharedSystemSharedLibraryPackageName(); - method public String getWellbeingPackageName(); + method @Nullable public String getWellbeingPackageName(); method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); - method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS"}) public abstract void updatePermissionFlags(String, String, int, int, @NonNull android.os.UserHandle); + method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS"}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, int, int, @NonNull android.os.UserHandle); field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage"; field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption"; - field public static final int FLAG_PERMISSION_HIDDEN = 1024; // 0x400 + field public static final int FLAG_PERMISSION_POLICY_FIXED = 4; // 0x4 field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40 field public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8 field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80 + field public static final int FLAG_PERMISSION_SYSTEM_FIXED = 16; // 0x10 field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2 field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1 field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 @@ -1237,7 +1240,7 @@ package android.net { } public final class IpPrefix implements android.os.Parcelable { - ctor public IpPrefix(@NonNull java.net.InetAddress, int); + ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); ctor public IpPrefix(@NonNull String); } @@ -1246,8 +1249,8 @@ package android.net { } public class LinkAddress implements android.os.Parcelable { - ctor public LinkAddress(java.net.InetAddress, int, int, int); - ctor public LinkAddress(@NonNull java.net.InetAddress, int); + ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int); + ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); ctor public LinkAddress(@NonNull String); ctor public LinkAddress(@NonNull String, int, int); method public boolean isGlobalPreferred(); @@ -1356,8 +1359,8 @@ package android.net.apf { public final class ApfCapabilities implements android.os.Parcelable { ctor public ApfCapabilities(int, int, int); method public int describeContents(); - method public static boolean getApfDrop8023Frames(@NonNull android.content.Context); - method @NonNull public static int[] getApfEthTypeBlackList(@NonNull android.content.Context); + method public static boolean getApfDrop8023Frames(); + method @NonNull public static int[] getApfEtherTypeBlackList(); method public boolean hasDataAccess(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; @@ -2457,6 +2460,7 @@ package android.service.contentcapture { method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId); method public void onDisconnected(); method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest); + method public final void setContentCaptureConditions(@NonNull String, @Nullable java.util.Set); method public final void setContentCaptureWhitelist(@Nullable java.util.Set, @Nullable java.util.Set); field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService"; field public static final String SERVICE_META_DATA = "android.content_capture"; @@ -2662,6 +2666,15 @@ package android.telephony { method public void setVoiceRoamingType(int); } + public final class SmsManager { + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int checkSmsShortCodeDestination(String, String); + field public static final int SMS_CATEGORY_FREE_SHORT_CODE = 1; // 0x1 + field public static final int SMS_CATEGORY_NOT_SHORT_CODE = 0; // 0x0 + field public static final int SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3; // 0x3 + field public static final int SMS_CATEGORY_PREMIUM_SHORT_CODE = 4; // 0x4 + field public static final int SMS_CATEGORY_STANDARD_SHORT_CODE = 2; // 0x2 + } + public class TelephonyManager { method public int checkCarrierPrivilegesForPackage(String); method public int getCarrierIdListVersion(); diff --git a/cmds/bmgr/Android.bp b/cmds/bmgr/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..b64923bcbe1b177bb7295a36f59e3d4f0e568c84 --- /dev/null +++ b/cmds/bmgr/Android.bp @@ -0,0 +1,8 @@ +// Copyright 2007 The Android Open Source Project +// + +java_binary { + name: "bmgr", + wrapper: "bmgr", + srcs: ["**/*.java"], +} diff --git a/cmds/bmgr/Android.mk b/cmds/bmgr/Android.mk deleted file mode 100644 index d520cf2143ee6d02a8d9f8a8f3bfdded66568f52..0000000000000000000000000000000000000000 --- a/cmds/bmgr/Android.mk +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2007 The Android Open Source Project -# -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_MODULE := bmgrlib -LOCAL_MODULE_STEM := bmgr -include $(BUILD_JAVA_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := bmgr -LOCAL_MODULE_CLASS := EXECUTABLES -LOCAL_SRC_FILES := bmgr -LOCAL_REQUIRED_MODULES := bmgrlib -include $(BUILD_PREBUILT) diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index a6c7cae9bdb6cc1a751d43885d5ab5362139b916..8e7277c55ed8beb20d07a8b466fd31747dd06a23 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -101,7 +101,7 @@ static constexpr size_t TEXT_POS_LEN_MAX = 16; BootAnimation::BootAnimation(sp callbacks) : Thread(false), mClockEnabled(true), mTimeIsAccurate(false), - mTimeFormat12Hour(false), mTimeCheckThread(NULL), mCallbacks(callbacks) { + mTimeFormat12Hour(false), mTimeCheckThread(nullptr), mCallbacks(callbacks) { mSession = new SurfaceComposerClient(); std::string powerCtl = android::base::GetProperty("sys.powerctl", ""); @@ -156,7 +156,7 @@ void BootAnimation::binderDied(const wp&) status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, const char* name) { Asset* asset = assets.open(name, Asset::ACCESS_BUFFER); - if (asset == NULL) + if (asset == nullptr) return NO_INIT; SkBitmap bitmap; sk_sp data = SkData::MakeWithoutCopy(asset->getBuffer(false), @@ -234,7 +234,7 @@ status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) case kN32_SkColorType: if (!mUseNpotTextures && (tw != w || th != h)) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, - GL_UNSIGNED_BYTE, 0); + GL_UNSIGNED_BYTE, nullptr); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p); } else { @@ -246,7 +246,7 @@ status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) case kRGB_565_SkColorType: if (!mUseNpotTextures && (tw != w || th != h)) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB, - GL_UNSIGNED_SHORT_5_6_5, 0); + GL_UNSIGNED_SHORT_5_6_5, nullptr); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p); } else { @@ -304,10 +304,10 @@ status_t BootAnimation::readyToRun() { EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - eglInitialize(display, 0, 0); + eglInitialize(display, nullptr, nullptr); eglChooseConfig(display, attribs, &config, 1, &numConfigs); - surface = eglCreateWindowSurface(display, config, s.get(), NULL); - context = eglCreateContext(display, config, NULL, NULL); + surface = eglCreateWindowSurface(display, config, s.get(), nullptr); + context = eglCreateContext(display, config, nullptr, nullptr); eglQuerySurface(display, surface, EGL_WIDTH, &w); eglQuerySurface(display, surface, EGL_HEIGHT, &h); @@ -671,7 +671,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) // Parse the description file for (;;) { const char* endl = strstr(s, "\n"); - if (endl == NULL) break; + if (endl == nullptr) break; String8 line(s, endl - s); const char* l = line.string(); int fps = 0; @@ -699,8 +699,8 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) part.count = count; part.pause = pause; part.path = path; - part.audioData = NULL; - part.animation = NULL; + part.audioData = nullptr; + part.animation = nullptr; if (!parseColor(color, part.backgroundColor)) { SLOGE("> invalid color '#%s'", color); part.backgroundColor[0] = 0.0f; @@ -716,9 +716,9 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) part.playUntilComplete = false; part.count = 1; part.pause = 0; - part.audioData = NULL; + part.audioData = nullptr; part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE)); - if (part.animation != NULL) + if (part.animation != nullptr) animation.parts.add(part); } s = ++endl; @@ -731,7 +731,7 @@ bool BootAnimation::preloadZip(Animation& animation) { // read all the data structures const size_t pcount = animation.parts.size(); - void *cookie = NULL; + void *cookie = nullptr; ZipFileRO* zip = animation.zip; if (!zip->startIteration(&cookie)) { return false; @@ -739,7 +739,7 @@ bool BootAnimation::preloadZip(Animation& animation) ZipEntryRO entry; char name[ANIM_ENTRY_NAME_MAX]; - while ((entry = zip->nextEntry(cookie)) != NULL) { + while ((entry = zip->nextEntry(cookie)) != nullptr) { const int foundEntryName = zip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX); if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) { SLOGE("Error fetching entry file name"); @@ -762,7 +762,7 @@ bool BootAnimation::preloadZip(Animation& animation) if (path == animation.parts[j].path) { uint16_t method; // supports only stored png files - if (zip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) { + if (zip->getEntryInfo(entry, &method, nullptr, nullptr, nullptr, nullptr, nullptr)) { if (method == ZipFileRO::kCompressStored) { FileMap* map = zip->createEntryFileMap(entry); if (map) { @@ -800,7 +800,7 @@ bool BootAnimation::preloadZip(Animation& animation) for (size_t frameIdx = 0; frameIdx < part.frames.size(); frameIdx++) { const char* endl = strstr(trimDataStr, "\n"); // No more trimData for this part. - if (endl == NULL) { + if (endl == nullptr) { break; } String8 line(trimDataStr, endl - trimDataStr); @@ -927,7 +927,7 @@ bool BootAnimation::playAnimation(const Animation& animation) glBindTexture(GL_TEXTURE_2D, 0); // Handle animation package - if (part.animation != NULL) { + if (part.animation != nullptr) { playAnimation(*part.animation); if (exitPending()) break; @@ -1001,7 +1001,7 @@ bool BootAnimation::playAnimation(const Animation& animation) spec.tv_nsec = (now + delay) % 1000000000; int err; do { - err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL); + err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, nullptr); } while (err<0 && errno == EINTR); } @@ -1090,13 +1090,13 @@ BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn) if (mLoadedFiles.indexOf(fn) >= 0) { SLOGE("File \"%s\" is already loaded. Cyclic ref is not allowed", fn.string()); - return NULL; + return nullptr; } ZipFileRO *zip = ZipFileRO::open(fn); - if (zip == NULL) { + if (zip == nullptr) { SLOGE("Failed to open animation zip \"%s\": %s", fn.string(), strerror(errno)); - return NULL; + return nullptr; } Animation *animation = new Animation; @@ -1107,7 +1107,7 @@ BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn) parseAnimationDesc(*animation); if (!preloadZip(*animation)) { - return NULL; + return nullptr; } @@ -1135,7 +1135,7 @@ bool BootAnimation::updateIsTimeAccurate() { } FILE* file = fopen(LAST_TIME_CHANGED_FILE_PATH, "r"); - if (file != NULL) { + if (file != nullptr) { long long lastChangedTime = 0; fscanf(file, "%lld", &lastChangedTime); fclose(file); diff --git a/cmds/bootanimation/audioplay.cpp b/cmds/bootanimation/audioplay.cpp index 874aab08862ed7e0dbf9306910c68fdbeace02a4..c5e16c6b7deb68ccf51d8e73c4600dfee911c69b 100644 --- a/cmds/bootanimation/audioplay.cpp +++ b/cmds/bootanimation/audioplay.cpp @@ -39,14 +39,14 @@ namespace { using namespace android; // engine interfaces -static SLObjectItf engineObject = NULL; +static SLObjectItf engineObject = nullptr; static SLEngineItf engineEngine; // output mix interfaces -static SLObjectItf outputMixObject = NULL; +static SLObjectItf outputMixObject = nullptr; // buffer queue player interfaces -static SLObjectItf bqPlayerObject = NULL; +static SLObjectItf bqPlayerObject = nullptr; static SLPlayItf bqPlayerPlay; static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue; static SLMuteSoloItf bqPlayerMuteSolo; @@ -89,7 +89,7 @@ void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) { } bool hasPlayer() { - return (engineObject != NULL && bqPlayerObject != NULL); + return (engineObject != nullptr && bqPlayerObject != nullptr); } // create the engine and output mix objects @@ -97,7 +97,7 @@ bool createEngine() { SLresult result; // create engine - result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); + result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr); if (result != SL_RESULT_SUCCESS) { ALOGE("slCreateEngine failed with result %d", result); return false; @@ -121,7 +121,7 @@ bool createEngine() { (void)result; // create output mix - result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL); + result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, nullptr, nullptr); if (result != SL_RESULT_SUCCESS) { ALOGE("sl engine CreateOutputMix failed with result %d", result); return false; @@ -173,7 +173,7 @@ bool createBufferQueueAudioPlayer(const ChunkFormat* chunkFormat) { // configure audio sink SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject}; - SLDataSink audioSnk = {&loc_outmix, NULL}; + SLDataSink audioSnk = {&loc_outmix, nullptr}; // create audio player const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_ANDROIDCONFIGURATION}; @@ -236,7 +236,7 @@ bool createBufferQueueAudioPlayer(const ChunkFormat* chunkFormat) { (void)result; // register callback on the buffer queue - result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL); + result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, nullptr); if (result != SL_RESULT_SUCCESS) { ALOGE("sl bqPlayerBufferQueue RegisterCallback failed with result %d", result); return false; @@ -261,7 +261,7 @@ bool parseClipBuf(const uint8_t* clipBuf, int clipBufSize, const ChunkFormat** o const uint8_t** oSoundBuf, unsigned* oSoundBufSize) { *oSoundBuf = clipBuf; *oSoundBufSize = clipBufSize; - *oChunkFormat = NULL; + *oChunkFormat = nullptr; const RiffWaveHeader* wavHeader = (const RiffWaveHeader*)*oSoundBuf; if (*oSoundBufSize < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) || (wavHeader->wave_id != ID_WAVE)) { @@ -303,7 +303,7 @@ bool parseClipBuf(const uint8_t* clipBuf, int clipBufSize, const ChunkFormat** o } } - if (*oChunkFormat == NULL) { + if (*oChunkFormat == nullptr) { ALOGE("format not found in WAV file"); return false; } @@ -435,7 +435,7 @@ void setPlaying(bool isPlaying) { SLresult result; - if (NULL != bqPlayerPlay) { + if (nullptr != bqPlayerPlay) { // set the player's state result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, isPlaying ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_STOPPED); @@ -445,28 +445,28 @@ void setPlaying(bool isPlaying) { void destroy() { // destroy buffer queue audio player object, and invalidate all associated interfaces - if (bqPlayerObject != NULL) { + if (bqPlayerObject != nullptr) { CHATTY("destroying audio player"); (*bqPlayerObject)->Destroy(bqPlayerObject); - bqPlayerObject = NULL; - bqPlayerPlay = NULL; - bqPlayerBufferQueue = NULL; - bqPlayerMuteSolo = NULL; - bqPlayerVolume = NULL; + bqPlayerObject = nullptr; + bqPlayerPlay = nullptr; + bqPlayerBufferQueue = nullptr; + bqPlayerMuteSolo = nullptr; + bqPlayerVolume = nullptr; } // destroy output mix object, and invalidate all associated interfaces - if (outputMixObject != NULL) { + if (outputMixObject != nullptr) { (*outputMixObject)->Destroy(outputMixObject); - outputMixObject = NULL; + outputMixObject = nullptr; } // destroy engine object, and invalidate all associated interfaces - if (engineObject != NULL) { + if (engineObject != nullptr) { CHATTY("destroying audio engine"); (*engineObject)->Destroy(engineObject); - engineObject = NULL; - engineEngine = NULL; + engineObject = nullptr; + engineEngine = nullptr; } } diff --git a/cmds/bootanimation/iot/iotbootanimation_main.cpp b/cmds/bootanimation/iot/iotbootanimation_main.cpp index 2a3d3766ab38a14bb8e617d580651163f45e486d..ae3529796b9af84a5168da0c5513e54e43ae07e8 100644 --- a/cmds/bootanimation/iot/iotbootanimation_main.cpp +++ b/cmds/bootanimation/iot/iotbootanimation_main.cpp @@ -60,7 +60,7 @@ public: mBootAction = new BootAction(); if (!mBootAction->init(library_path, mBootParameters)) { - mBootAction = NULL; + mBootAction = nullptr; } }; diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index d757e4611158a2d487c758e1a33fc3dcfeb7a28c..18d56fa87ccab1d389d4a5c7b4e7dfdbe6b44f50 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -84,6 +84,7 @@ cc_test { "-readability-magic-numbers", ], host_supported: true, + test_suites: ["general-tests"], srcs: [ "tests/BinaryStreamVisitorTests.cpp", "tests/CommandLineOptionsTests.cpp", diff --git a/cmds/incident/Android.bp b/cmds/incident/Android.bp index 2a5ec5bfacafc6d06d48573dee0b470310cdb406..f56f101465eb0b0f7e04b7c9b7a31091468aae12 100644 --- a/cmds/incident/Android.bp +++ b/cmds/incident/Android.bp @@ -29,6 +29,10 @@ cc_binary { "libincident", ], + static_libs: [ + "libplatformprotos", + ], + cflags: [ "-Wall", "-Werror", diff --git a/cmds/incident/main.cpp b/cmds/incident/main.cpp index cdec6a01d086d80f489c0ebeacdab173dfd9f060..93e592c9c01b9e93315678063ca8327be92b3c88 100644 --- a/cmds/incident/main.cpp +++ b/cmds/incident/main.cpp @@ -68,6 +68,7 @@ Status StatusListener::onReportSectionStatus(int32_t section, int32_t status) { fprintf(stderr, "section %d status %d\n", section, status); + ALOGD("section %d status %d\n", section, status); return Status::ok(); } @@ -75,6 +76,7 @@ Status StatusListener::onReportServiceStatus(const String16& service, int32_t status) { fprintf(stderr, "service '%s' status %d\n", String8(service).string(), status); + ALOGD("service '%s' status %d\n", String8(service).string(), status); return Status::ok(); } @@ -82,6 +84,7 @@ Status StatusListener::onReportFinished() { fprintf(stderr, "done\n"); + ALOGD("done\n"); exit(0); return Status::ok(); } @@ -90,6 +93,7 @@ Status StatusListener::onReportFailed() { fprintf(stderr, "failed\n"); + ALOGD("failed\n"); exit(1); return Status::ok(); } @@ -146,24 +150,49 @@ find_section(const char* name) // ================================================================================ static int -get_dest(const char* arg) +get_privacy_policy(const char* arg) { if (strcmp(arg, "L") == 0 || strcmp(arg, "LOCAL") == 0) { - return DEST_LOCAL; + return PRIVACY_POLICY_LOCAL; } if (strcmp(arg, "E") == 0 || strcmp(arg, "EXPLICIT") == 0) { - return DEST_EXPLICIT; + return PRIVACY_POLICY_EXPLICIT; } if (strcmp(arg, "A") == 0 || strcmp(arg, "AUTO") == 0 || strcmp(arg, "AUTOMATIC") == 0) { - return DEST_AUTOMATIC; + return PRIVACY_POLICY_AUTOMATIC; } return -1; // return the default value } +// ================================================================================ +static bool +parse_receiver_arg(const string& arg, string* pkg, string* cls) +{ + if (arg.length() == 0) { + return true; + } + size_t slash = arg.find('/'); + if (slash == string::npos) { + return false; + } + if (slash == 0 || slash == arg.length() - 1) { + return false; + } + if (arg.find('/', slash+1) != string::npos) { + return false; + } + pkg->assign(arg, 0, slash); + cls->assign(arg, slash+1); + if ((*cls)[0] == '.') { + *cls = (*pkg) + (*cls); + } + return true; +} + // ================================================================================ static void usage(FILE* out) @@ -173,10 +202,13 @@ usage(FILE* out) fprintf(out, "Takes an incident report.\n"); fprintf(out, "\n"); fprintf(out, "OPTIONS\n"); + fprintf(out, " -l list available sections\n"); + fprintf(out, " -p privacy spec, LOCAL, EXPLICIT or AUTOMATIC. Default AUTOMATIC.\n"); + fprintf(out, "\n"); + fprintf(out, "and one of these destinations:\n"); fprintf(out, " -b (default) print the report to stdout (in proto format)\n"); fprintf(out, " -d send the report into dropbox\n"); - fprintf(out, " -l list available sections\n"); - fprintf(out, " -p privacy spec, LOCAL, EXPLICIT or AUTOMATIC\n"); + fprintf(out, " -s PKG/CLS send broadcast to the broadcast receiver.\n"); fprintf(out, "\n"); fprintf(out, " SECTION the field numbers of the incident report fields to include\n"); fprintf(out, "\n"); @@ -187,12 +219,13 @@ main(int argc, char** argv) { Status status; IncidentReportArgs args; - enum { DEST_DROPBOX, DEST_STDOUT } destination = DEST_STDOUT; - int dest = -1; // default + enum { DEST_UNSET, DEST_DROPBOX, DEST_STDOUT, DEST_BROADCAST } destination = DEST_UNSET; + int privacyPolicy = PRIVACY_POLICY_AUTOMATIC; + string receiverArg; // Parse the args int opt; - while ((opt = getopt(argc, argv, "bhdlp:")) != -1) { + while ((opt = getopt(argc, argv, "bhdlp:s:")) != -1) { switch (opt) { case 'h': usage(stdout); @@ -201,13 +234,29 @@ main(int argc, char** argv) section_list(stdout); return 0; case 'b': + if (!(destination == DEST_UNSET || destination == DEST_STDOUT)) { + usage(stderr); + return 1; + } destination = DEST_STDOUT; break; case 'd': + if (!(destination == DEST_UNSET || destination == DEST_DROPBOX)) { + usage(stderr); + return 1; + } destination = DEST_DROPBOX; break; case 'p': - dest = get_dest(optarg); + privacyPolicy = get_privacy_policy(optarg); + break; + case 's': + if (destination != DEST_UNSET) { + usage(stderr); + return 1; + } + destination = DEST_BROADCAST; + receiverArg = optarg; break; default: usage(stderr); @@ -215,6 +264,17 @@ main(int argc, char** argv) } } + string pkg; + string cls; + if (parse_receiver_arg(receiverArg, &pkg, &cls)) { + args.setReceiverPkg(pkg); + args.setReceiverCls(cls); + } else { + fprintf(stderr, "badly formatted -s package/class option: %s\n\n", receiverArg.c_str()); + usage(stderr); + return 1; + } + if (optind == argc) { args.setAll(true); } else { @@ -236,7 +296,7 @@ main(int argc, char** argv) } } } - args.setDest(dest); + args.setPrivacyPolicy(privacyPolicy); // Start the thread pool. sp ps(ProcessState::self()); @@ -272,12 +332,17 @@ main(int argc, char** argv) //IPCThreadState::self()->joinThreadPool(); while (true) { - int amt = splice(fds[0], NULL, STDOUT_FILENO, NULL, 4096, 0); - fprintf(stderr, "spliced %d bytes\n", amt); + uint8_t buf[4096]; + ssize_t amt = TEMP_FAILURE_RETRY(read(fds[0], buf, sizeof(buf))); if (amt < 0) { - return errno; + break; } else if (amt == 0) { - return 0; + break; + } + + ssize_t wamt = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buf, amt)); + if (wamt != amt) { + return errno; } } } else { diff --git a/cmds/incidentd/Android.bp b/cmds/incidentd/Android.bp index 3dc10939fed75c1801c1ea6a2ab4f3c076c4cf11..8f9a5f848668ab8ac970704f2c972132b9ba27ff 100644 --- a/cmds/incidentd/Android.bp +++ b/cmds/incidentd/Android.bp @@ -21,6 +21,7 @@ cc_binary { srcs: [ "src/**/*.cpp", + "src/**/*.proto", ":incidentd_section_list", ], @@ -43,6 +44,10 @@ cc_binary { local_include_dirs: ["src"], generated_headers: ["gen-platform-proto-constants"], + proto: { + type: "lite", + }, + shared_libs: [ "libbase", "libbinder", @@ -56,6 +61,18 @@ cc_binary { "libprotobuf-cpp-lite", ], + static_libs: [ + "libincidentcompanion", + "libplatformprotos", + ], + + product_variables: { + debuggable: { + cflags: ["-DALLOW_RESTRICTED_SECTIONS=1"], + }, + }, + + init_rc: ["incidentd.rc"], } @@ -72,6 +89,7 @@ cc_test { "-Wall", "-Wno-unused-variable", "-Wunused-parameter", + "-g", // Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed. "-Wno-error=implicit-fallthrough", @@ -82,21 +100,26 @@ cc_test { srcs: [ "tests/**/*.cpp", - "src/PrivacyBuffer.cpp", + "tests/**/*.proto", "src/FdBuffer.cpp", "src/Privacy.cpp", + "src/PrivacyFilter.cpp", "src/Reporter.cpp", "src/Section.cpp", "src/Throttler.cpp", + "src/WorkDirectory.cpp", "src/incidentd_util.cpp", + "src/proto_util.cpp", "src/report_directory.cpp", + "src/**/*.proto", ], data: ["testdata/**/*"], static_libs: [ "libgmock", - "libplatformprotos", + "libincidentcompanion", + "libplatformprotos-test", ], shared_libs: [ "libbase", @@ -105,11 +128,19 @@ cc_test { "libdumputils", "libincident", "liblog", - "libprotobuf-cpp-lite", + "libprotobuf-cpp-full", "libprotoutil", "libservices", "libutils", ], + + target: { + android: { + proto: { + type: "full", + }, + }, + }, } genrule { diff --git a/cmds/incidentd/src/Broadcaster.cpp b/cmds/incidentd/src/Broadcaster.cpp new file mode 100644 index 0000000000000000000000000000000000000000..39e5393e1f81c869a84f152daa36018654b4c8d8 --- /dev/null +++ b/cmds/incidentd/src/Broadcaster.cpp @@ -0,0 +1,440 @@ +/* + * 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. + */ + +#include "Log.h" + +#include "Broadcaster.h" + +#include "IncidentService.h" + +#include +#include + +namespace android { +namespace os { +namespace incidentd { + +using android::os::IIncidentCompanion; +using binder::Status; + +// ============================================================ +Broadcaster::ConsentListener::ConsentListener(const sp& broadcaster, + const ReportId& reportId) + :mBroadcaster(broadcaster), + mId(reportId) { +} + +Broadcaster::ConsentListener::~ConsentListener() { +} + +Status Broadcaster::ConsentListener::onReportApproved() { + mBroadcaster->report_approved(mId); + return Status::ok(); +} + +Status Broadcaster::ConsentListener::onReportDenied() { + mBroadcaster->report_denied(mId); + return Status::ok(); +} + +// ============================================================ +Broadcaster::ReportId::ReportId() + :id(), + pkg(), + cls() { +} + +Broadcaster::ReportId::ReportId(const ReportId& that) + :id(that.id), + pkg(that.pkg), + cls(that.cls) { +} + +Broadcaster::ReportId::ReportId(const string& i, const string& p, const string& c) + :id(i), + pkg(p), + cls(c) { +} + +Broadcaster::ReportId::~ReportId() { +} + +bool Broadcaster::ReportId::operator<(const ReportId& that) const { + if (id < that.id) { + return true; + } + if (id > that.id) { + return false; + } + if (pkg < that.pkg) { + return true; + } + if (pkg > that.pkg) { + return false; + } + if (cls < that.cls) { + return true; + } + return false; +} + +// ============================================================ +Broadcaster::ReportStatus::ReportStatus() + :approval_sent(false), + ready_sent(false), + listener(nullptr) { +} + +Broadcaster::ReportStatus::ReportStatus(const ReportStatus& that) + :approval_sent(that.approval_sent), + ready_sent(that.ready_sent), + listener(that.listener) { +} + +Broadcaster::ReportStatus::~ReportStatus() { +} + +// ============================================================ +Broadcaster::Broadcaster(const sp& workDirectory) + :mReportHandler(), + mWorkDirectory(workDirectory) { +} + +void Broadcaster::setHandler(const sp& handler) { + mReportHandler = handler; +} + +void Broadcaster::reset() { + unique_lock lock(mLock); + mLastSent = 0; + mHistory.clear(); + // Could cancel the listeners, but this happens when + // the system process crashes, so don't bother. +} + +void Broadcaster::clearBroadcasts(const string& pkg, const string& cls, const string& id) { + unique_lock lock(mLock); + + map::const_iterator found = mHistory.find(ReportId(id, pkg, cls)); + if (found != mHistory.end()) { + if (found->second.listener != nullptr) { + sp ics = get_incident_companion(); + if (ics != nullptr) { + ics->cancelAuthorization(found->second.listener); + } + } + mHistory.erase(found); + } +} + +void Broadcaster::clearPackageBroadcasts(const string& pkg) { + unique_lock lock(mLock); + + map::iterator it = mHistory.begin(); + while (it != mHistory.end()) { + if (it->first.pkg == pkg) { + if (it->second.listener != nullptr) { + sp ics = get_incident_companion(); + if (ics != nullptr) { + ics->cancelAuthorization(it->second.listener); + } + } + it = mHistory.erase(it); + } else { + it++; + } + } +} + +Broadcaster::broadcast_status_t Broadcaster::sendBroadcasts() { + int err; + int64_t lastSent = get_last_sent(); + + vector> files; + mWorkDirectory->getReports(&files, 0); //lastSent); + + // Don't send multiple broadcasts to the same receiver. + set reportReadyBroadcasts; + + for (const sp& file: files) { + err = file->loadEnvelope(); + if (err != NO_ERROR) { + ALOGW("Error (%s) loading envelope from %s", strerror(-err), + file->getEnvelopeFileName().c_str()); + continue; + } + + const ReportFileProto& envelope = file->getEnvelope(); + + if (!envelope.completed()) { + ALOGI("Incident report not completed skipping it: %s", + file->getEnvelopeFileName().c_str()); + continue; + } + + // When one of the broadcast functions in this loop fails, it's almost + // certainly because the system process is crashing or has crashed. Rather + // than continuing to pound on the system process and potentially make things + // worse, we bail right away, return BROADCASTS_BACKOFF, and we will try + // again later. In the meantime, if the system process did crash, it might + // clear out mHistory, which means we'll be back here again to send the + // backlog. + size_t reportCount = envelope.report_size(); + bool hasApprovalPending = false; + for (int reportIndex = 0; reportIndex < reportCount; reportIndex++) { + + const ReportFileProto_Report& report = envelope.report(reportIndex); + status_t err; + if (report.privacy_policy() == PRIVACY_POLICY_AUTOMATIC || report.share_approved()) { + // It's privacy policy is AUTO, or it's been approved, + // so send the actual broadcast. + if (!was_ready_sent(file->getId(), report.pkg(), report.cls())) { + if (report.pkg() == DROPBOX_SENTINEL.getPackageName() + && report.cls() == DROPBOX_SENTINEL.getClassName()) { + IncidentReportArgs args; + get_args_from_report(&args, report); + err = send_to_dropbox(file, args); + if (err != NO_ERROR) { + return BROADCASTS_BACKOFF; + } + } else { + reportReadyBroadcasts.insert(ReportId(file->getId(), report.pkg(), + report.cls())); + } + } + } else { + // It's not approved yet, so send the approval. + if (!was_approval_sent(file->getId(), report.pkg(), report.cls())) { + err = send_approval_broadcasts(file->getId(), report.pkg(), report.cls()); + if (err != NO_ERROR) { + return BROADCASTS_BACKOFF; + } + hasApprovalPending = true; + } + } + } + + lastSent = file->getTimestampNs(); + if (!hasApprovalPending) { + set_last_sent(lastSent); + } + } + + for (const ReportId& report: reportReadyBroadcasts) { + err = send_report_ready_broadcasts(report.id, report.pkg, report.cls); + if (err != NO_ERROR) { + return BROADCASTS_BACKOFF; + } + } + + return mWorkDirectory->hasMore(lastSent) ? BROADCASTS_REPEAT : BROADCASTS_FINISHED; +} + +void Broadcaster::set_last_sent(int64_t timestamp) { + unique_lock lock(mLock); + mLastSent = timestamp; +} + +int64_t Broadcaster::get_last_sent() { + unique_lock lock(mLock); + return mLastSent; +} + +/* +void Broadcaster::printReportStatuses() const { + ALOGD("mHistory {"); + for (map::const_iterator it = mHistory.begin(); + it != mHistory.end(); it++) { + ALOGD(" [%s %s] --> [%d %d]", it->first.id.c_str(), it->first.pkg.c_str(), + it->second.approval_sent, it->second.ready_sent); + } + ALOGD("}"); +} +*/ + +bool Broadcaster::was_approval_sent(const string& id, const string& pkg, const string& cls) { + unique_lock lock(mLock); + map::const_iterator found = mHistory.find(ReportId(id, pkg, cls)); + if (found != mHistory.end()) { + return found->second.approval_sent; + } + return false; +} + +void Broadcaster::set_approval_sent(const string& id, const string& pkg, const string& cls, + const sp& listener) { + unique_lock lock(mLock); + ReportStatus& reportStatus = mHistory[ReportId(id, pkg, cls)]; + reportStatus.approval_sent = true; + reportStatus.listener = listener; +} + +bool Broadcaster::was_ready_sent(const string& id, const string& pkg, const string& cls) { + unique_lock lock(mLock); + map::const_iterator found = mHistory.find(ReportId(id, pkg, cls)); + if (found != mHistory.end()) { + return found->second.ready_sent; + } + return false; +} + +void Broadcaster::set_ready_sent(const string& id, const string& pkg, const string& cls) { + unique_lock lock(mLock); + mHistory[ReportId(id, pkg, cls)].ready_sent = true; +} + +status_t Broadcaster::send_approval_broadcasts(const string& id, const string& pkg, + const string& cls) { + sp ics = get_incident_companion(); + if (ics == nullptr) { + return NAME_NOT_FOUND; + } + + sp listener = new ConsentListener(this, ReportId(id, pkg, cls)); + + ALOGI("send_approval_broadcasts for %s %s/%s", id.c_str(), pkg.c_str(), cls.c_str()); + + Status status = ics->authorizeReport(0, String16(pkg.c_str()), + String16(cls.c_str()), String16(id.c_str()), 0, listener); + + if (!status.isOk()) { + // authorizeReport is oneway, so any error is a transaction error. + return status.transactionError(); + } + + set_approval_sent(id, pkg, cls, listener); + + return NO_ERROR; +} + +void Broadcaster::report_approved(const ReportId& reportId) { + status_t err; + + // Kick off broadcaster to do send the ready broadcasts. + ALOGI("The user approved the report, so kicking off another broadcast pass. %s %s/%s", + reportId.id.c_str(), reportId.pkg.c_str(), reportId.cls.c_str()); + sp file = mWorkDirectory->getReport(reportId.pkg, reportId.cls, reportId.id, + nullptr); + if (file != nullptr) { + err = file->loadEnvelope(); + if (err != NO_ERROR) { + return; + } + + err = file->markApproved(reportId.pkg, reportId.cls); + if (err != NO_ERROR) { + ALOGI("Couldn't find report that was just approved: %s %s/%s", + reportId.id.c_str(), reportId.pkg.c_str(), reportId.cls.c_str()); + return; + } + + file->saveEnvelope(); + if (err != NO_ERROR) { + return; + } + } + mReportHandler->scheduleSendBacklog(); +} + +void Broadcaster::report_denied(const ReportId& reportId) { + // The user didn't approve the report, so remove it from the WorkDirectory. + ALOGI("The user denied the report, so deleting it. %s %s/%s", + reportId.id.c_str(), reportId.pkg.c_str(), reportId.cls.c_str()); + sp file = mWorkDirectory->getReport(reportId.pkg, reportId.cls, reportId.id, + nullptr); + if (file != nullptr) { + mWorkDirectory->commit(file, reportId.pkg, reportId.cls); + } +} + +status_t Broadcaster::send_report_ready_broadcasts(const string& id, const string& pkg, + const string& cls) { + sp ics = get_incident_companion(); + if (ics == nullptr) { + return NAME_NOT_FOUND; + } + + ALOGI("send_report_ready_broadcasts for %s %s/%s", id.c_str(), pkg.c_str(), cls.c_str()); + + Status status = ics->sendReportReadyBroadcast(String16(pkg.c_str()), String16(cls.c_str())); + + if (!status.isOk()) { + // sendReportReadyBroadcast is oneway, so any error is a transaction error. + return status.transactionError(); + } + + set_ready_sent(id, pkg, cls); + + return NO_ERROR; +} + +status_t Broadcaster::send_to_dropbox(const sp& file, + const IncidentReportArgs& args) { + status_t err; + + sp dropbox = new DropBoxManager(); + if (dropbox == nullptr) { + ALOGW("Can't reach dropbox now, so we won't be able to write the incident report to there"); + return NO_ERROR; + } + + // Start a thread to write the data to dropbox. + int readFd = -1; + err = file->startFilteringData(&readFd, args); + if (err != NO_ERROR) { + return err; + } + + // Takes ownership of readFd. + Status status = dropbox->addFile(String16("incident"), readFd, 0); + if (!status.isOk()) { + // TODO: This may or may not leak the readFd, depending on where it failed. + // Not sure how to fix this given the dropbox API. + ALOGW("Error sending incident report to dropbox."); + return -errno; + } + + // On successful write, tell the working directory that this file is done. + mWorkDirectory->commit(file, DROPBOX_SENTINEL.getPackageName(), + DROPBOX_SENTINEL.getClassName()); + + // Don't need to call set_ready_sent, because we just removed it from the ReportFile, + // so we'll never hear about it again. + + return NO_ERROR; +} + +sp Broadcaster::get_incident_companion() { + sp binder = defaultServiceManager()->getService(String16("incidentcompanion")); + if (binder == nullptr) { + ALOGI("Can not find IIncidentCompanion service to send broadcast. Will try again later."); + return nullptr; + } + + sp ics = interface_cast(binder); + if (ics == nullptr) { + ALOGI("The incidentcompanion service is not an IIncidentCompanion. Will try again later."); + return nullptr; + } + + return ics; +} + +} // namespace incidentd +} // namespace os +} // namespace android + + diff --git a/cmds/incidentd/src/Broadcaster.h b/cmds/incidentd/src/Broadcaster.h new file mode 100644 index 0000000000000000000000000000000000000000..933029709fb6b0caf86c8f488afe9865c6c3a895 --- /dev/null +++ b/cmds/incidentd/src/Broadcaster.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "WorkDirectory.h" + +#include +#include +#include + +namespace android { +namespace os { +namespace incidentd { + +using android::binder::Status; +using android::os::BnIncidentAuthListener; +using android::os::IIncidentCompanion; + +class ReportHandler; + +class Broadcaster : public virtual RefBase { +public: + enum broadcast_status_t { + BROADCASTS_FINISHED = 0, + BROADCASTS_REPEAT = 1, + BROADCASTS_BACKOFF = 2 + }; + + Broadcaster(const sp& workDirectory); + + void setHandler(const sp& handler); + + /** + * Reset the beginning timestamp for broadcasts. Call this when + * the system_server restarts. + */ + void reset(); + + /** + * Remove the history record for the broadcasts, including pending authorizations + * if necessary. + */ + void clearBroadcasts(const string& pkg, const string& cls, const string& id); + void clearPackageBroadcasts(const string& pkg); + + /** + * Send whichever broadcasts have been pending. + */ + broadcast_status_t sendBroadcasts(); + +private: + struct ReportId { + ReportId(); + ReportId(const ReportId& that); + ReportId(const string& i, const string& p, const string& c); + ~ReportId(); + + bool operator<(const ReportId& that) const; + + string id; + string pkg; + string cls; + }; + + class ConsentListener : public BnIncidentAuthListener { + public: + ConsentListener(const sp& broadcaster, const ReportId& reportId); + virtual ~ConsentListener(); + virtual Status onReportApproved(); + virtual Status onReportDenied(); + private: + sp mBroadcaster; + ReportId mId; + }; + + struct ReportStatus { + ReportStatus(); + ReportStatus(const ReportStatus& that); + ~ReportStatus(); + + bool approval_sent; + bool ready_sent; + sp listener; + }; + + sp mReportHandler; + sp mWorkDirectory; + + // protected by mLock + mutex mLock; + map mHistory; // what we sent so we don't send it again + int64_t mLastSent; + + void set_last_sent(int64_t timestamp); + int64_t get_last_sent(); + void print_report_statuses() const; + status_t send_approval_broadcasts(const string& id, const string& pkg, const string& cls); + void report_approved(const ReportId& reportId); + void report_denied(const ReportId& reportId); + status_t send_report_ready_broadcasts(const string& id, const string& pkg, const string& cls); + status_t send_to_dropbox(const sp& file, const IncidentReportArgs& args); + bool was_approval_sent(const string& id, const string& pkg, const string& cls); + void set_approval_sent(const string& id, const string& pkg, const string& cls, + const sp& listener); + bool was_ready_sent(const string& id, const string& pkg, const string& cls); + void set_ready_sent(const string& id, const string& pkg, const string& cls); + sp get_incident_companion(); +}; + + +} // namespace incidentd +} // namespace os +} // namespace android + diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp index 04819ec75a09db146917b078ba8c9d5f3597d586..b46c9e357fc4d408bfdce63e893447c3d390994c 100644 --- a/cmds/incidentd/src/FdBuffer.cpp +++ b/cmds/incidentd/src/FdBuffer.cpp @@ -31,12 +31,18 @@ namespace os { namespace incidentd { const ssize_t BUFFER_SIZE = 16 * 1024; // 16 KB -const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max +const ssize_t MAX_BUFFER_COUNT = 1536; // 24 MB max FdBuffer::FdBuffer() - : mBuffer(BUFFER_SIZE), mStartTime(-1), mFinishTime(-1), mTimedOut(false), mTruncated(false) {} + :mBuffer(new EncodedBuffer(BUFFER_SIZE)), + mStartTime(-1), + mFinishTime(-1), + mTimedOut(false), + mTruncated(false) { +} -FdBuffer::~FdBuffer() {} +FdBuffer::~FdBuffer() { +} status_t FdBuffer::read(int fd, int64_t timeout) { struct pollfd pfds = {.fd = fd, .events = POLLIN}; @@ -45,12 +51,12 @@ status_t FdBuffer::read(int fd, int64_t timeout) { fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); while (true) { - if (mBuffer.size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) { + if (mBuffer->size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) { mTruncated = true; VLOG("Truncating data"); break; } - if (mBuffer.writeBuffer() == NULL) { + if (mBuffer->writeBuffer() == NULL) { VLOG("No memory"); return NO_MEMORY; } @@ -76,7 +82,7 @@ status_t FdBuffer::read(int fd, int64_t timeout) { return errno != 0 ? -errno : UNKNOWN_ERROR; } else { ssize_t amt = TEMP_FAILURE_RETRY( - ::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite())); + ::read(fd, mBuffer->writeBuffer(), mBuffer->currentToWrite())); if (amt < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { continue; @@ -88,7 +94,7 @@ status_t FdBuffer::read(int fd, int64_t timeout) { VLOG("Reached EOF of fd=%d", fd); break; } - mBuffer.wp()->move(amt); + mBuffer->wp()->move(amt); } } } @@ -100,28 +106,28 @@ status_t FdBuffer::readFully(int fd) { mStartTime = uptimeMillis(); while (true) { - if (mBuffer.size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) { + if (mBuffer->size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) { // Don't let it get too big. mTruncated = true; VLOG("Truncating data"); break; } - if (mBuffer.writeBuffer() == NULL) { + if (mBuffer->writeBuffer() == NULL) { VLOG("No memory"); return NO_MEMORY; } ssize_t amt = - TEMP_FAILURE_RETRY(::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite())); + TEMP_FAILURE_RETRY(::read(fd, mBuffer->writeBuffer(), mBuffer->currentToWrite())); if (amt < 0) { VLOG("Fail to read %d: %s", fd, strerror(errno)); return -errno; } else if (amt == 0) { - VLOG("Done reading %zu bytes", mBuffer.size()); + VLOG("Done reading %zu bytes", mBuffer->size()); // We're done. break; } - mBuffer.wp()->move(amt); + mBuffer->wp()->move(amt); } mFinishTime = uptimeMillis(); @@ -150,12 +156,12 @@ status_t FdBuffer::readProcessedDataInStream(int fd, unique_fd toFd, unique_fd f // This is the buffer used to store processed data while (true) { - if (mBuffer.size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) { + if (mBuffer->size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) { VLOG("Truncating data"); mTruncated = true; break; } - if (mBuffer.writeBuffer() == NULL) { + if (mBuffer->writeBuffer() == NULL) { VLOG("No memory"); return NO_MEMORY; } @@ -248,7 +254,7 @@ status_t FdBuffer::readProcessedDataInStream(int fd, unique_fd toFd, unique_fd f // read from parsing process ssize_t amt = TEMP_FAILURE_RETRY( - ::read(fromFd.get(), mBuffer.writeBuffer(), mBuffer.currentToWrite())); + ::read(fromFd.get(), mBuffer->writeBuffer(), mBuffer->currentToWrite())); if (amt < 0) { if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { VLOG("Fail to read fromFd %d: %s", fromFd.get(), strerror(errno)); @@ -258,7 +264,7 @@ status_t FdBuffer::readProcessedDataInStream(int fd, unique_fd toFd, unique_fd f VLOG("Reached EOF of fromFd %d", fromFd.get()); break; } else { - mBuffer.wp()->move(amt); + mBuffer->wp()->move(amt); } } @@ -266,9 +272,25 @@ status_t FdBuffer::readProcessedDataInStream(int fd, unique_fd toFd, unique_fd f return NO_ERROR; } -size_t FdBuffer::size() const { return mBuffer.size(); } +status_t FdBuffer::write(uint8_t const* buf, size_t size) { + return mBuffer->writeRaw(buf, size); +} + +status_t FdBuffer::write(const sp& reader) { + return mBuffer->writeRaw(reader); +} + +status_t FdBuffer::write(const sp& reader, size_t size) { + return mBuffer->writeRaw(reader, size); +} -EncodedBuffer::iterator FdBuffer::data() const { return mBuffer.begin(); } +size_t FdBuffer::size() const { + return mBuffer->size(); +} + +sp FdBuffer::data() const { + return mBuffer; +} } // namespace incidentd } // namespace os diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h index 20deefd02558d3dd1c714485df95ff95dd6ca0a6..a3493604f425bb5d8b279b004b5f7b44e304f8d7 100644 --- a/cmds/incidentd/src/FdBuffer.h +++ b/cmds/incidentd/src/FdBuffer.h @@ -63,6 +63,21 @@ public: status_t readProcessedDataInStream(int fd, unique_fd toFd, unique_fd fromFd, int64_t timeoutMs, const bool isSysfs = false); + /** + * Write by hand into the buffer. + */ + status_t write(uint8_t const* buf, size_t size); + + /** + * Write all the data from a ProtoReader into our internal buffer. + */ + status_t write(const sp& data); + + /** + * Write size bytes of data from a ProtoReader into our internal buffer. + */ + status_t write(const sp& data, size_t size); + /** * Whether we timed out. */ @@ -89,17 +104,12 @@ public: int64_t durationMs() const { return mFinishTime - mStartTime; } /** - * Reader API for data stored in FdBuffer - */ - EncodedBuffer::iterator data() const; - - /** - * Return the internal buffer, don't call unless you are familiar with EncodedBuffer. + * Get the EncodedBuffer inside. */ - EncodedBuffer* getInternalBuffer() { return &mBuffer; } + sp data() const; private: - EncodedBuffer mBuffer; + sp mBuffer; int64_t mStartTime; int64_t mFinishTime; bool mTimedOut; diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp index f8fb4a676ba0ddd4dccb07f1d285899e564046e8..4ba31b45e81cd693d334d7ec6c2a45486d86742a 100644 --- a/cmds/incidentd/src/IncidentService.cpp +++ b/cmds/incidentd/src/IncidentService.cpp @@ -19,7 +19,7 @@ #include "IncidentService.h" #include "FdBuffer.h" -#include "PrivacyBuffer.h" +#include "PrivacyFilter.h" #include "Reporter.h" #include "incidentd_util.h" #include "section_list.h" @@ -35,9 +35,12 @@ #include -enum { WHAT_RUN_REPORT = 1, WHAT_SEND_BACKLOG_TO_DROPBOX = 2 }; +enum { + WHAT_TAKE_REPORT = 1, + WHAT_SEND_BROADCASTS = 2 +}; -#define DEFAULT_BACKLOG_DELAY_NS (1000000000LL) +#define DEFAULT_DELAY_NS (1000000000LL) #define DEFAULT_BYTES_SIZE_LIMIT (20 * 1024 * 1024) // 20MB #define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000) // 1 Day @@ -53,6 +56,7 @@ namespace android { namespace os { namespace incidentd { +String16 const APPROVE_INCIDENT_REPORTS("android.permission.APPROVE_INCIDENT_REPORTS"); String16 const DUMP_PERMISSION("android.permission.DUMP"); String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS"); @@ -60,7 +64,14 @@ static Status checkIncidentPermissions(const IncidentReportArgs& args) { uid_t callingUid = IPCThreadState::self()->getCallingUid(); pid_t callingPid = IPCThreadState::self()->getCallingPid(); if (callingUid == AID_ROOT || callingUid == AID_SHELL) { - // root doesn't have permission.DUMP if don't do this! + // Root and shell are ok. + return Status::ok(); + } + + if (checkCallingPermission(APPROVE_INCIDENT_REPORTS)) { + // Permission controller (this is a singleton permission that is always granted + // exactly for PermissionController) is allowed to access incident reports + // so it can show the user info about what they are approving. return Status::ok(); } @@ -81,8 +92,8 @@ static Status checkIncidentPermissions(const IncidentReportArgs& args) { } // checking calling request uid permission. - switch (args.dest()) { - case DEST_LOCAL: + switch (args.getPrivacyPolicy()) { + case PRIVACY_POLICY_LOCAL: if (callingUid != AID_SHELL && callingUid != AID_ROOT) { ALOGW("Calling pid %d and uid %d does not have permission to get local data.", callingPid, callingUid); @@ -91,7 +102,7 @@ static Status checkIncidentPermissions(const IncidentReportArgs& args) { "Calling process does not have permission to get local data."); } break; - case DEST_EXPLICIT: + case PRIVACY_POLICY_EXPLICIT: if (callingUid != AID_SHELL && callingUid != AID_ROOT && callingUid != AID_STATSD && callingUid != AID_SYSTEM) { ALOGW("Calling pid %d and uid %d does not have permission to get explicit data.", @@ -105,78 +116,79 @@ static Status checkIncidentPermissions(const IncidentReportArgs& args) { return Status::ok(); } -// ================================================================================ -ReportRequestQueue::ReportRequestQueue() {} - -ReportRequestQueue::~ReportRequestQueue() {} - -void ReportRequestQueue::addRequest(const sp& request) { - unique_lock lock(mLock); - mQueue.push_back(request); -} - -sp ReportRequestQueue::getNextRequest() { - unique_lock lock(mLock); - if (mQueue.empty()) { - return NULL; - } else { - sp front(mQueue.front()); - mQueue.pop_front(); - return front; - } +static string build_uri(const string& pkg, const string& cls, const string& id) { + return "build_uri not implemented " + pkg + "/" + cls + "/" + id; } // ================================================================================ -ReportHandler::ReportHandler(const sp& handlerLooper, const sp& queue, - const sp& throttler) - : mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS), - mHandlerLooper(handlerLooper), - mQueue(queue), - mThrottler(throttler) {} +ReportHandler::ReportHandler(const sp& workDirectory, + const sp& broadcaster, const sp& handlerLooper, + const sp& throttler) + :mLock(), + mWorkDirectory(workDirectory), + mBroadcaster(broadcaster), + mHandlerLooper(handlerLooper), + mBacklogDelay(DEFAULT_DELAY_NS), + mThrottler(throttler), + mBatch(new ReportBatch()) { +} -ReportHandler::~ReportHandler() {} +ReportHandler::~ReportHandler() { +} void ReportHandler::handleMessage(const Message& message) { switch (message.what) { - case WHAT_RUN_REPORT: - run_report(); + case WHAT_TAKE_REPORT: + take_report(); break; - case WHAT_SEND_BACKLOG_TO_DROPBOX: - send_backlog_to_dropbox(); + case WHAT_SEND_BROADCASTS: + send_broadcasts(); break; } } -void ReportHandler::scheduleRunReport(const sp& request) { - mQueue->addRequest(request); - mHandlerLooper->removeMessages(this, WHAT_RUN_REPORT); - mHandlerLooper->sendMessage(this, Message(WHAT_RUN_REPORT)); +void ReportHandler::schedulePersistedReport(const IncidentReportArgs& args) { + mBatch->addPersistedReport(args); + mHandlerLooper->removeMessages(this, WHAT_TAKE_REPORT); + mHandlerLooper->sendMessage(this, Message(WHAT_TAKE_REPORT)); +} + +void ReportHandler::scheduleStreamingReport(const IncidentReportArgs& args, + const sp& listener, int streamFd) { + mBatch->addStreamingReport(args, listener, streamFd); + mHandlerLooper->removeMessages(this, WHAT_TAKE_REPORT); + mHandlerLooper->sendMessage(this, Message(WHAT_TAKE_REPORT)); } -void ReportHandler::scheduleSendBacklogToDropbox() { +void ReportHandler::scheduleSendBacklog() { unique_lock lock(mLock); - mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS; - schedule_send_backlog_to_dropbox_locked(); + mBacklogDelay = DEFAULT_DELAY_NS; + schedule_send_broadcasts_locked(); } -void ReportHandler::schedule_send_backlog_to_dropbox_locked() { - mHandlerLooper->removeMessages(this, WHAT_SEND_BACKLOG_TO_DROPBOX); - mHandlerLooper->sendMessageDelayed(mBacklogDelay, this, Message(WHAT_SEND_BACKLOG_TO_DROPBOX)); +void ReportHandler::schedule_send_broadcasts_locked() { + mHandlerLooper->removeMessages(this, WHAT_SEND_BROADCASTS); + mHandlerLooper->sendMessageDelayed(mBacklogDelay, this, Message(WHAT_SEND_BROADCASTS)); } -void ReportHandler::run_report() { - sp reporter = new Reporter(); +void ReportHandler::take_report() { + // Cycle the batch + sp batch; + { + unique_lock lock(mLock); + batch = mBatch; + mBatch = new ReportBatch(); + } - // Merge all of the requests into one that has all of the - // requested fields. - while (true) { - sp request = mQueue->getNextRequest(); - if (request == NULL) { - break; - } - reporter->batch.add(request); + if (batch->empty()) { + // Nothing to do. + return; } + sp reporter = new Reporter(mWorkDirectory, batch); + + // TODO: Do we really want to clear the reports if we throttle? Should we only throttle + // requests going to dropbox? How do we reconcile throttling with testing? if (mThrottler->shouldThrottle()) { ALOGW("RunReport got throttled."); return; @@ -185,46 +197,76 @@ void ReportHandler::run_report() { // Take the report, which might take a while. More requests might queue // up while we're doing this, and we'll handle them in their next batch. // TODO: We should further rate-limit the reports to no more than N per time-period. + // TODO: Move this inside reporter. size_t reportByteSize = 0; - Reporter::run_report_status_t reportStatus = reporter->runReport(&reportByteSize); + reporter->runReport(&reportByteSize); + mThrottler->addReportSize(reportByteSize); - if (reportStatus == Reporter::REPORT_NEEDS_DROPBOX) { + + // Kick off the next steps, one of which is to send any new or otherwise remaining + // approvals, and one of which is to send any new or remaining broadcasts. + { unique_lock lock(mLock); - schedule_send_backlog_to_dropbox_locked(); + schedule_send_broadcasts_locked(); } } -void ReportHandler::send_backlog_to_dropbox() { - if (Reporter::upload_backlog() == Reporter::REPORT_NEEDS_DROPBOX) { +void ReportHandler::send_broadcasts() { + Broadcaster::broadcast_status_t result = mBroadcaster->sendBroadcasts(); + if (result == Broadcaster::BROADCASTS_FINISHED) { + // We're done. + unique_lock lock(mLock); + mBacklogDelay = DEFAULT_DELAY_NS; + } else if (result == Broadcaster::BROADCASTS_REPEAT) { + // It worked, but there are more. + unique_lock lock(mLock); + mBacklogDelay = DEFAULT_DELAY_NS; + schedule_send_broadcasts_locked(); + } else if (result == Broadcaster::BROADCASTS_BACKOFF) { // There was a failure. Exponential backoff. unique_lock lock(mLock); mBacklogDelay *= 2; ALOGI("Error sending to dropbox. Trying again in %lld minutes", (mBacklogDelay / (1000000000LL * 60))); - schedule_send_backlog_to_dropbox_locked(); - } else { - mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS; + schedule_send_broadcasts_locked(); } } // ================================================================================ -IncidentService::IncidentService(const sp& handlerLooper) - : mQueue(new ReportRequestQueue()), - mThrottler(new Throttler(DEFAULT_BYTES_SIZE_LIMIT, DEFAULT_REFACTORY_PERIOD_MS)) { - mHandler = new ReportHandler(handlerLooper, mQueue, mThrottler); +IncidentService::IncidentService(const sp& handlerLooper) { + mThrottler = new Throttler(DEFAULT_BYTES_SIZE_LIMIT, DEFAULT_REFACTORY_PERIOD_MS); + mWorkDirectory = new WorkDirectory(); + mBroadcaster = new Broadcaster(mWorkDirectory); + mHandler = new ReportHandler(mWorkDirectory, mBroadcaster, handlerLooper, + mThrottler); + mBroadcaster->setHandler(mHandler); } IncidentService::~IncidentService() {} Status IncidentService::reportIncident(const IncidentReportArgs& args) { - ALOGI("reportIncident"); + // TODO: Validate that the privacy policy is one of the real ones. + // If it isn't, clamp it to the next more restrictive real one. + // TODO: This function should reject the LOCAL privacy policy. + // Those have to stream. + + // TODO: Check that the broadcast recevier has the proper permissions + // TODO: Maybe we should consider relaxing the permissions if it's going to + // dropbox, but definitely not if it's going to the broadcaster. Status status = checkIncidentPermissions(args); if (!status.isOk()) { return status; } - mHandler->scheduleRunReport(new ReportRequest(args, NULL, -1)); + // If they didn't specify a component, use dropbox. + IncidentReportArgs argsCopy(args); + if (argsCopy.receiverPkg().length() == 0 && argsCopy.receiverCls().length() == 0) { + argsCopy.setReceiverPkg(DROPBOX_SENTINEL.getPackageName()); + argsCopy.setReceiverCls(DROPBOX_SENTINEL.getClassName()); + } + + mHandler->schedulePersistedReport(argsCopy); return Status::ok(); } @@ -232,19 +274,29 @@ Status IncidentService::reportIncident(const IncidentReportArgs& args) { Status IncidentService::reportIncidentToStream(const IncidentReportArgs& args, const sp& listener, const unique_fd& stream) { - ALOGI("reportIncidentToStream"); + // TODO: Validate that the privacy policy is one of the real ones. + // If it isn't, clamp it to the next more restrictive real one. - Status status = checkIncidentPermissions(args); + // TODO: Only shell should be able to do a LOCAL privacy policy report. + + // Streaming reports can not also be broadcast. + IncidentReportArgs argsCopy(args); + argsCopy.setReceiverPkg(""); + argsCopy.setReceiverCls(""); + + Status status = checkIncidentPermissions(argsCopy); if (!status.isOk()) { return status; } + + // The ReportRequest takes ownership of the fd, so we need to dup it. int fd = dup(stream.get()); if (fd < 0) { return Status::fromStatusT(-errno); } - mHandler->scheduleRunReport(new ReportRequest(args, listener, fd)); + mHandler->scheduleStreamingReport(argsCopy, listener, fd); return Status::ok(); } @@ -256,7 +308,92 @@ Status IncidentService::systemRunning() { } // When system_server is up and running, schedule the dropbox task to run. - mHandler->scheduleSendBacklogToDropbox(); + mBroadcaster->reset(); + mHandler->scheduleSendBacklog(); + + return Status::ok(); +} + +Status IncidentService::getIncidentReportList(const String16& pkg16, const String16& cls16, + vector* result) { + status_t err; + const string pkg(String8(pkg16).string()); + const string cls(String8(cls16).string()); + + // List the reports + vector> all; + err = mWorkDirectory->getReports(&all, 0); + if (err != NO_ERROR) { + return Status::fromStatusT(err); + } + + // Find the ones that match pkg and cls. + for (sp& file: all) { + err = file->loadEnvelope(); + if (err != NO_ERROR) { + continue; + } + const ReportFileProto& envelope = file->getEnvelope(); + size_t reportCount = envelope.report_size(); + for (int reportIndex = 0; reportIndex < reportCount; reportIndex++) { + const ReportFileProto_Report& report = envelope.report(reportIndex); + if (pkg == report.pkg() && cls == report.cls()) { + result->push_back(String16(build_uri(pkg, cls, file->getId()).c_str())); + break; + } + } + } + + return Status::ok(); +} + +Status IncidentService::getIncidentReport(const String16& pkg16, const String16& cls16, + const String16& id16, IncidentManager::IncidentReport* result) { + status_t err; + + const string pkg(String8(pkg16).string()); + const string cls(String8(cls16).string()); + const string id(String8(id16).string()); + + IncidentReportArgs args; + sp file = mWorkDirectory->getReport(pkg, cls, id, &args); + if (file != nullptr) { + int fd; + err = file->startFilteringData(&fd, args); + if (err != 0) { + ALOGW("Error reading data file that we think should exist: %s", + file->getDataFileName().c_str()); + return Status::ok(); + } + + result->setTimestampNs(file->getTimestampNs()); + result->setPrivacyPolicy(file->getEnvelope().privacy_policy()); + result->takeFileDescriptor(fd); + } + + return Status::ok(); +} + +Status IncidentService::deleteIncidentReports(const String16& pkg16, const String16& cls16, + const String16& id16) { + const string pkg(String8(pkg16).string()); + const string cls(String8(cls16).string()); + const string id(String8(id16).string()); + + sp file = mWorkDirectory->getReport(pkg, cls, id, nullptr); + if (file != nullptr) { + mWorkDirectory->commit(file, pkg, cls); + } + mBroadcaster->clearBroadcasts(pkg, cls, id); + + return Status::ok(); +} + +Status IncidentService::deleteAllIncidentReports(const String16& pkg16) { + const string pkg(String8(pkg16).string()); + + mWorkDirectory->commitAll(pkg); + mBroadcaster->clearPackageBroadcasts(pkg); return Status::ok(); } @@ -354,7 +491,7 @@ status_t IncidentService::cmd_help(FILE* out) { static void printPrivacy(const Privacy* p, FILE* out, String8 indent) { if (p == NULL) return; - fprintf(out, "%sid:%d, type:%d, dest:%d\n", indent.string(), p->field_id, p->type, p->dest); + fprintf(out, "%sid:%d, type:%d, dest:%d\n", indent.string(), p->field_id, p->type, p->policy); if (p->children == NULL) return; for (int i = 0; p->children[i] != NULL; i++) { // NULL-terminated. printPrivacy(p->children[i], out, indent + " "); @@ -362,6 +499,8 @@ static void printPrivacy(const Privacy* p, FILE* out, String8 indent) { } status_t IncidentService::cmd_privacy(FILE* in, FILE* out, FILE* err, Vector& args) { + (void)in; + const int argCount = args.size(); if (argCount >= 3) { String8 opt = args[1]; @@ -376,6 +515,7 @@ status_t IncidentService::cmd_privacy(FILE* in, FILE* out, FILE* err, Vector 3 ? atoi(args[3]) : -1); error = pBuf.strip(spec); if (error != NO_ERROR) { - fprintf(err, "Error strip pii fields with spec %d\n", spec.dest); + fprintf(err, "Error strip pii fields with spec %d\n", spec.policy); return error; } return pBuf.flush(fileno(out)); + */ + return -1; } } else { return cmd_help(out); @@ -408,7 +550,7 @@ status_t IncidentService::dump(int fd, const Vector& args) { ALOGD("Dump incident proto"); IncidentReportArgs incidentArgs; - incidentArgs.setDest(DEST_EXPLICIT); + incidentArgs.setPrivacyPolicy(PRIVACY_POLICY_EXPLICIT); int skipped[] = SKIPPED_SECTIONS; for (const Section** section = SECTION_LIST; *section; section++) { const int id = (*section)->id; @@ -421,12 +563,16 @@ status_t IncidentService::dump(int fd, const Vector& args) { return PERMISSION_DENIED; } + // The ReportRequest takes ownership of the fd, so we need to dup it. int fd1 = dup(fd); if (fd1 < 0) { return -errno; } - mHandler->scheduleRunReport(new ReportRequest(incidentArgs, NULL, fd1)); + // TODO: Remove this. Someone even dumpstate, wanting to get an incident report + // should use the API. That will take making dumpstated call the API, which is a + // good thing. It also means it won't be subject to the timeout. + mHandler->scheduleStreamingReport(incidentArgs, NULL, fd1); return NO_ERROR; } diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h index c63a183b70b95b2523883beba941bb4a742345ee..6481321bbd69c204a67dcadb0389b24271a1ba84 100644 --- a/cmds/incidentd/src/IncidentService.h +++ b/cmds/incidentd/src/IncidentService.h @@ -20,13 +20,16 @@ #include "Reporter.h" +#include "Broadcaster.h" +#include "Throttler.h" +#include "WorkDirectory.h" + #include #include -#include +#include #include -#include "Throttler.h" namespace android { namespace os { @@ -37,61 +40,75 @@ using namespace android::base; using namespace android::binder; using namespace android::os; -// ================================================================================ -class ReportRequestQueue : public virtual RefBase { -public: - ReportRequestQueue(); - virtual ~ReportRequestQueue(); - - void addRequest(const sp& request); - sp getNextRequest(); - -private: - mutex mLock; - deque > mQueue; -}; - // ================================================================================ class ReportHandler : public MessageHandler { public: - ReportHandler(const sp& handlerLooper, const sp& queue, - const sp& throttler); + ReportHandler(const sp& workDirectory, + const sp& broadcaster, const sp& handlerLooper, + const sp& throttler); virtual ~ReportHandler(); virtual void handleMessage(const Message& message); /** - * Adds a ReportRequest to the queue. + * Schedule a report for the "main" report, where it will be delivered to + * the uploaders and/or dropbox. */ - void scheduleRunReport(const sp& request); + void schedulePersistedReport(const IncidentReportArgs& args); + + /** + * Adds a ReportRequest to the queue for one that has a listener an and fd + */ + void scheduleStreamingReport(const IncidentReportArgs& args, + const sp& listener, + int streamFd); /** * Resets mBacklogDelay to the default and schedules sending * the messages to dropbox. */ - void scheduleSendBacklogToDropbox(); + void scheduleSendBacklog(); private: mutex mLock; - nsecs_t mBacklogDelay; + + sp mWorkDirectory; + sp mBroadcaster; + sp mHandlerLooper; - sp mQueue; + nsecs_t mBacklogDelay; sp mThrottler; + sp mBatch; + /** * Runs all of the reports that have been queued. */ - void run_report(); + void take_report(); + + /** + * Schedules permission controller approve the reports. + */ + void schedule_send_approvals_locked(); /** - * Schedules a dropbox task mBacklogDelay nanoseconds from now. + * Sends the approvals to the PermissionController */ - void schedule_send_backlog_to_dropbox_locked(); + void send_approvals(); /** - * Sends the backlog to the dropbox service. + * Schedules the broadcasts that reports are complete mBacklogDelay nanoseconds from now. + * The delay is because typically when an incident report is taken, the system is not + * really in a happy state. So we wait a bit before sending the report to let things + * quiet down if they can. The urgency is in taking the report, not sharing the report. + * However, we don */ - void send_backlog_to_dropbox(); + void schedule_send_broadcasts_locked(); + + /** + * Sends the broadcasts to the dropbox service. + */ + void send_broadcasts(); }; // ================================================================================ @@ -108,6 +125,17 @@ public: virtual Status systemRunning(); + virtual Status getIncidentReportList(const String16& pkg, const String16& cls, + vector* result); + + virtual Status getIncidentReport(const String16& pkg, const String16& cls, + const String16& id, IncidentManager::IncidentReport* result); + + virtual Status deleteIncidentReports(const String16& pkg, const String16& cls, + const String16& id); + + virtual Status deleteAllIncidentReports(const String16& pkg); + // Implement commands for debugging purpose. virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override; @@ -115,7 +143,8 @@ public: virtual status_t dump(int fd, const Vector& args); private: - sp mQueue; + sp mWorkDirectory; + sp mBroadcaster; sp mHandler; sp mThrottler; diff --git a/cmds/incidentd/src/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp index 6e55f906087cc4a730bfb2bb2ab8467b19f1dee6..386303b038e7a3f9f4e0b40bdfb6aa69f1baebae 100644 --- a/cmds/incidentd/src/Privacy.cpp +++ b/cmds/incidentd/src/Privacy.cpp @@ -23,6 +23,8 @@ namespace android { namespace os { namespace incidentd { +using namespace android::os; + uint64_t encode_field_id(const Privacy* p) { return (uint64_t)p->type << 32 | p->field_id; } const Privacy* lookup(const Privacy* p, uint32_t fieldId) { @@ -35,39 +37,48 @@ const Privacy* lookup(const Privacy* p, uint32_t fieldId) { return NULL; } -static bool allowDest(const uint8_t dest, const uint8_t policy) { - switch (policy) { - case android::os::DEST_LOCAL: - return dest == android::os::DEST_LOCAL; - case android::os::DEST_EXPLICIT: - case DEST_UNSET: - return dest == android::os::DEST_LOCAL || dest == android::os::DEST_EXPLICIT || - dest == DEST_UNSET; - case android::os::DEST_AUTOMATIC: +static bool isAllowed(const uint8_t policy, const uint8_t check) { + switch (check) { + case PRIVACY_POLICY_LOCAL: + return policy == PRIVACY_POLICY_LOCAL; + case PRIVACY_POLICY_EXPLICIT: + case PRIVACY_POLICY_UNSET: + return policy == PRIVACY_POLICY_LOCAL + || policy == PRIVACY_POLICY_EXPLICIT + || policy == PRIVACY_POLICY_UNSET; + case PRIVACY_POLICY_AUTOMATIC: return true; default: return false; } } -bool PrivacySpec::operator<(const PrivacySpec& other) const { return dest < other.dest; } +PrivacySpec::PrivacySpec(uint8_t argPolicy) { + // TODO: Why on earth do we have two definitions of policy. Maybe + // it's not too late to clean this up. + switch (argPolicy) { + case android::os::PRIVACY_POLICY_AUTOMATIC: + case android::os::PRIVACY_POLICY_EXPLICIT: + case android::os::PRIVACY_POLICY_LOCAL: + mPolicy = argPolicy; + break; + default: + mPolicy = android::os::PRIVACY_POLICY_AUTOMATIC; + break; + } +} -bool PrivacySpec::CheckPremission(const Privacy* privacy, const uint8_t defaultDest) const { - uint8_t policy = privacy != NULL ? privacy->dest : defaultDest; - return allowDest(dest, policy); +bool PrivacySpec::operator<(const PrivacySpec& that) const { + return mPolicy < that.mPolicy; } -bool PrivacySpec::RequireAll() const { return dest == android::os::DEST_LOCAL; } +bool PrivacySpec::CheckPremission(const Privacy* privacy, const uint8_t defaultDest) const { + uint8_t check = privacy != NULL ? privacy->policy : defaultDest; + return isAllowed(mPolicy, check); +} -PrivacySpec PrivacySpec::new_spec(int dest) { - switch (dest) { - case android::os::DEST_AUTOMATIC: - case android::os::DEST_EXPLICIT: - case android::os::DEST_LOCAL: - return PrivacySpec(dest); - default: - return PrivacySpec(android::os::DEST_AUTOMATIC); - } +bool PrivacySpec::RequireAll() const { + return mPolicy == android::os::PRIVACY_POLICY_LOCAL; } } // namespace incidentd diff --git a/cmds/incidentd/src/Privacy.h b/cmds/incidentd/src/Privacy.h index a0159d9a8649fbca657f2d3d441123414087cc61..fc8caae7fc60a0f4801588af137a345a97e9f732 100644 --- a/cmds/incidentd/src/Privacy.h +++ b/cmds/incidentd/src/Privacy.h @@ -18,15 +18,15 @@ #ifndef PRIVACY_H #define PRIVACY_H +#include + #include namespace android { namespace os { namespace incidentd { -// This is the default value of DEST enum, sync with privacy.proto -const uint8_t DEST_UNSET = 255; // DEST_UNSET is not exposed to libincident -const uint8_t DEST_DEFAULT_VALUE = DEST_UNSET; +using namespace android::os; /* * In order to NOT auto-generate large chuck of code by proto compiler in incidentd, @@ -48,8 +48,8 @@ struct Privacy { // This array is NULL-terminated. Privacy** children; - // DESTINATION Enum in frameworks/base/libs/incident/proto/android/privacy.proto. - uint8_t dest; + // DESTINATION Enum in frameworks/base/core/proto/android/privacy.proto. + uint8_t policy; // A list of regexp rules for stripping string fields in proto. const char** patterns; }; @@ -63,27 +63,28 @@ const Privacy* lookup(const Privacy* p, uint32_t fieldId); /** * PrivacySpec defines the request has what level of privacy authorization. * For example, a device without user consent should only be able to upload AUTOMATIC fields. - * DEST_UNSET are treated as DEST_EXPLICIT. + * PRIVACY_POLICY_UNSET are treated as PRIVACY_POLICY_EXPLICIT. */ class PrivacySpec { public: - const uint8_t dest; + explicit PrivacySpec(uint8_t argPolicy); - PrivacySpec() : dest(DEST_DEFAULT_VALUE) {} bool operator<(const PrivacySpec& other) const; // check permission of a policy, if returns true, don't strip the data. bool CheckPremission(const Privacy* privacy, - const uint8_t defaultDest = DEST_DEFAULT_VALUE) const; + const uint8_t defaultPrivacyPolicy = PRIVACY_POLICY_UNSET) const; // if returns true, no data need to be stripped. bool RequireAll() const; - // Constructs spec using static methods below. - static PrivacySpec new_spec(int dest); + uint8_t getPolicy() const; private: - explicit PrivacySpec(uint8_t dest) : dest(dest) {} + // unimplemented constructors + explicit PrivacySpec(); + + uint8_t mPolicy; }; } // namespace incidentd diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp deleted file mode 100644 index 08f535db01facf67b9d65f30cc57a4becbe6a0da..0000000000000000000000000000000000000000 --- a/cmds/incidentd/src/PrivacyBuffer.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#define DEBUG false -#include "Log.h" - -#include "PrivacyBuffer.h" -#include "incidentd_util.h" - -#include -#include -#include - -namespace android { -namespace os { -namespace incidentd { - -/** - * Write the field to buf based on the wire type, iterator will point to next field. - * If skip is set to true, no data will be written to buf. Return number of bytes written. - */ -void PrivacyBuffer::writeFieldOrSkip(uint32_t fieldTag, bool skip) { - uint8_t wireType = read_wire_type(fieldTag); - size_t bytesToWrite = 0; - uint64_t varint = 0; - - switch (wireType) { - case WIRE_TYPE_VARINT: - varint = mData.readRawVarint(); - if (!skip) { - mProto.writeRawVarint(fieldTag); - mProto.writeRawVarint(varint); - } - return; - case WIRE_TYPE_FIXED64: - if (!skip) mProto.writeRawVarint(fieldTag); - bytesToWrite = 8; - break; - case WIRE_TYPE_LENGTH_DELIMITED: - bytesToWrite = mData.readRawVarint(); - if (!skip) mProto.writeLengthDelimitedHeader(read_field_id(fieldTag), bytesToWrite); - break; - case WIRE_TYPE_FIXED32: - if (!skip) mProto.writeRawVarint(fieldTag); - bytesToWrite = 4; - break; - } - if (skip) { - mData.rp()->move(bytesToWrite); - } else { - for (size_t i = 0; i < bytesToWrite; i++) { - mProto.writeRawByte(mData.next()); - } - } -} - -/** - * Strip next field based on its private policy and request spec, then stores data in buf. - * Return NO_ERROR if succeeds, otherwise BAD_VALUE is returned to indicate bad data in FdBuffer. - * - * The iterator must point to the head of a protobuf formatted field for successful operation. - * After exit with NO_ERROR, iterator points to the next protobuf field's head. - */ -status_t PrivacyBuffer::stripField(const Privacy* parentPolicy, const PrivacySpec& spec, - int depth /* use as a counter for this recusive method. */) { - if (!mData.hasNext() || parentPolicy == NULL) return BAD_VALUE; - uint32_t fieldTag = mData.readRawVarint(); - uint32_t fieldId = read_field_id(fieldTag); - const Privacy* policy = lookup(parentPolicy, fieldId); - - VLOG("[Depth %2d]Try to strip id %d, wiretype %d", depth, fieldId, read_wire_type(fieldTag)); - if (policy == NULL || policy->children == NULL) { - bool skip = !spec.CheckPremission(policy, parentPolicy->dest); - // iterator will point to head of next field - size_t currentAt = mData.rp()->pos(); - writeFieldOrSkip(fieldTag, skip); - VLOG("[Depth %2d]Field %d %ss %zu bytes", depth, fieldId, skip ? "skip" : "write", - get_varint_size(fieldTag) + mData.rp()->pos() - currentAt); - return NO_ERROR; - } - // current field is message type and its sub-fields have extra privacy policies - uint32_t msgSize = mData.readRawVarint(); - size_t start = mData.rp()->pos(); - uint64_t token = mProto.start(encode_field_id(policy)); - while (mData.rp()->pos() - start != msgSize) { - status_t err = stripField(policy, spec, depth + 1); - if (err != NO_ERROR) { - VLOG("Bad value when stripping id %d, wiretype %d, tag %#x, depth %d, size %d, " - "relative pos %zu, ", fieldId, read_wire_type(fieldTag), fieldTag, depth, - msgSize, mData.rp()->pos() - start); - return err; - } - } - mProto.end(token); - return NO_ERROR; -} - -// ================================================================================ -PrivacyBuffer::PrivacyBuffer(const Privacy* policy, EncodedBuffer::iterator data) - : mPolicy(policy), mData(data), mProto(), mSize(0) {} - -PrivacyBuffer::~PrivacyBuffer() {} - -status_t PrivacyBuffer::strip(const PrivacySpec& spec) { - VLOG("Strip with spec %d", spec.dest); - // optimization when no strip happens - if (mPolicy == NULL || mPolicy->children == NULL || spec.RequireAll()) { - if (spec.CheckPremission(mPolicy)) mSize = mData.size(); - return NO_ERROR; - } - while (mData.hasNext()) { - status_t err = stripField(mPolicy, spec, 0); - if (err != NO_ERROR) return err; // Error logged in stripField. - } - if (mData.bytesRead() != mData.size()) { - VLOG("Buffer corrupted: expect %zu bytes, read %zu bytes", - mData.size(), mData.bytesRead()); - return BAD_VALUE; - } - mSize = mProto.size(); - mData.rp()->rewind(); // rewind the read pointer back to beginning after the strip. - return NO_ERROR; -} - -void PrivacyBuffer::clear() { - mSize = 0; - mProto.clear(); -} - -size_t PrivacyBuffer::size() const { return mSize; } - -status_t PrivacyBuffer::flush(int fd) { - status_t err = NO_ERROR; - EncodedBuffer::iterator iter = size() == mData.size() ? mData : mProto.data(); - while (iter.readBuffer() != NULL) { - err = WriteFully(fd, iter.readBuffer(), iter.currentToRead()) ? NO_ERROR : -errno; - iter.rp()->move(iter.currentToRead()); - if (err != NO_ERROR) return err; - } - return NO_ERROR; -} - -} // namespace incidentd -} // namespace os -} // namespace android diff --git a/cmds/incidentd/src/PrivacyBuffer.h b/cmds/incidentd/src/PrivacyBuffer.h deleted file mode 100644 index eac3862b2cab0302648b92f2196bbb4621183810..0000000000000000000000000000000000000000 --- a/cmds/incidentd/src/PrivacyBuffer.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#ifndef PRIVACY_BUFFER_H -#define PRIVACY_BUFFER_H - -#include "Privacy.h" - -#include -#include -#include -#include - -namespace android { -namespace os { -namespace incidentd { - -using namespace android::util; - -/** - * PrivacyBuffer holds the original protobuf data and strips PII-sensitive fields - * based on the request and holds stripped data in its own buffer for output. - */ -class PrivacyBuffer { -public: - PrivacyBuffer(const Privacy* policy, EncodedBuffer::iterator data); - ~PrivacyBuffer(); - - /** - * Strip based on the request and hold data in its own buffer. Return NO_ERROR if strip - * succeeds. - */ - status_t strip(const PrivacySpec& spec); - - /** - * Clear encoded buffer so it can be reused by another request. - */ - void clear(); - - /** - * Return the size of the stripped data. - */ - size_t size() const; - - /** - * Flush buffer to the given fd. NO_ERROR is returned if the flush succeeds. - */ - status_t flush(int fd); - -private: - const Privacy* mPolicy; - EncodedBuffer::iterator mData; - - ProtoOutputStream mProto; - size_t mSize; - - status_t stripField(const Privacy* parentPolicy, const PrivacySpec& spec, int depth); - void writeFieldOrSkip(uint32_t fieldTag, bool skip); -}; - -} // namespace incidentd -} // namespace os -} // namespace android - -#endif // PRIVACY_BUFFER_H \ No newline at end of file diff --git a/cmds/incidentd/src/PrivacyFilter.cpp b/cmds/incidentd/src/PrivacyFilter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7126322575d5cedf7d54efc08145e8c7b405cc79 --- /dev/null +++ b/cmds/incidentd/src/PrivacyFilter.cpp @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define DEBUG false +#include "Log.h" + +#include "incidentd_util.h" +#include "PrivacyFilter.h" +#include "proto_util.h" + +#include +#include +#include +#include + +namespace android { +namespace os { +namespace incidentd { + +// ================================================================================ +/** + * Write the field to buf based on the wire type, iterator will point to next field. + * If skip is set to true, no data will be written to buf. Return number of bytes written. + */ +void write_field_or_skip(ProtoOutputStream* out, const sp& in, + uint32_t fieldTag, bool skip) { + uint8_t wireType = read_wire_type(fieldTag); + size_t bytesToWrite = 0; + uint64_t varint = 0; + + switch (wireType) { + case WIRE_TYPE_VARINT: + varint = in->readRawVarint(); + if (!skip) { + out->writeRawVarint(fieldTag); + out->writeRawVarint(varint); + } + return; + case WIRE_TYPE_FIXED64: + if (!skip) { + out->writeRawVarint(fieldTag); + } + bytesToWrite = 8; + break; + case WIRE_TYPE_LENGTH_DELIMITED: + bytesToWrite = in->readRawVarint(); + if (!skip) { + out->writeLengthDelimitedHeader(read_field_id(fieldTag), bytesToWrite); + } + break; + case WIRE_TYPE_FIXED32: + if (!skip) { + out->writeRawVarint(fieldTag); + } + bytesToWrite = 4; + break; + } + if (skip) { + in->move(bytesToWrite); + } else { + for (size_t i = 0; i < bytesToWrite; i++) { + out->writeRawByte(in->next()); + } + } +} + +/** + * Strip next field based on its private policy and request spec, then stores data in buf. + * Return NO_ERROR if succeeds, otherwise BAD_VALUE is returned to indicate bad data in + * FdBuffer. + * + * The iterator must point to the head of a protobuf formatted field for successful operation. + * After exit with NO_ERROR, iterator points to the next protobuf field's head. + * + * depth is the depth of recursion, for debugging. + */ +status_t strip_field(ProtoOutputStream* out, const sp& in, + const Privacy* parentPolicy, const PrivacySpec& spec, int depth) { + if (!in->hasNext() || parentPolicy == NULL) { + return BAD_VALUE; + } + uint32_t fieldTag = in->readRawVarint(); + uint32_t fieldId = read_field_id(fieldTag); + const Privacy* policy = lookup(parentPolicy, fieldId); + + if (policy == NULL || policy->children == NULL) { + bool skip = !spec.CheckPremission(policy, parentPolicy->policy); + // iterator will point to head of next field + size_t currentAt = in->bytesRead(); + write_field_or_skip(out, in, fieldTag, skip); + return NO_ERROR; + } + // current field is message type and its sub-fields have extra privacy policies + uint32_t msgSize = in->readRawVarint(); + size_t start = in->bytesRead(); + uint64_t token = out->start(encode_field_id(policy)); + while (in->bytesRead() - start != msgSize) { + status_t err = strip_field(out, in, policy, spec, depth + 1); + if (err != NO_ERROR) { + ALOGW("Bad value when stripping id %d, wiretype %d, tag %#x, depth %d, size %d, " + "relative pos %zu, ", fieldId, read_wire_type(fieldTag), fieldTag, depth, + msgSize, in->bytesRead() - start); + return err; + } + } + out->end(token); + return NO_ERROR; +} + +// ================================================================================ +class FieldStripper { +public: + FieldStripper(const Privacy* restrictions, const sp& data, + uint8_t bufferLevel); + + /** + * Take the data that we have, and filter it down so that no fields + * are more sensitive than the given privacy policy. + */ + status_t strip(uint8_t privacyPolicy); + + /** + * At the current filter level, how many bytes of data there is. + */ + ssize_t dataSize() const { return mSize; } + + /** + * Write the data from the current filter level to the file descriptor. + */ + status_t writeData(int fd); + +private: + /** + * The global set of field --> required privacy level mapping. + */ + const Privacy* mRestrictions; + + /** + * The current buffer. + */ + sp mData; + + /** + * The current size of the buffer inside mData. + */ + ssize_t mSize; + + /** + * The current privacy policy that the data is filtered to, as an optimization + * so we don't always re-filter data that has already been filtered. + */ + uint8_t mCurrentLevel; + +}; + +FieldStripper::FieldStripper(const Privacy* restrictions, const sp& data, + uint8_t bufferLevel) + :mRestrictions(restrictions), + mData(data), + mSize(data->size()), + mCurrentLevel(bufferLevel) { + if (mSize < 0) { + ALOGW("FieldStripper constructed with a ProtoReader that doesn't support size." + " Data will be missing."); + } +} + +status_t FieldStripper::strip(const uint8_t privacyPolicy) { + // If the current strip level is less (fewer fields retained) than what's already in the + // buffer, then we can skip it. + if (mCurrentLevel < privacyPolicy) { + PrivacySpec spec(privacyPolicy); + ProtoOutputStream proto; + + // Optimization when no strip happens. + if (mRestrictions == NULL || mRestrictions->children == NULL || spec.RequireAll()) { + if (spec.CheckPremission(mRestrictions)) { + mSize = mData->size(); + } + return NO_ERROR; + } + + while (mData->hasNext()) { + status_t err = strip_field(&proto, mData, mRestrictions, spec, 0); + if (err != NO_ERROR) { + return err; // Error logged in strip_field. + } + } + + if (mData->bytesRead() != mData->size()) { + ALOGW("Buffer corrupted: expect %zu bytes, read %zu bytes", mData->size(), + mData->bytesRead()); + return BAD_VALUE; + } + + mData = proto.data(); + mSize = proto.size(); + mCurrentLevel = privacyPolicy; + } + return NO_ERROR; +} + +status_t FieldStripper::writeData(int fd) { + status_t err = NO_ERROR; + sp reader = mData; + while (reader->readBuffer() != NULL) { + err = WriteFully(fd, reader->readBuffer(), reader->currentToRead()) ? NO_ERROR : -errno; + reader->move(reader->currentToRead()); + if (err != NO_ERROR) return err; + } + return NO_ERROR; +} + + +// ================================================================================ +FilterFd::FilterFd(uint8_t privacyPolicy, int fd) + :mPrivacyPolicy(privacyPolicy), + mFd(fd) { +} + +FilterFd::~FilterFd() { +} + +// ================================================================================ +PrivacyFilter::PrivacyFilter(int sectionId, const Privacy* restrictions) + :mSectionId(sectionId), + mRestrictions(restrictions), + mOutputs() { +} + +PrivacyFilter::~PrivacyFilter() { +} + +void PrivacyFilter::addFd(const sp& output) { + mOutputs.push_back(output); +} + +status_t PrivacyFilter::writeData(const FdBuffer& buffer, uint8_t bufferLevel, + size_t* maxSize) { + status_t err; + + if (maxSize != NULL) { + *maxSize = 0; + } + + // Order the writes by privacy filter, with increasing levels of filtration,k + // so we can do the filter once, and then write many times. + sort(mOutputs.begin(), mOutputs.end(), + [](const sp& a, const sp& b) -> bool { + return a->getPrivacyPolicy() < b->getPrivacyPolicy(); + }); + + uint8_t privacyPolicy = PRIVACY_POLICY_LOCAL; // a.k.a. no filtering + FieldStripper fieldStripper(mRestrictions, buffer.data()->read(), bufferLevel); + for (const sp& output: mOutputs) { + // Do another level of filtering if necessary + if (privacyPolicy != output->getPrivacyPolicy()) { + privacyPolicy = output->getPrivacyPolicy(); + err = fieldStripper.strip(privacyPolicy); + if (err != NO_ERROR) { + // We can't successfully strip this data. We will skip + // the rest of this section. + return err; + } + } + + // Write the resultant buffer to the fd, along with the header. + ssize_t dataSize = fieldStripper.dataSize(); + if (dataSize > 0) { + err = write_section_header(output->getFd(), mSectionId, dataSize); + if (err != NO_ERROR) { + output->onWriteError(err); + continue; + } + + err = fieldStripper.writeData(output->getFd()); + if (err != NO_ERROR) { + output->onWriteError(err); + continue; + } + } + + if (maxSize != NULL) { + if (dataSize > *maxSize) { + *maxSize = dataSize; + } + } + } + + return NO_ERROR; +} + +// ================================================================================ +class ReadbackFilterFd : public FilterFd { +public: + ReadbackFilterFd(uint8_t privacyPolicy, int fd); + + virtual void onWriteError(status_t err); + status_t getError() { return mError; } + +private: + status_t mError; +}; + +ReadbackFilterFd::ReadbackFilterFd(uint8_t privacyPolicy, int fd) + :FilterFd(privacyPolicy, fd), + mError(NO_ERROR) { +} + +void ReadbackFilterFd::onWriteError(status_t err) { + mError = err; +} + +// ================================================================================ +status_t filter_and_write_report(int to, int from, uint8_t bufferLevel, + const IncidentReportArgs& args) { + status_t err; + sp reader = new ProtoFileReader(from); + + while (reader->hasNext()) { + uint64_t fieldTag = reader->readRawVarint(); + uint32_t fieldId = read_field_id(fieldTag); + uint8_t wireType = read_wire_type(fieldTag); + if (wireType == WIRE_TYPE_LENGTH_DELIMITED && args.containsSection(fieldId)) { + // We need this field, but we need to strip it to the level provided in args. + PrivacyFilter filter(fieldId, get_privacy_of_section(fieldId)); + filter.addFd(new ReadbackFilterFd(args.getPrivacyPolicy(), to)); + + // Read this section from the reader into an FdBuffer + size_t sectionSize = reader->readRawVarint(); + FdBuffer sectionData; + err = sectionData.write(reader, sectionSize); + if (err != NO_ERROR) { + ALOGW("filter_and_write_report FdBuffer.write failed (this shouldn't happen): %s", + strerror(-err)); + return err; + } + + // Do the filter and write. + err = filter.writeData(sectionData, bufferLevel, nullptr); + if (err != NO_ERROR) { + ALOGW("filter_and_write_report filter.writeData had an error: %s", strerror(-err)); + return err; + } + } else { + // We don't need this field. Incident does not have any direct children + // other than sections. So just skip them. + write_field_or_skip(NULL, reader, fieldTag, true); + } + } + + err = reader->getError(); + if (err != NO_ERROR) { + ALOGW("filter_and_write_report reader had an error: %s", strerror(-err)); + return err; + } + + return NO_ERROR; +} + +} // namespace incidentd +} // namespace os +} // namespace android diff --git a/cmds/incidentd/src/PrivacyFilter.h b/cmds/incidentd/src/PrivacyFilter.h new file mode 100644 index 0000000000000000000000000000000000000000..76b28498a0aca8fc881a70e2efb9b41ee7418f84 --- /dev/null +++ b/cmds/incidentd/src/PrivacyFilter.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#ifndef PRIVACY_BUFFER_H +#define PRIVACY_BUFFER_H + +#include "Privacy.h" + +#include "FdBuffer.h" + +#include +#include +#include +#include + +namespace android { +namespace os { +namespace incidentd { + +using namespace android::util; + +/** + * Class to wrap a file descriptor, so callers of PrivacyFilter + * can associate additional data with each fd for their own + * purposes. + */ +class FilterFd : public RefBase { +public: + FilterFd(uint8_t privacyPolicy, int fd); + virtual ~FilterFd(); + + uint8_t getPrivacyPolicy() const { return mPrivacyPolicy; } + int getFd() { return mFd;} + + virtual void onWriteError(status_t err) = 0; + +private: + uint8_t mPrivacyPolicy; + int mFd; +}; + +/** + * PrivacyFilter holds the original protobuf data and strips PII-sensitive fields + * for several requests, streaming them to a set of corresponding file descriptors. + */ +class PrivacyFilter { +public: + /** + * Constructor, with the field --> privacy restrictions mapping. + */ + PrivacyFilter(int sectionId, const Privacy* restrictions); + + ~PrivacyFilter(); + + /** + * Add a target file descriptor, and the privacy policy to which + * it should be filtered. + */ + void addFd(const sp& output); + + /** + * Write the data, filtered according to the privacy specs, to each of the + * file descriptors. Any non-NO_ERROR return codes are fatal to the whole + * report. Individual write errors to streams are reported via the callbacks + * on the FilterFds. + * + * If maxSize is not NULL, it will be set to the maximum size buffer that + * was written (i.e. after filtering). + * + * The buffer is assumed to have already been filtered to bufferLevel. + */ + status_t writeData(const FdBuffer& buffer, uint8_t bufferLevel, size_t* maxSize); + +private: + int mSectionId; + const Privacy* mRestrictions; + vector> mOutputs; +}; + +status_t filter_and_write_report(int to, int from, uint8_t bufferLevel, + const IncidentReportArgs& args); + +} // namespace incidentd +} // namespace os +} // namespace android + +#endif // PRIVACY_BUFFER_H diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp index 8f62da202606ebb9cdabc1f759460826a857113e..e773e74bbf15d06cee749d497ee1c79ae6aad9b2 100644 --- a/cmds/incidentd/src/Reporter.cpp +++ b/cmds/incidentd/src/Reporter.cpp @@ -18,12 +18,17 @@ #include "Reporter.h" +#include "incidentd_util.h" #include "Privacy.h" +#include "PrivacyFilter.h" +#include "proto_util.h" #include "report_directory.h" #include "section_list.h" -#include +#include #include +#include +#include #include #include @@ -33,308 +38,664 @@ #include #include #include - -/** - * The directory where the incident reports are stored. - */ -static const char* INCIDENT_DIRECTORY = "/data/misc/incidents/"; +#include namespace android { namespace os { namespace incidentd { -// ================================================================================ -ReportRequest::ReportRequest(const IncidentReportArgs& a, - const sp& l, int f) - : args(a), listener(l), fd(f), err(NO_ERROR) {} +using namespace android::util; -ReportRequest::~ReportRequest() { - if (fd >= 0) { - // clean up the opened file descriptor - close(fd); +/** + * The field id of the metadata section from + * frameworks/base/core/proto/android/os/incident.proto + */ +const int FIELD_ID_METADATA = 2; + +IncidentMetadata_Destination privacy_policy_to_dest(uint8_t privacyPolicy) { + switch (privacyPolicy) { + case PRIVACY_POLICY_AUTOMATIC: + return IncidentMetadata_Destination_AUTOMATIC; + case PRIVACY_POLICY_EXPLICIT: + return IncidentMetadata_Destination_EXPLICIT; + case PRIVACY_POLICY_LOCAL: + return IncidentMetadata_Destination_LOCAL; + default: + // Anything else reverts to automatic + return IncidentMetadata_Destination_AUTOMATIC; } } -bool ReportRequest::ok() { return fd >= 0 && err == NO_ERROR; } +void poo_make_metadata(IncidentMetadata* result, const IncidentMetadata& full, + int64_t reportId, int32_t privacyPolicy, const IncidentReportArgs& args) { + result->set_report_id(reportId); + result->set_dest(privacy_policy_to_dest(privacyPolicy)); + + size_t sectionCount = full.sections_size(); + for (int sectionIndex = 0; sectionIndex < sectionCount; sectionIndex++) { + const IncidentMetadata::SectionStats& sectionStats = full.sections(sectionIndex); + if (args.containsSection(sectionStats.id())) { + *result->add_sections() = sectionStats; + } + } +} + +// ARGS must have a containsSection(int) method +template void make_metadata(IncidentMetadata* result, const IncidentMetadata& full, + int64_t reportId, int32_t privacyPolicy, ARGS args) { + result->set_report_id(reportId); + result->set_dest(privacy_policy_to_dest(privacyPolicy)); + + size_t sectionCount = full.sections_size(); + for (int sectionIndex = 0; sectionIndex < sectionCount; sectionIndex++) { + const IncidentMetadata::SectionStats& sectionStats = full.sections(sectionIndex); + if (args->containsSection(sectionStats.id())) { + *result->add_sections() = sectionStats; + } + } +} // ================================================================================ -ReportRequestSet::ReportRequestSet() - : mRequests(), mSections(), mMainFd(-1), mMainDest(-1), mMetadata(), mSectionStats() {} +class StreamingFilterFd : public FilterFd { +public: + StreamingFilterFd(uint8_t privacyPolicy, int fd, const sp& request); + + virtual void onWriteError(status_t err); -ReportRequestSet::~ReportRequestSet() {} +private: + sp mRequest; +}; -// TODO: dedup on exact same args and fd, report the status back to listener! -void ReportRequestSet::add(const sp& request) { - mRequests.push_back(request); - mSections.merge(request->args); - mMetadata.set_request_size(mMetadata.request_size() + 1); +StreamingFilterFd::StreamingFilterFd(uint8_t privacyPolicy, int fd, + const sp& request) + :FilterFd(privacyPolicy, fd), + mRequest(request) { } -void ReportRequestSet::setMainFd(int fd) { - mMainFd = fd; - mMetadata.set_use_dropbox(fd > 0); +void StreamingFilterFd::onWriteError(status_t err) { + mRequest->setStatus(err); } -void ReportRequestSet::setMainDest(int dest) { - mMainDest = dest; - PrivacySpec spec = PrivacySpec::new_spec(dest); - switch (spec.dest) { - case android::os::DEST_AUTOMATIC: - mMetadata.set_dest(IncidentMetadata_Destination_AUTOMATIC); - break; - case android::os::DEST_EXPLICIT: - mMetadata.set_dest(IncidentMetadata_Destination_EXPLICIT); - break; - case android::os::DEST_LOCAL: - mMetadata.set_dest(IncidentMetadata_Destination_LOCAL); - break; + +// ================================================================================ +class PersistedFilterFd : public FilterFd { +public: + PersistedFilterFd(uint8_t privacyPolicy, int fd, const sp& reportFile); + + virtual void onWriteError(status_t err); + +private: + sp mReportFile; +}; + +PersistedFilterFd::PersistedFilterFd(uint8_t privacyPolicy, int fd, + const sp& reportFile) + :FilterFd(privacyPolicy, fd), + mReportFile(reportFile) { +} + +void PersistedFilterFd::onWriteError(status_t err) { + mReportFile->setWriteError(err); +} + + +// ================================================================================ +ReportRequest::ReportRequest(const IncidentReportArgs& a, + const sp& listener, int fd) + :args(a), + mListener(listener), + mFd(fd), + mIsStreaming(fd >= 0), + mStatus(NO_ERROR) { +} + +ReportRequest::~ReportRequest() { + if (mIsStreaming && mFd >= 0) { + // clean up the opened file descriptor + close(mFd); } } -bool ReportRequestSet::containsSection(int id) { return mSections.containsSection(id); } +bool ReportRequest::ok() { + return mFd >= 0 && mStatus == NO_ERROR; +} -IncidentMetadata::SectionStats* ReportRequestSet::sectionStats(int id) { - if (mSectionStats.find(id) == mSectionStats.end()) { - IncidentMetadata::SectionStats stats; - stats.set_id(id); - mSectionStats[id] = stats; +void ReportRequest::closeFd() { + if (mIsStreaming && mFd >= 0) { + close(mFd); + mFd = -1; } - return &mSectionStats[id]; } // ================================================================================ -Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; }; +ReportBatch::ReportBatch() {} + +ReportBatch::~ReportBatch() {} + +void ReportBatch::addPersistedReport(const IncidentReportArgs& args) { + ComponentName component(args.receiverPkg(), args.receiverCls()); + map>::iterator found = mPersistedRequests.find(component); + if (found == mPersistedRequests.end()) { + // not found + mPersistedRequests[component] = new ReportRequest(args, nullptr, -1); + } else { + // found + sp request = found->second; + request->args.merge(args); + } +} -Reporter::Reporter(const char* directory) : batch() { - char buf[100]; +void ReportBatch::addStreamingReport(const IncidentReportArgs& args, + const sp& listener, int streamFd) { + mStreamingRequests.push_back(new ReportRequest(args, listener, streamFd)); +} - mMaxSize = 30 * 1024 * 1024; // incident reports can take up to 30MB on disk - mMaxCount = 100; +bool ReportBatch::empty() const { + return mPersistedRequests.size() == 0 && mStreamingRequests.size() == 0; +} - // string ends up with '/' is a directory - String8 dir = String8(directory); - if (directory[dir.size() - 1] != '/') dir += "/"; - mIncidentDirectory = dir.string(); +sp ReportBatch::getPersistedRequest(const ComponentName& component) { + map>::iterator it = mPersistedRequests.find(component); + if (it != mPersistedRequests.find(component)) { + return it->second; + } else { + return nullptr; + } +} - // There can't be two at the same time because it's on one thread. - mStartTime = time(NULL); - strftime(buf, sizeof(buf), "incident-%Y%m%d-%H%M%S", localtime(&mStartTime)); - mFilename = mIncidentDirectory + buf; +void ReportBatch::forEachPersistedRequest(const function&)>& func) { + for (map>::iterator it = mPersistedRequests.begin(); + it != mPersistedRequests.end(); it++) { + func(it->second); + } } -Reporter::~Reporter() {} +void ReportBatch::forEachStreamingRequest(const function&)>& func) { + for (vector>::iterator request = mStreamingRequests.begin(); + request != mStreamingRequests.end(); request++) { + func(*request); + } +} -Reporter::run_report_status_t Reporter::runReport(size_t* reportByteSize) { - status_t err = NO_ERROR; - bool needMainFd = false; - int mainFd = -1; - int mainDest = -1; - int sectionCount = 0; - HeaderSection headers; - MetadataSection metadataSection; - std::string buildType = android::base::GetProperty("ro.build.type", ""); - const bool isUserdebugOrEng = buildType == "userdebug" || buildType == "eng"; - - // See if we need the main file - for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) { - if ((*it)->fd < 0 && mainFd < 0) { - needMainFd = true; - mainDest = (*it)->args.dest(); - break; +void ReportBatch::forEachListener( + const function&)>& func) { + for (map>::iterator it = mPersistedRequests.begin(); + it != mPersistedRequests.end(); it++) { + sp listener = it->second->getListener(); + if (listener != nullptr) { + func(listener); } } - if (needMainFd) { - // Create the directory - if (!isTest) err = create_directory(mIncidentDirectory); - if (err != NO_ERROR) { - goto DONE; + for (vector>::iterator request = mStreamingRequests.begin(); + request != mStreamingRequests.end(); request++) { + sp listener = (*request)->getListener(); + if (listener != nullptr) { + func(listener); } + } +} - // If there are too many files in the directory (for whatever reason), - // delete the oldest ones until it's under the limit. Doing this first - // does mean that we can go over, so the max size is not a hard limit. - if (!isTest) clean_directory(mIncidentDirectory, mMaxSize, mMaxCount); - - // Open the file. - err = create_file(&mainFd); - if (err != NO_ERROR) { - goto DONE; +void ReportBatch::forEachListener(int sectionId, + const function&)>& func) { + for (map>::iterator it = mPersistedRequests.begin(); + it != mPersistedRequests.end(); it++) { + if (it->second->containsSection(sectionId)) { + sp listener = it->second->getListener(); + if (listener != nullptr) { + func(listener); + } } - - // Add to the set - batch.setMainFd(mainFd); - batch.setMainDest(mainDest); } - - // Tell everyone that we're starting. - for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) { - if ((*it)->listener != NULL) { - (*it)->listener->onReportStarted(); + for (vector>::iterator request = mStreamingRequests.begin(); + request != mStreamingRequests.end(); request++) { + if ((*request)->containsSection(sectionId)) { + sp listener = (*request)->getListener(); + if (listener != nullptr) { + func(listener); + } } } +} - // Write the incident headers - headers.Execute(&batch); +void ReportBatch::getCombinedPersistedArgs(IncidentReportArgs* result) { + for (map>::iterator it = mPersistedRequests.begin(); + it != mPersistedRequests.end(); it++) { + result->merge(it->second->args); + } +} - // For each of the report fields, see if we need it, and if so, execute the command - // and report to those that care that we're doing it. - for (const Section** section = SECTION_LIST; *section; section++) { - const int id = (*section)->id; - if ((*section)->userdebugAndEngOnly && !isUserdebugOrEng) { - VLOG("Skipping incident report section %d '%s' because it's limited to userdebug/eng", - id, (*section)->name.string()); - continue; +bool ReportBatch::containsSection(int sectionId) { + // We don't cache this, because in case of error, we remove requests + // from the batch, and this is easier than recomputing the set. + for (map>::iterator it = mPersistedRequests.begin(); + it != mPersistedRequests.end(); it++) { + if (it->second->containsSection(sectionId)) { + return true; } - if (this->batch.containsSection(id)) { - VLOG("Taking incident report section %d '%s'", id, (*section)->name.string()); - for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) { - if ((*it)->listener != NULL && (*it)->args.containsSection(id)) { - (*it)->listener->onReportSectionStatus( - id, IIncidentReportStatusListener::STATUS_STARTING); - } - } + } + for (vector>::iterator request = mStreamingRequests.begin(); + request != mStreamingRequests.end(); request++) { + if ((*request)->containsSection(sectionId)) { + return true; + } + } + return false; +} - // Execute - go get the data and write it into the file descriptors. - IncidentMetadata::SectionStats* stats = batch.sectionStats(id); - int64_t startTime = uptimeMillis(); - err = (*section)->Execute(&batch); - int64_t endTime = uptimeMillis(); - stats->set_exec_duration_ms(endTime - startTime); - if (err != NO_ERROR) { - ALOGW("Incident section %s (%d) failed: %s. Stopping report.", - (*section)->name.string(), id, strerror(-err)); - // Execute() has already recorded this status. Only update if there's new failure. - stats->set_success(false); - goto DONE; - } - (*reportByteSize) += stats->report_size_bytes(); +void ReportBatch::clearPersistedRequests() { + mPersistedRequests.clear(); +} - // Notify listener of starting - for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) { - if ((*it)->listener != NULL && (*it)->args.containsSection(id)) { - (*it)->listener->onReportSectionStatus( - id, IIncidentReportStatusListener::STATUS_FINISHED); - } - } - VLOG("Finish incident report section %d '%s'", id, (*section)->name.string()); - sectionCount++; +void ReportBatch::getFailedRequests(vector>* requests) { + for (map>::iterator it = mPersistedRequests.begin(); + it != mPersistedRequests.end(); it++) { + if (it->second->getStatus() != NO_ERROR) { + requests->push_back(it->second); } } - -DONE: - ALOGD("Incident reporting took %d sections.", sectionCount); - // Reports the metdadata when taking the incident report. - if (!isTest) metadataSection.Execute(&batch); - - // Close the file. - if (mainFd >= 0) { - close(mainFd); - } - - // Tell everyone that we're done. - for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) { - if ((*it)->listener != NULL) { - if (err == NO_ERROR) { - (*it)->listener->onReportFinished(); - } else { - (*it)->listener->onReportFailed(); - } + for (vector>::iterator request = mStreamingRequests.begin(); + request != mStreamingRequests.end(); request++) { + if ((*request)->getStatus() != NO_ERROR) { + requests->push_back(*request); } } +} - // Put the report into dropbox. - if (needMainFd && err == NO_ERROR) { - sp dropbox = new DropBoxManager(); - Status status = dropbox->addFile(String16("incident"), mFilename, 0); - ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string()); - if (!status.isOk()) { - return REPORT_NEEDS_DROPBOX; +void ReportBatch::removeRequest(const sp& request) { + for (map>::iterator it = mPersistedRequests.begin(); + it != mPersistedRequests.end(); it++) { + if (it->second == request) { + mPersistedRequests.erase(it); + return; } + } + for (vector>::iterator it = mStreamingRequests.begin(); + it != mStreamingRequests.end(); it++) { + if (*it == request) { + mStreamingRequests.erase(it); + return; + } + } +} + +// ================================================================================ +ReportWriter::ReportWriter(const sp& batch) + :mBatch(batch), + mPersistedFile(), + mMaxPersistedPrivacyPolicy(PRIVACY_POLICY_UNSET) { +} + +ReportWriter::~ReportWriter() { +} - // If the status was ok, delete the file. If not, leave it around until the next - // boot or the next checkin. If the directory gets too big older files will - // be rotated out. - if (!isTest) unlink(mFilename.c_str()); +void ReportWriter::setPersistedFile(sp file) { + mPersistedFile = file; +} + +void ReportWriter::setMaxPersistedPrivacyPolicy(uint8_t privacyPolicy) { + mMaxPersistedPrivacyPolicy = privacyPolicy; +} + +void ReportWriter::startSection(int sectionId) { + mCurrentSectionId = sectionId; + mSectionStartTimeMs = uptimeMillis(); + + mSectionStatsCalledForSectionId = -1; + mDumpSizeBytes = 0; + mDumpDurationMs = 0; + mSectionTimedOut = false; + mSectionTruncated = false; + mSectionBufferSuccess = false; + mHadError = false; + mSectionErrors.clear(); + +} + +void ReportWriter::setSectionStats(const FdBuffer& buffer) { + mSectionStatsCalledForSectionId = mCurrentSectionId; + mDumpSizeBytes = buffer.size(); + mDumpDurationMs = buffer.durationMs(); + mSectionTimedOut = buffer.timedOut(); + mSectionTruncated = buffer.truncated(); + mSectionBufferSuccess = !buffer.timedOut() && !buffer.truncated(); +} + +void ReportWriter::endSection(IncidentMetadata::SectionStats* sectionMetadata) { + long endTime = uptimeMillis(); + + if (mSectionStatsCalledForSectionId != mCurrentSectionId) { + ALOGW("setSectionStats not called for section %d", mCurrentSectionId); } - return REPORT_FINISHED; + sectionMetadata->set_id(mCurrentSectionId); + sectionMetadata->set_success((!mHadError) && mSectionBufferSuccess); + sectionMetadata->set_report_size_bytes(mMaxSectionDataFilteredSize); + sectionMetadata->set_exec_duration_ms(endTime - mSectionStartTimeMs); + sectionMetadata->set_dump_size_bytes(mDumpSizeBytes); + sectionMetadata->set_dump_duration_ms(mDumpDurationMs); + sectionMetadata->set_timed_out(mSectionTimedOut); + sectionMetadata->set_is_truncated(mSectionTruncated); + sectionMetadata->set_error_msg(mSectionErrors); } -/** - * Create our output file and set the access permissions to -rw-rw---- - */ -status_t Reporter::create_file(int* fd) { - const char* filename = mFilename.c_str(); +void ReportWriter::warning(const Section* section, status_t err, const char* format, ...) { + va_list args; + va_start(args, format); + vflog(section, err, ANDROID_LOG_ERROR, "error", format, args); + va_end(args); +} - *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660); - if (*fd < 0) { - ALOGE("Couldn't open incident file: %s (%s)", filename, strerror(errno)); - return -errno; +void ReportWriter::error(const Section* section, status_t err, const char* format, ...) { + va_list args; + va_start(args, format); + vflog(section, err, ANDROID_LOG_WARN, "warning", format, args); + va_end(args); +} + +void ReportWriter::vflog(const Section* section, status_t err, int level, const char* levelText, + const char* format, va_list args) { + const char* prefixFormat = "%s in section %d (%d) '%s': "; + int prefixLen = snprintf(NULL, 0, prefixFormat, levelText, section->id, + err, strerror(-err)); + + va_list measureArgs; + va_copy(measureArgs, args); + int messageLen = vsnprintf(NULL, 0, format, args); + va_end(measureArgs); + + char* line = (char*)malloc(prefixLen + messageLen + 1); + if (line == NULL) { + // All hope is lost, just give up. + return; } - // Override umask. Not super critical. If it fails go on with life. - chmod(filename, 0660); + sprintf(line, prefixFormat, levelText, section->id, err, strerror(-err)); + + vsprintf(line + prefixLen, format, args); - if (chown(filename, AID_INCIDENTD, AID_INCIDENTD)) { - ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno)); - status_t err = -errno; - unlink(mFilename.c_str()); - return err; + __android_log_write(level, LOG_TAG, line); + + if (mSectionErrors.length() == 0) { + mSectionErrors = line; + } else { + mSectionErrors += '\n'; + mSectionErrors += line; + } + + free(line); + + if (level >= ANDROID_LOG_ERROR) { + mHadError = true; + } +} + +// Reads data from FdBuffer and writes it to the requests file descriptor. +status_t ReportWriter::writeSection(const FdBuffer& buffer) { + PrivacyFilter filter(mCurrentSectionId, get_privacy_of_section(mCurrentSectionId)); + + // Add the fd for the persisted requests + if (mPersistedFile != nullptr) { + filter.addFd(new PersistedFilterFd(mMaxPersistedPrivacyPolicy, + mPersistedFile->getDataFileFd(), mPersistedFile)); } - return NO_ERROR; + // Add the fds for the streamed requests + mBatch->forEachStreamingRequest([&filter, this](const sp& request) { + if (request->ok() && request->args.containsSection(mCurrentSectionId)) { + filter.addFd(new StreamingFilterFd(request->args.getPrivacyPolicy(), + request->getFd(), request)); + } + }); + + return filter.writeData(buffer, PRIVACY_POLICY_LOCAL, &mMaxSectionDataFilteredSize); } -Reporter::run_report_status_t Reporter::upload_backlog() { - DIR* dir; - struct dirent* entry; - struct stat st; - status_t err; - ALOGD("Start uploading backlogs in %s", INCIDENT_DIRECTORY); - if ((err = create_directory(INCIDENT_DIRECTORY)) != NO_ERROR) { - ALOGE("directory doesn't exist: %s", strerror(-err)); - return REPORT_FINISHED; +// ================================================================================ +Reporter::Reporter(const sp& workDirectory, const sp& batch) + :mWorkDirectory(workDirectory), + mWriter(batch), + mBatch(batch) { +} + +Reporter::~Reporter() { +} + +void Reporter::runReport(size_t* reportByteSize) { + status_t err = NO_ERROR; + + IncidentMetadata metadata; + int persistedPrivacyPolicy = PRIVACY_POLICY_UNSET; + + (*reportByteSize) = 0; + + // Tell everyone that we're starting. + ALOGI("Starting incident report"); + mBatch->forEachListener([](const auto& listener) { listener->onReportStarted(); }); + + if (mBatch->hasPersistedReports()) { + // Open a work file to contain the contents of all of the persisted reports. + // For this block, if we can't initialize the report file for some reason, + // then we will remove the persisted ReportRequests from the report, but + // continue with the streaming ones. + mPersistedFile = mWorkDirectory->createReportFile(); + ALOGI("Report will be persisted: envelope: %s data: %s", + mPersistedFile->getEnvelopeFileName().c_str(), + mPersistedFile->getDataFileName().c_str()); + + // Record all of the metadata to the persisted file's metadata file. + // It will be read from there and reconstructed as the actual reports + // are sent out. + if (mPersistedFile != nullptr) { + mBatch->forEachPersistedRequest([this, &persistedPrivacyPolicy]( + const sp& request) { + mPersistedFile->addReport(request->args); + if (request->args.getPrivacyPolicy() < persistedPrivacyPolicy) { + persistedPrivacyPolicy = request->args.getPrivacyPolicy(); + } + }); + mPersistedFile->setMaxPersistedPrivacyPolicy(persistedPrivacyPolicy); + err = mPersistedFile->saveEnvelope(); + if (err != NO_ERROR) { + mWorkDirectory->remove(mPersistedFile); + mPersistedFile = nullptr; + } + mWriter.setMaxPersistedPrivacyPolicy(persistedPrivacyPolicy); + } + + if (mPersistedFile != nullptr) { + err = mPersistedFile->startWritingDataFile(); + if (err != NO_ERROR) { + mWorkDirectory->remove(mPersistedFile); + mPersistedFile = nullptr; + } + } + + if (mPersistedFile != nullptr) { + mWriter.setPersistedFile(mPersistedFile); + } else { + ALOGW("Error creating the persisted file, so clearing persisted reports."); + // If we couldn't open the file (permissions err, etc), then + // we still want to proceed with any streaming reports, but + // cancel all of the persisted ones. + mBatch->forEachPersistedRequest([](const sp& request) { + sp listener = request->getListener(); + if (listener != nullptr) { + listener->onReportFailed(); + } + }); + mBatch->clearPersistedRequests(); + } } - if ((dir = opendir(INCIDENT_DIRECTORY)) == NULL) { - ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY); - return REPORT_NEEDS_DROPBOX; + // If we have a persisted ID, then we allow all the readers to see that. There's + // enough in the data to allow for a join, and nothing in here that intrisincally + // could ever prevent that, so just give them the ID. If we don't have that then we + // make and ID that's extremely likely to be unique, but clock resetting could allow + // it to be duplicate. + int64_t reportId; + if (mPersistedFile != nullptr) { + reportId = mPersistedFile->getTimestampNs(); + } else { + struct timespec spec; + clock_gettime(CLOCK_REALTIME, &spec); + reportId = (spec.tv_sec) * 1000 + spec.tv_nsec; } - sp dropbox = new DropBoxManager(); + // Write the incident report headers - each request gets its own headers. It's different + // from the other top-level fields in IncidentReport that are the sections where the rest + // is all shared data (although with their own individual privacy filtering). + mBatch->forEachStreamingRequest([](const sp& request) { + const vector>& headers = request->args.headers(); + for (vector>::const_iterator buf = headers.begin(); buf != headers.end(); + buf++) { + // If there was an error now, there will be an error later and we will remove + // it from the list then. + write_header_section(request->getFd(), *buf); + } + }); + + // If writing to any of the headers failed, we don't want to keep processing + // sections for it. + cancel_and_remove_failed_requests(); + + // For each of the report fields, see if we need it, and if so, execute the command + // and report to those that care that we're doing it. + for (const Section** section = SECTION_LIST; *section; section++) { + const int sectionId = (*section)->id; - // Enumerate, count and add up size - int count = 0; - while ((entry = readdir(dir)) != NULL) { - if (entry->d_name[0] == '.') { + // If nobody wants this section, skip it. + if (!mBatch->containsSection(sectionId)) { continue; } - String8 filename = String8(INCIDENT_DIRECTORY) + entry->d_name; - if (stat(filename.string(), &st) != 0) { - ALOGE("Unable to stat file %s", filename.string()); - continue; + + ALOGD("Start incident report section %d '%s'", sectionId, (*section)->name.string()); + IncidentMetadata::SectionStats* sectionMetadata = metadata.add_sections(); + + // Notify listener of starting + mBatch->forEachListener(sectionId, [sectionId](const auto& listener) { + listener->onReportSectionStatus( + sectionId, IIncidentReportStatusListener::STATUS_STARTING); + }); + + // Go get the data and write it into the file descriptors. + mWriter.startSection(sectionId); + err = (*section)->Execute(&mWriter); + mWriter.endSection(sectionMetadata); + + // Sections returning errors are fatal. Most errors should not be fatal. + if (err != NO_ERROR) { + mWriter.error((*section), err, "Section failed. Stopping report."); + goto DONE; } - if (!S_ISREG(st.st_mode)) { - continue; + + // The returned max data size is used for throttling too many incident reports. + (*reportByteSize) += sectionMetadata->report_size_bytes(); + + // For any requests that failed during this section, remove them now. We do this + // before calling back about section finished, so listeners do not erroniously get the + // impression that the section succeeded. But we do it here instead of inside + // writeSection so that the callback is done from a known context and not from the + // bowels of a section, where changing the batch could cause odd errors. + cancel_and_remove_failed_requests(); + + // Notify listener of finishing + mBatch->forEachListener(sectionId, [sectionId](const auto& listener) { + listener->onReportSectionStatus( + sectionId, IIncidentReportStatusListener::STATUS_FINISHED); + }); + + ALOGD("Finish incident report section %d '%s'", sectionId, (*section)->name.string()); + } + +DONE: + // Finish up the persisted file. + if (mPersistedFile != nullptr) { + mPersistedFile->closeDataFile(); + + // Set the stored metadata + IncidentReportArgs combinedArgs; + mBatch->getCombinedPersistedArgs(&combinedArgs); + IncidentMetadata persistedMetadata; + make_metadata(&persistedMetadata, metadata, mPersistedFile->getTimestampNs(), + persistedPrivacyPolicy, &combinedArgs); + mPersistedFile->setMetadata(persistedMetadata); + + mPersistedFile->markCompleted(); + err = mPersistedFile->saveEnvelope(); + if (err != NO_ERROR) { + ALOGW("mPersistedFile->saveEnvelope returned %s. Won't send broadcast", + strerror(-err)); + // Abandon ship. + mWorkDirectory->remove(mPersistedFile); } + } - Status status = dropbox->addFile(String16("incident"), filename.string(), 0); - ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string()); - if (!status.isOk()) { - return REPORT_NEEDS_DROPBOX; + // Write the metadata to the streaming ones + mBatch->forEachStreamingRequest([reportId, &metadata](const sp& request) { + IncidentMetadata streamingMetadata; + make_metadata(&streamingMetadata, metadata, reportId, + request->args.getPrivacyPolicy(), request); + status_t nonFatalErr = write_section(request->getFd(), FIELD_ID_METADATA, + streamingMetadata); + if (nonFatalErr != NO_ERROR) { + ALOGW("Error writing the metadata to streaming incident report. This is the last" + " thing so we won't return an error: %s", strerror(nonFatalErr)); } + }); + + // Finish up the streaming ones. + mBatch->forEachStreamingRequest([](const sp& request) { + request->closeFd(); + }); + + // Tell the listeners that we're done. + if (err == NO_ERROR) { + mBatch->forEachListener([](const auto& listener) { + listener->onReportFinished(); + }); + } else { + mBatch->forEachListener([](const auto& listener) { + listener->onReportFailed(); + }); + } + + ALOGI("Done taking incident report err=%s", strerror(-err)); +} - // If the status was ok, delete the file. If not, leave it around until the next - // boot or the next checkin. If the directory gets too big older files will - // be rotated out. - unlink(filename.string()); - count++; +void Reporter::cancel_and_remove_failed_requests() { + // Handle a failure in the persisted file + if (mPersistedFile != nullptr) { + if (mPersistedFile->getWriteError() != NO_ERROR) { + ALOGW("Error writing to the persisted file (%s). Closing it and canceling.", + strerror(-mPersistedFile->getWriteError())); + mBatch->forEachPersistedRequest([this](const sp& request) { + sp listener = request->getListener(); + if (listener != nullptr) { + listener->onReportFailed(); + } + mBatch->removeRequest(request); + }); + mWriter.setPersistedFile(nullptr); + mPersistedFile->closeDataFile(); + mWorkDirectory->remove(mPersistedFile); + mPersistedFile = nullptr; + } } - ALOGD("Successfully uploaded %d files to Dropbox.", count); - closedir(dir); - return REPORT_FINISHED; + // Handle failures in the streaming files + vector> failed; + mBatch->getFailedRequests(&failed); + for (sp& request: failed) { + ALOGW("Error writing to a request stream (%s). Closing it and canceling.", + strerror(-request->getStatus())); + sp listener = request->getListener(); + if (listener != nullptr) { + listener->onReportFailed(); + } + request->closeFd(); // Will only close the streaming ones. + mBatch->removeRequest(request); + } } } // namespace incidentd diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h index 2a3abd7b4d976257b5e1b605731cab89b3f106f5..e7a474fde40ce3f9128b5f90d9988349081cd076 100644 --- a/cmds/incidentd/src/Reporter.h +++ b/cmds/incidentd/src/Reporter.h @@ -15,103 +15,247 @@ */ #pragma once -#ifndef REPORTER_H -#define REPORTER_H +#include "FdBuffer.h" +#include "Throttler.h" +#include "WorkDirectory.h" +#include "frameworks/base/core/proto/android/os/metadata.pb.h" +#include #include #include +#include #include #include #include #include - -#include "Throttler.h" -#include "frameworks/base/libs/incident/proto/android/os/metadata.pb.h" +#include namespace android { namespace os { namespace incidentd { +using namespace std; +using namespace android::content; +using namespace android::os; + +class Section; + // ================================================================================ -struct ReportRequest : public virtual RefBase { +class ReportRequest : public virtual RefBase { +public: IncidentReportArgs args; - sp listener; - int fd; - status_t err; ReportRequest(const IncidentReportArgs& args, const sp& listener, int fd); virtual ~ReportRequest(); + bool isStreaming() { return mIsStreaming; } + + void setStatus(status_t err) { mStatus = err; } + status_t getStatus() const { return mStatus; } + bool ok(); // returns true if the request is ok for write. + + bool containsSection(int sectionId) const { return args.containsSection(sectionId); } + + sp getListener() { return mListener; } + + int getFd() { return mFd; } + + int setPersistedFd(int fd); + + void closeFd(); + +private: + sp mListener; + int mFd; + bool mIsStreaming; + status_t mStatus; }; // ================================================================================ -class ReportRequestSet { +class ReportBatch : public virtual RefBase { public: - ReportRequestSet(); - ~ReportRequestSet(); + ReportBatch(); + virtual ~ReportBatch(); + + // TODO: Should there be some kind of listener associated with the + // component? Could be good for getting status updates e.g. in the ui, + // as it progresses. But that's out of scope for now. + + /** + * Schedule a report for the "main" report, where it will be delivered to + * the uploaders and/or dropbox. + */ + void addPersistedReport(const IncidentReportArgs& args); + + /** + * Adds a ReportRequest to the queue for one that has a listener an and fd + */ + void addStreamingReport(const IncidentReportArgs& args, + const sp& listener, int streamFd); - void add(const sp& request); - void setMainFd(int fd); - void setMainDest(int dest); + /** + * Returns whether both queues are empty. + */ + bool empty() const; - typedef vector>::iterator iterator; + /** + * Returns whether there are any persisted records. + */ + bool hasPersistedReports() const { return mPersistedRequests.size() > 0; } - iterator begin() { return mRequests.begin(); } - iterator end() { return mRequests.end(); } + /** + * Return the persisted request for the given component, or nullptr. + */ + sp getPersistedRequest(const ComponentName& component); - int mainFd() { return mMainFd; } - int mainDest() { return mMainDest; } - IncidentMetadata& metadata() { return mMetadata; } - map& allSectionStats() { return mSectionStats; } + /** + * Call func(request) for each Request. + */ + void forEachPersistedRequest(const function&)>& func); + /** + * Call func(request) for each Request. + */ + void forEachStreamingRequest(const function&)>& func); + + /** + * Call func(request) for each file descriptor that has + */ + void forEachFd(int sectionId, const function&)>& func); + + /** + * Call func(listener) for every listener in this batch. + */ + void forEachListener(const function&)>& func); + + /** + * Call func(listener) for every listener in this batch that requests + * sectionId. + */ + void forEachListener(int sectionId, + const function&)>& func); + /** + * Get an IncidentReportArgs that represents the combined args for the + * persisted requests. + */ + void getCombinedPersistedArgs(IncidentReportArgs* results); + + /** + * Return whether any of the requests contain the section. + */ bool containsSection(int id); - IncidentMetadata::SectionStats* sectionStats(int id); -private: - vector> mRequests; - IncidentReportArgs mSections; - int mMainFd; - int mMainDest; + /** + * Remove all of the broadcast (persisted) requests. + */ + void clearPersistedRequests(); + + /** + * Get the requests that have encountered errors. + */ + void getFailedRequests(vector>* requests); - IncidentMetadata mMetadata; - map mSectionStats; + /** + * Remove the request from whichever list it's in. + */ + void removeRequest(const sp& request); + + +private: + map> mPersistedRequests; + vector> mStreamingRequests; }; // ================================================================================ -class Reporter : public virtual RefBase { +class ReportWriter { public: - enum run_report_status_t { REPORT_FINISHED = 0, REPORT_NEEDS_DROPBOX = 1 }; + ReportWriter(const sp& batch); + ~ReportWriter(); - ReportRequestSet batch; + void setPersistedFile(sp file); + void setMaxPersistedPrivacyPolicy(uint8_t privacyPolicy); - Reporter(); // PROD must use this constructor. - explicit Reporter(const char* directory); // For testing purpose only. - virtual ~Reporter(); + void startSection(int sectionId); + void endSection(IncidentMetadata::SectionStats* sectionStats); - // Run the report as described in the batch and args parameters. - run_report_status_t runReport(size_t* reportByteSize); + void setSectionStats(const FdBuffer& buffer); - static run_report_status_t upload_backlog(); + void warning(const Section* section, status_t err, const char* format, ...); + void error(const Section* section, status_t err, const char* format, ...); + + status_t writeSection(const FdBuffer& buffer); private: - String8 mIncidentDirectory; + // Data about all requests + sp mBatch; + + /** + * The file on disk where we will store the persisted file. + */ + sp mPersistedFile; + + /** + * The least restricted privacy policy of all of the perstited + * requests. We pre-filter to that to save disk space. + */ + uint8_t mMaxPersistedPrivacyPolicy; + + /** + * The current section that is being written. + */ + int mCurrentSectionId; + + /** + * The time that that the current section was started. + */ + int64_t mSectionStartTimeMs; + + /** + * The last section that setSectionStats was called for, so if someone misses + * it we can log that. + */ + int mSectionStatsCalledForSectionId; - string mFilename; - off_t mMaxSize; - size_t mMaxCount; - time_t mStartTime; + /* + * Fields for IncidentMetadata.SectionStats. Set by setSectionStats. Accessed by + * getSectionStats. + */ + int32_t mDumpSizeBytes; + int64_t mDumpDurationMs; + bool mSectionTimedOut; + bool mSectionTruncated; + bool mSectionBufferSuccess; + bool mHadError; + string mSectionErrors; + size_t mMaxSectionDataFilteredSize; - status_t create_file(int* fd); + void vflog(const Section* section, status_t err, int level, const char* levelText, + const char* format, va_list args); +}; + +// ================================================================================ +class Reporter : public virtual RefBase { +public: + Reporter(const sp& workDirectory, const sp& batch); + + virtual ~Reporter(); + + // Run the report as described in the batch and args parameters. + void runReport(size_t* reportByteSize); - bool isTest = true; // default to true for testing +private: + sp mWorkDirectory; + ReportWriter mWriter; + sp mBatch; + sp mPersistedFile; + + void cancel_and_remove_failed_requests(); }; } // namespace incidentd } // namespace os } // namespace android - -#endif // REPORTER_H diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index 32ec1ba90cdaf593e070cc7797463e4ab8043a6d..935a7c43fe900a849c8fdc635ad51d3efc599d15 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -25,8 +25,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -37,7 +39,6 @@ #include "FdBuffer.h" #include "Privacy.h" -#include "PrivacyBuffer.h" #include "frameworks/base/core/proto/android/os/backtrace.proto.h" #include "frameworks/base/core/proto/android/os/data.proto.h" #include "frameworks/base/core/proto/android/util/log.proto.h" @@ -51,7 +52,6 @@ using namespace android::base; using namespace android::util; // special section ids -const int FIELD_ID_INCIDENT_HEADER = 1; const int FIELD_ID_INCIDENT_METADATA = 2; // incident section parameters @@ -64,206 +64,18 @@ static pid_t fork_execute_incident_helper(const int id, Fpipe* p2cPipe, Fpipe* c } // ================================================================================ -static status_t write_section_header(int fd, int sectionId, size_t size) { - uint8_t buf[20]; - uint8_t* p = write_length_delimited_tag_header(buf, sectionId, size); - return WriteFully(fd, buf, p - buf) ? NO_ERROR : -errno; -} - -static void write_section_stats(IncidentMetadata::SectionStats* stats, const FdBuffer& buffer) { - stats->set_dump_size_bytes(buffer.data().size()); - stats->set_dump_duration_ms(buffer.durationMs()); - stats->set_timed_out(buffer.timedOut()); - stats->set_is_truncated(buffer.truncated()); - stats->set_success(!buffer.timedOut() && !buffer.truncated()); -} - -// Reads data from FdBuffer and writes it to the requests file descriptor. -static status_t write_report_requests(const int id, const FdBuffer& buffer, - ReportRequestSet* requests) { - status_t err = -EBADF; - EncodedBuffer::iterator data = buffer.data(); - PrivacyBuffer privacyBuffer(get_privacy_of_section(id), data); - IncidentMetadata::SectionStats* stats = requests->sectionStats(id); - int nPassed = 0; - - // The streaming ones, group requests by spec in order to save unnecessary strip operations - map>> requestsBySpec; - for (auto it = requests->begin(); it != requests->end(); it++) { - sp request = *it; - if (!request->ok() || !request->args.containsSection(id)) { - continue; // skip invalid request - } - PrivacySpec spec = PrivacySpec::new_spec(request->args.dest()); - requestsBySpec[spec].push_back(request); - } - - for (auto mit = requestsBySpec.begin(); mit != requestsBySpec.end(); mit++) { - PrivacySpec spec = mit->first; - err = privacyBuffer.strip(spec); - if (err != NO_ERROR) { - // Privacy Buffer is corrupted, probably due to an ill-formatted proto. This is a - // non-fatal error. The whole report is still valid. So just log the failure. - ALOGW("Failed to strip section %d with spec %d: %s", - id, spec.dest, strerror(-err)); - stats->set_success(false); - stats->set_error_msg("Failed to strip section: privacy buffer corrupted, probably " - "due to an ill-formatted proto"); - nPassed++; - continue; - } - - if (privacyBuffer.size() == 0) continue; - - for (auto it = mit->second.begin(); it != mit->second.end(); it++) { - sp request = *it; - err = write_section_header(request->fd, id, privacyBuffer.size()); - if (err != NO_ERROR) { - request->err = err; - continue; - } - err = privacyBuffer.flush(request->fd); - if (err != NO_ERROR) { - request->err = err; - continue; - } - nPassed++; - VLOG("Section %d flushed %zu bytes to fd %d with spec %d", id, privacyBuffer.size(), - request->fd, spec.dest); - } - privacyBuffer.clear(); - } - - // The dropbox file - if (requests->mainFd() >= 0) { - PrivacySpec spec = PrivacySpec::new_spec(requests->mainDest()); - err = privacyBuffer.strip(spec); - if (err != NO_ERROR) { - ALOGW("Failed to strip section %d with spec %d for dropbox: %s", - id, spec.dest, strerror(-err)); - stats->set_success(false); - stats->set_error_msg("Failed to strip section: privacy buffer corrupted, probably " - "due to an ill-formatted proto"); - nPassed++; - goto DONE; - } - if (privacyBuffer.size() == 0) goto DONE; - - err = write_section_header(requests->mainFd(), id, privacyBuffer.size()); - if (err != NO_ERROR) { - requests->setMainFd(-1); - goto DONE; - } - err = privacyBuffer.flush(requests->mainFd()); - if (err != NO_ERROR) { - requests->setMainFd(-1); - goto DONE; - } - nPassed++; - VLOG("Section %d flushed %zu bytes to dropbox %d with spec %d", id, privacyBuffer.size(), - requests->mainFd(), spec.dest); - // Reports bytes of the section uploaded via dropbox after filtering. - requests->sectionStats(id)->set_report_size_bytes(privacyBuffer.size()); - } - -DONE: - // only returns error if there is no fd to write to. - return nPassed > 0 ? NO_ERROR : err; -} - -// ================================================================================ -Section::Section(int i, int64_t timeoutMs, bool userdebugAndEngOnly) +Section::Section(int i, int64_t timeoutMs) : id(i), - timeoutMs(timeoutMs), - userdebugAndEngOnly(userdebugAndEngOnly) {} - -Section::~Section() {} - -// ================================================================================ -HeaderSection::HeaderSection() : Section(FIELD_ID_INCIDENT_HEADER, 0) {} - -HeaderSection::~HeaderSection() {} - -status_t HeaderSection::Execute(ReportRequestSet* requests) const { - for (ReportRequestSet::iterator it = requests->begin(); it != requests->end(); it++) { - const sp request = *it; - const vector>& headers = request->args.headers(); - - for (vector>::const_iterator buf = headers.begin(); buf != headers.end(); - buf++) { - if (buf->empty()) continue; - - // So the idea is only requests with negative fd are written to dropbox file. - int fd = request->fd >= 0 ? request->fd : requests->mainFd(); - write_section_header(fd, id, buf->size()); - WriteFully(fd, (uint8_t const*)buf->data(), buf->size()); - // If there was an error now, there will be an error later and we will remove - // it from the list then. - } - } - return NO_ERROR; + timeoutMs(timeoutMs) { } -// ================================================================================ -MetadataSection::MetadataSection() : Section(FIELD_ID_INCIDENT_METADATA, 0) {} -MetadataSection::~MetadataSection() {} - -status_t MetadataSection::Execute(ReportRequestSet* requests) const { - ProtoOutputStream proto; - IncidentMetadata metadata = requests->metadata(); - proto.write(FIELD_TYPE_ENUM | IncidentMetadata::kDestFieldNumber, metadata.dest()); - proto.write(FIELD_TYPE_INT32 | IncidentMetadata::kRequestSizeFieldNumber, - metadata.request_size()); - proto.write(FIELD_TYPE_BOOL | IncidentMetadata::kUseDropboxFieldNumber, metadata.use_dropbox()); - for (auto iter = requests->allSectionStats().begin(); iter != requests->allSectionStats().end(); - iter++) { - IncidentMetadata::SectionStats stats = iter->second; - uint64_t token = proto.start(FIELD_TYPE_MESSAGE | IncidentMetadata::kSectionsFieldNumber); - proto.write(FIELD_TYPE_INT32 | IncidentMetadata::SectionStats::kIdFieldNumber, stats.id()); - proto.write(FIELD_TYPE_BOOL | IncidentMetadata::SectionStats::kSuccessFieldNumber, - stats.success()); - proto.write(FIELD_TYPE_INT32 | IncidentMetadata::SectionStats::kReportSizeBytesFieldNumber, - stats.report_size_bytes()); - proto.write(FIELD_TYPE_INT64 | IncidentMetadata::SectionStats::kExecDurationMsFieldNumber, - stats.exec_duration_ms()); - proto.write(FIELD_TYPE_INT32 | IncidentMetadata::SectionStats::kDumpSizeBytesFieldNumber, - stats.dump_size_bytes()); - proto.write(FIELD_TYPE_INT64 | IncidentMetadata::SectionStats::kDumpDurationMsFieldNumber, - stats.dump_duration_ms()); - proto.write(FIELD_TYPE_BOOL | IncidentMetadata::SectionStats::kTimedOutFieldNumber, - stats.timed_out()); - proto.write(FIELD_TYPE_BOOL | IncidentMetadata::SectionStats::kIsTruncatedFieldNumber, - stats.is_truncated()); - proto.write(FIELD_TYPE_STRING | IncidentMetadata::SectionStats::kErrorMsgFieldNumber, - stats.error_msg()); - proto.end(token); - } +Section::~Section() {} - for (ReportRequestSet::iterator it = requests->begin(); it != requests->end(); it++) { - const sp request = *it; - if (request->fd < 0 || request->err != NO_ERROR) { - continue; - } - write_section_header(request->fd, id, proto.size()); - if (!proto.flush(request->fd)) { - ALOGW("Failed to write metadata to fd %d", request->fd); - // we don't fail if we can't write to a single request's fd. - } - } - if (requests->mainFd() >= 0) { - write_section_header(requests->mainFd(), id, proto.size()); - if (!proto.flush(requests->mainFd())) { - ALOGW("Failed to write metadata to dropbox fd %d", requests->mainFd()); - return -1; - } - } - return NO_ERROR; -} // ================================================================================ static inline bool isSysfs(const char* filename) { return strncmp(filename, "/sys/", 5) == 0; } FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs) - : Section(id, timeoutMs, false), mFilename(filename) { + : Section(id, timeoutMs), mFilename(filename) { name = "file "; name += filename; mIsSysfs = isSysfs(filename); @@ -271,7 +83,7 @@ FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs) FileSection::~FileSection() {} -status_t FileSection::Execute(ReportRequestSet* requests) const { +status_t FileSection::Execute(ReportWriter* writer) const { // read from mFilename first, make sure the file is available // add O_CLOEXEC to make sure it is closed when exec incident helper unique_fd fd(open(mFilename, O_RDONLY | O_CLOEXEC)); @@ -301,7 +113,7 @@ status_t FileSection::Execute(ReportRequestSet* requests) const { status_t readStatus = buffer.readProcessedDataInStream(fd.get(), std::move(p2cPipe.writeFd()), std::move(c2pPipe.readFd()), this->timeoutMs, mIsSysfs); - write_section_stats(requests->sectionStats(this->id), buffer); + writer->setSectionStats(buffer); if (readStatus != NO_ERROR || buffer.timedOut()) { ALOGW("[%s] failed to read data from incident helper: %s, timedout: %s", this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); @@ -315,7 +127,7 @@ status_t FileSection::Execute(ReportRequestSet* requests) const { return ihStatus; } - return write_report_requests(this->id, buffer, requests); + return writer->writeSection(buffer); } // ================================================================================ GZipSection::GZipSection(int id, const char* filename, ...) : Section(id) { @@ -332,7 +144,7 @@ GZipSection::GZipSection(int id, const char* filename, ...) : Section(id) { GZipSection::~GZipSection() { free(mFilenames); } -status_t GZipSection::Execute(ReportRequestSet* requests) const { +status_t GZipSection::Execute(ReportWriter* writer) const { // Reads the files in order, use the first available one. int index = 0; unique_fd fd; @@ -366,7 +178,7 @@ status_t GZipSection::Execute(ReportRequestSet* requests) const { // construct Fdbuffer to output GZippedfileProto, the reason to do this instead of using // ProtoOutputStream is to avoid allocation of another buffer inside ProtoOutputStream. - EncodedBuffer* internalBuffer = buffer.getInternalBuffer(); + sp internalBuffer = buffer.data(); internalBuffer->writeHeader((uint32_t)GZippedFileProto::FILENAME, WIRE_TYPE_LENGTH_DELIMITED); size_t fileLen = strlen(mFilenames[index]); internalBuffer->writeRawVarint32(fileLen); @@ -383,7 +195,7 @@ status_t GZipSection::Execute(ReportRequestSet* requests) const { status_t readStatus = buffer.readProcessedDataInStream( fd.get(), std::move(p2cPipe.writeFd()), std::move(c2pPipe.readFd()), this->timeoutMs, isSysfs(mFilenames[index])); - write_section_stats(requests->sectionStats(this->id), buffer); + writer->setSectionStats(buffer); if (readStatus != NO_ERROR || buffer.timedOut()) { ALOGW("[%s] failed to read data from gzip: %s, timedout: %s", this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); @@ -402,7 +214,7 @@ status_t GZipSection::Execute(ReportRequestSet* requests) const { internalBuffer->writeRawVarint32(dataSize); internalBuffer->copy(dataBeginAt, dataSize); - return write_report_requests(this->id, buffer, requests); + return writer->writeSection(buffer); } // ================================================================================ @@ -425,8 +237,8 @@ WorkerThreadData::WorkerThreadData(const WorkerThreadSection* sec) WorkerThreadData::~WorkerThreadData() {} // ================================================================================ -WorkerThreadSection::WorkerThreadSection(int id, const int64_t timeoutMs, bool userdebugAndEngOnly) - : Section(id, timeoutMs, userdebugAndEngOnly) {} +WorkerThreadSection::WorkerThreadSection(int id, const int64_t timeoutMs) + : Section(id, timeoutMs) {} WorkerThreadSection::~WorkerThreadSection() {} @@ -458,13 +270,12 @@ static void* worker_thread_func(void* cookie) { return NULL; } -status_t WorkerThreadSection::Execute(ReportRequestSet* requests) const { +status_t WorkerThreadSection::Execute(ReportWriter* writer) const { status_t err = NO_ERROR; pthread_t thread; pthread_attr_t attr; bool workerDone = false; FdBuffer buffer; - IncidentMetadata::SectionStats* stats = requests->sectionStats(this->id); // Data shared between this thread and the worker thread. sp data = new WorkerThreadData(this); @@ -474,10 +285,6 @@ status_t WorkerThreadSection::Execute(ReportRequestSet* requests) const { return -errno; } - // The worker thread needs a reference and we can't let the count go to zero - // if that thread is slow to start. - data->incStrong(this); - // Create the thread err = pthread_attr_init(&attr); if (err != 0) { @@ -489,12 +296,17 @@ status_t WorkerThreadSection::Execute(ReportRequestSet* requests) const { pthread_attr_destroy(&attr); return -err; } + + // The worker thread needs a reference and we can't let the count go to zero + // if that thread is slow to start. + data->incStrong(this); + err = pthread_create(&thread, &attr, worker_thread_func, (void*)data.get()); + pthread_attr_destroy(&attr); if (err != 0) { - pthread_attr_destroy(&attr); + data->decStrong(this); return -err; } - pthread_attr_destroy(&attr); // Loop reading until either the timeout or the worker side is done (i.e. eof). err = buffer.read(data->pipe.readFd().get(), this->timeoutMs); @@ -517,18 +329,17 @@ status_t WorkerThreadSection::Execute(ReportRequestSet* requests) const { workerDone = data->workerDone; } - write_section_stats(stats, buffer); + writer->setSectionStats(buffer); if (err != NO_ERROR) { char errMsg[128]; snprintf(errMsg, 128, "[%s] failed with error '%s'", this->name.string(), strerror(-err)); - stats->set_success(false); - stats->set_error_msg(errMsg); + writer->error(this, err, "WorkerThreadSection failed."); return NO_ERROR; } if (buffer.truncated()) { ALOGW("[%s] too large, truncating", this->name.string()); - // Do not write a truncated section. It won't pass through the PrivacyBuffer. + // Do not write a truncated section. It won't pass through the PrivacyFilter. return NO_ERROR; } if (!workerDone || buffer.timedOut()) { @@ -537,7 +348,7 @@ status_t WorkerThreadSection::Execute(ReportRequestSet* requests) const { } // Write the data that was collected - return write_report_requests(this->id, buffer, requests); + return writer->writeSection(buffer); } // ================================================================================ @@ -568,7 +379,7 @@ CommandSection::CommandSection(int id, const char* command, ...) : Section(id) { CommandSection::~CommandSection() { free(mCommand); } -status_t CommandSection::Execute(ReportRequestSet* requests) const { +status_t CommandSection::Execute(ReportWriter* writer) const { FdBuffer buffer; Fpipe cmdPipe; Fpipe ihPipe; @@ -591,7 +402,7 @@ status_t CommandSection::Execute(ReportRequestSet* requests) const { cmdPipe.writeFd().reset(); status_t readStatus = buffer.read(ihPipe.readFd().get(), this->timeoutMs); - write_section_stats(requests->sectionStats(this->id), buffer); + writer->setSectionStats(buffer); if (readStatus != NO_ERROR || buffer.timedOut()) { ALOGW("[%s] failed to read data from incident helper: %s, timedout: %s", this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false"); @@ -605,18 +416,18 @@ status_t CommandSection::Execute(ReportRequestSet* requests) const { status_t cmdStatus = wait_child(cmdPid); status_t ihStatus = wait_child(ihPid); if (cmdStatus != NO_ERROR || ihStatus != NO_ERROR) { - ALOGW("[%s] abnormal child processes, return status: command: %s, incident " - "helper: %s", + ALOGW("[%s] abnormal child processes, return status: command: %s, incident helper: %s", this->name.string(), strerror(-cmdStatus), strerror(-ihStatus)); - return cmdStatus != NO_ERROR ? cmdStatus : ihStatus; + // Not a fatal error. + return NO_ERROR; } - return write_report_requests(this->id, buffer, requests); + return writer->writeSection(buffer); } // ================================================================================ -DumpsysSection::DumpsysSection(int id, bool userdebugAndEngOnly, const char* service, ...) - : WorkerThreadSection(id, REMOTE_CALL_TIMEOUT_MS, userdebugAndEngOnly), mService(service) { +DumpsysSection::DumpsysSection(int id, const char* service, ...) + : WorkerThreadSection(id, REMOTE_CALL_TIMEOUT_MS), mService(service) { name = "dumpsys "; name += service; @@ -842,7 +653,8 @@ status_t TombstoneSection::BlockingCall(int pipeWriteFd) const { const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid); std::string exe; if (!android::base::Readlink(link_name, &exe)) { - ALOGE("Can't read '%s': %s\n", link_name.c_str(), strerror(errno)); + ALOGE("Section %s: Can't read '%s': %s\n", name.string(), + link_name.c_str(), strerror(errno)); continue; } @@ -913,10 +725,10 @@ status_t TombstoneSection::BlockingCall(int pipeWriteFd) const { } auto dump = std::make_unique(buffer.size()); - auto iterator = buffer.data(); + sp reader = buffer.data()->read(); int i = 0; - while (iterator.hasNext()) { - dump[i] = iterator.next(); + while (reader->hasNext()) { + dump[i] = reader->next(); i++; } uint64_t token = proto.start(android::os::BackTraceProto::TRACES); diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h index 86d956ff75d8df06140b6464f02128be46979dd3..cfe7e1648ad80cf3e68585eb03b52baf9a6fd533 100644 --- a/cmds/incidentd/src/Section.h +++ b/cmds/incidentd/src/Section.h @@ -40,35 +40,12 @@ class Section { public: const int id; const int64_t timeoutMs; // each section must have a timeout - const bool userdebugAndEngOnly; String8 name; - Section(int id, int64_t timeoutMs = REMOTE_CALL_TIMEOUT_MS, bool userdebugAndEngOnly = false); + Section(int id, int64_t timeoutMs = REMOTE_CALL_TIMEOUT_MS); virtual ~Section(); - virtual status_t Execute(ReportRequestSet* requests) const = 0; -}; - -/** - * Section that generates incident headers. - */ -class HeaderSection : public Section { -public: - HeaderSection(); - virtual ~HeaderSection(); - - virtual status_t Execute(ReportRequestSet* requests) const; -}; - -/** - * Section that generates incident metadata. - */ -class MetadataSection : public Section { -public: - MetadataSection(); - virtual ~MetadataSection(); - - virtual status_t Execute(ReportRequestSet* requests) const; + virtual status_t Execute(ReportWriter* writer) const = 0; }; /** @@ -80,7 +57,7 @@ public: int64_t timeoutMs = 5000 /* 5 seconds */); virtual ~FileSection(); - virtual status_t Execute(ReportRequestSet* requests) const; + virtual status_t Execute(ReportWriter* writer) const; private: const char* mFilename; @@ -95,7 +72,7 @@ public: GZipSection(int id, const char* filename, ...); virtual ~GZipSection(); - virtual status_t Execute(ReportRequestSet* requests) const; + virtual status_t Execute(ReportWriter* writer) const; private: // It looks up the content from multiple files and stops when the first one is available. @@ -107,11 +84,10 @@ private: */ class WorkerThreadSection : public Section { public: - WorkerThreadSection(int id, int64_t timeoutMs = REMOTE_CALL_TIMEOUT_MS, - bool userdebugAndEngOnly = false); + WorkerThreadSection(int id, int64_t timeoutMs = REMOTE_CALL_TIMEOUT_MS); virtual ~WorkerThreadSection(); - virtual status_t Execute(ReportRequestSet* requests) const; + virtual status_t Execute(ReportWriter* writer) const; virtual status_t BlockingCall(int pipeWriteFd) const = 0; }; @@ -127,7 +103,7 @@ public: virtual ~CommandSection(); - virtual status_t Execute(ReportRequestSet* requests) const; + virtual status_t Execute(ReportWriter* writer) const; private: const char** mCommand; @@ -138,7 +114,7 @@ private: */ class DumpsysSection : public WorkerThreadSection { public: - DumpsysSection(int id, bool userdebugAndEngOnly, const char* service, ...); + DumpsysSection(int id, const char* service, ...); virtual ~DumpsysSection(); virtual status_t BlockingCall(int pipeWriteFd) const; @@ -148,6 +124,21 @@ private: Vector mArgs; }; +/** + * Section that calls dumpsys on a system service. + */ +class SystemPropertyDumpsysSection : public WorkerThreadSection { +public: + SystemPropertyDumpsysSection(int id, const char* service, ...); + virtual ~SystemPropertyDumpsysSection(); + + virtual status_t BlockingCall(int pipeWriteFd) const; + +private: + String16 mService; + Vector mArgs; +}; + /** * Section that reads from logd. */ diff --git a/cmds/incidentd/src/WorkDirectory.cpp b/cmds/incidentd/src/WorkDirectory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aa376ddee082877396328109af075507f28f3bda --- /dev/null +++ b/cmds/incidentd/src/WorkDirectory.cpp @@ -0,0 +1,844 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Log.h" + +#include "WorkDirectory.h" + +#include "PrivacyFilter.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace android { +namespace os { +namespace incidentd { + +using std::thread; +using google::protobuf::MessageLite; +using google::protobuf::RepeatedPtrField; +using google::protobuf::io::FileInputStream; +using google::protobuf::io::FileOutputStream; + +/** + * Turn off to skip removing files for debugging. + */ +static const bool DO_UNLINK = true; + +/** + * File extension for envelope files. + */ +static const string EXTENSION_ENVELOPE(".envelope"); + +/** + * File extension for data files. + */ +static const string EXTENSION_DATA(".data"); + +/** + * Send these reports to dropbox. + */ +const ComponentName DROPBOX_SENTINEL("android", "DROPBOX"); + +/** + * Read a protobuf from disk into the message. + */ +static status_t read_proto(MessageLite* msg, const string& filename) { + int fd = open(filename.c_str(), O_RDONLY | O_CLOEXEC); + if (fd < 0) { + return -errno; + } + + FileInputStream stream(fd); + stream.SetCloseOnDelete(fd); + + if (!msg->ParseFromZeroCopyStream(&stream)) { + return BAD_VALUE; + } + + return stream.GetErrno(); +} + +/** + * Write a protobuf to disk. + */ +static status_t write_proto(const MessageLite& msg, const string& filename) { + int fd = open(filename.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660); + if (fd < 0) { + return -errno; + } + + FileOutputStream stream(fd); + stream.SetCloseOnDelete(fd); + + if (!msg.SerializeToZeroCopyStream(&stream)) { + ALOGW("write_proto: error writing to %s", filename.c_str()); + return BAD_VALUE; + } + + return stream.GetErrno(); +} + +static string strip_extension(const string& filename) { + return filename.substr(0, filename.find('.')); +} + +static bool ends_with(const string& str, const string& ending) { + if (str.length() >= ending.length()) { + return str.compare(str.length()-ending.length(), ending.length(), ending) == 0; + } else { + return false; + } +} + +// Returns true if it was a valid timestamp. +static bool parse_timestamp_ns(const string& id, int64_t* result) { + char* endptr; + *result = strtoll(id.c_str(), &endptr, 10); + return id.length() != 0 && *endptr == '\0'; +} + +static bool has_section(const ReportFileProto_Report& report, int section) { + const size_t sectionCount = report.section_size(); + for (int i = 0; i < sectionCount; i++) { + if (report.section(i) == section) { + return true; + } + } + return false; +} + +status_t create_directory(const char* directory) { + struct stat st; + status_t err = NO_ERROR; + char* dir = strdup(directory); + + // Skip first slash + char* d = dir + 1; + + // Create directories, assigning them to the system user + bool last = false; + while (!last) { + d = strchr(d, '/'); + if (d != NULL) { + *d = '\0'; + } else { + last = true; + } + if (stat(dir, &st) == 0) { + if (!S_ISDIR(st.st_mode)) { + err = ALREADY_EXISTS; + goto done; + } + } else { + ALOGE("No such directory %s, something wrong.", dir); + err = -1; + goto done; + } + if (!last) { + *d++ = '/'; + } + } + + // Ensure that the final directory is owned by the system with 0770. If it isn't + // we won't write into it. + if (stat(directory, &st) != 0) { + ALOGE("No incident reports today. Can't stat: %s", directory); + err = -errno; + goto done; + } + if ((st.st_mode & 0777) != 0770) { + ALOGE("No incident reports today. Mode is %0o on report directory %s", st.st_mode, + directory); + err = BAD_VALUE; + goto done; + } + if (st.st_uid != AID_INCIDENTD || st.st_gid != AID_INCIDENTD) { + ALOGE("No incident reports today. Owner is %d and group is %d on report directory %s", + st.st_uid, st.st_gid, directory); + err = BAD_VALUE; + goto done; + } + +done: + free(dir); + return err; +} + +void log_envelope(const ReportFileProto& envelope) { + ALOGD("Envelope: {"); + for (int i=0; i& workDirectory, int64_t timestampNs, + const string& envelopeFileName, const string& dataFileName) + :mWorkDirectory(workDirectory), + mTimestampNs(timestampNs), + mEnvelopeFileName(envelopeFileName), + mDataFileName(dataFileName), + mEnvelope(), + mDataFd(-1), + mError(NO_ERROR) { + // might get overwritten when we read but that's ok + mEnvelope.set_data_file(mDataFileName); +} + +ReportFile::~ReportFile() { + if (mDataFd >= 0) { + close(mDataFd); + } +} + +int64_t ReportFile::getTimestampNs() const { + return mTimestampNs; +} + +void ReportFile::addReport(const IncidentReportArgs& args) { + // There is only one report per component. Merge into an existing one if necessary. + ReportFileProto_Report* report; + const int reportCount = mEnvelope.report_size(); + int i = 0; + for (; i < reportCount; i++) { + report = mEnvelope.mutable_report(i); + if (report->pkg() == args.receiverPkg() && report->cls() == args.receiverCls()) { + if (args.getPrivacyPolicy() < report->privacy_policy()) { + // Lower privacy policy (less restrictive) wins. + report->set_privacy_policy(args.getPrivacyPolicy()); + } + report->set_all_sections(report->all_sections() | args.all()); + for (int section: args.sections()) { + if (!has_section(*report, section)) { + report->add_section(section); + } + } + break; + } + } + if (i >= reportCount) { + report = mEnvelope.add_report(); + report->set_pkg(args.receiverPkg()); + report->set_cls(args.receiverCls()); + report->set_privacy_policy(args.getPrivacyPolicy()); + report->set_all_sections(args.all()); + for (int section: args.sections()) { + report->add_section(section); + } + } + + for (const vector& header: args.headers()) { + report->add_header(header.data(), header.size()); + } +} + +void ReportFile::removeReport(const string& pkg, const string& cls) { + RepeatedPtrField* reports = mEnvelope.mutable_report(); + const int reportCount = reports->size(); + for (int i = 0; i < reportCount; i++) { + const ReportFileProto_Report& r = reports->Get(i); + if (r.pkg() == pkg && r.cls() == cls) { + reports->DeleteSubrange(i, 1); + return; + } + } +} + +void ReportFile::removeReports(const string& pkg) { + RepeatedPtrField* reports = mEnvelope.mutable_report(); + const int reportCount = reports->size(); + for (int i = reportCount-1; i >= 0; i--) { + const ReportFileProto_Report& r = reports->Get(i); + if (r.pkg() == pkg) { + reports->DeleteSubrange(i, 1); + } + } +} + +void ReportFile::setMetadata(const IncidentMetadata& metadata) { + *mEnvelope.mutable_metadata() = metadata; +} + +void ReportFile::markCompleted() { + mEnvelope.set_completed(true); +} + +status_t ReportFile::markApproved(const string& pkg, const string& cls) { + size_t const reportCount = mEnvelope.report_size(); + for (int reportIndex = 0; reportIndex < reportCount; reportIndex++) { + ReportFileProto_Report* report = mEnvelope.mutable_report(reportIndex); + if (report->pkg() == pkg && report->cls() == cls) { + report->set_share_approved(true); + return NO_ERROR; + } + } + return NAME_NOT_FOUND; +} + +void ReportFile::setMaxPersistedPrivacyPolicy(int persistedPrivacyPolicy) { + mEnvelope.set_privacy_policy(persistedPrivacyPolicy); +} + +status_t ReportFile::saveEnvelope() { + return save_envelope_impl(true); +} + +status_t ReportFile::trySaveEnvelope() { + return save_envelope_impl(false); +} + +status_t ReportFile::loadEnvelope() { + return load_envelope_impl(true); +} + +status_t ReportFile::tryLoadEnvelope() { + return load_envelope_impl(false); +} + +const ReportFileProto& ReportFile::getEnvelope() { + return mEnvelope; +} + +status_t ReportFile::startWritingDataFile() { + if (mDataFd >= 0) { + ALOGW("ReportFile::startWritingDataFile called with the file already open: %s", + mDataFileName.c_str()); + return ALREADY_EXISTS; + } + mDataFd = open(mDataFileName.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660); + if (mDataFd < 0) { + return -errno; + } + return NO_ERROR; +} + +void ReportFile::closeDataFile() { + if (mDataFd >= 0) { + mEnvelope.set_data_file_size(lseek(mDataFd, 0, SEEK_END)); + close(mDataFd); + mDataFd = -1; + } +} + +status_t ReportFile::startFilteringData(int* fd, const IncidentReportArgs& args) { + // Open data file. + int dataFd = open(mDataFileName.c_str(), O_RDONLY | O_CLOEXEC); + if (dataFd < 0) { + return -errno; + } + + // Check that the size on disk is what we thought we wrote. + struct stat st; + if (fstat(dataFd, &st) != 0) { + return -errno; + } + if (st.st_size != mEnvelope.data_file_size()) { + ALOGW("File size mismatch. Envelope says %" PRIi64 " bytes but data file is %" PRIi64 + " bytes: %s", (int64_t)mEnvelope.data_file_size(), st.st_size, + mDataFileName.c_str()); + ALOGW("Removing incident report"); + mWorkDirectory->remove(this); + return BAD_VALUE; + } + + // Create pipe + int fds[2]; + if (pipe(fds) != 0) { + ALOGW("Error opening pipe to filter incident report: %s", getDataFileName().c_str()); + return -errno; + } + + *fd = fds[0]; + int writeFd = fds[1]; + + // Spawn off a thread to do the filtering and writing + thread th([this, dataFd, writeFd, args]() { + ALOGD("worker thread started dataFd=%d writeFd=%d", dataFd, writeFd); + status_t err; + + err = filter_and_write_report(writeFd, dataFd, mEnvelope.privacy_policy(), args); + close(writeFd); + + if (err != NO_ERROR) { + ALOGW("Error writing incident report '%s' to dropbox: %s", getDataFileName().c_str(), + strerror(-err)); + // If there's an error here, there will also be an error returned from + // addFile, so we'll use that error to reschedule the send_to_dropbox. + // If the file is corrupted, we will put some logs in logcat, but won't + // actually return an error. + return; + } + }); + + // Better would be to join this thread after write is back, but there is no + // timeout parameter for that, which means we can't clean up if system server + // is stuck. Better is to leak the thread, which will eventually clean itself + // up after system server eventually dies, which it probably will. + th.detach(); + + // If the thread fails to start, we should return an error, but the thread + // class doesn't give us a good way to determine that. Just pretend everything + // is ok. + return NO_ERROR; +} + +string ReportFile::getDataFileName() const { + return mDataFileName; +} + +string ReportFile::getEnvelopeFileName() const { + return mEnvelopeFileName; +} + +int ReportFile::getDataFileFd() { + return mDataFd; +} + +void ReportFile::setWriteError(status_t err) { + mError = err; +} + +status_t ReportFile::getWriteError() { + return mError; +} + +string ReportFile::getId() { + return to_string(mTimestampNs); +} + +status_t ReportFile::save_envelope_impl(bool cleanup) { + status_t err; + err = write_proto(mEnvelope, mEnvelopeFileName); + if (err != NO_ERROR) { + // If there was an error writing the envelope, then delete the whole thing. + if (cleanup) { + mWorkDirectory->remove(this); + } + return err; + } + return NO_ERROR; +} + +status_t ReportFile::load_envelope_impl(bool cleanup) { + status_t err; + err = read_proto(&mEnvelope, mEnvelopeFileName); + if (err != NO_ERROR) { + // If there was an error reading the envelope, then delete the whole thing. + if (cleanup) { + mWorkDirectory->remove(this); + } + return err; + } + return NO_ERROR; +} + + + +// ================================================================================ +// + +WorkDirectory::WorkDirectory() + :mDirectory("/data/misc/incidents"), + mMaxFileCount(100), + mMaxDiskUsageBytes(30 * 1024 * 1024) { // Incident reports can take up to 30MB on disk. + // TODO: Should be a flag. + create_directory(mDirectory.c_str()); +} + +WorkDirectory::WorkDirectory(const string& dir, int maxFileCount, long maxDiskUsageBytes) + :mDirectory(dir), + mMaxFileCount(maxFileCount), + mMaxDiskUsageBytes(maxDiskUsageBytes) { + create_directory(mDirectory.c_str()); +} + +sp WorkDirectory::createReportFile() { + unique_lock lock(mLock); + status_t err; + + clean_directory_locked(); + + int64_t timestampNs = make_timestamp_ns_locked(); + string envelopeFileName = make_filename(timestampNs, EXTENSION_ENVELOPE); + string dataFileName = make_filename(timestampNs, EXTENSION_DATA); + + sp result = new ReportFile(this, timestampNs, envelopeFileName, dataFileName); + + err = result->trySaveEnvelope(); + if (err != NO_ERROR) { + ALOGW("Can't save envelope file %s: %s", strerror(-errno), envelopeFileName.c_str()); + return nullptr; + } + + return result; +} + +status_t WorkDirectory::getReports(vector>* result, int64_t after) { + unique_lock lock(mLock); + + const bool DBG = true; + + if (DBG) { + ALOGD("WorkDirectory::getReports"); + } + + map files; + get_directory_contents_locked(&files, after); + for (map::iterator it = files.begin(); + it != files.end(); it++) { + sp reportFile = new ReportFile(this, it->second.timestampNs, + it->second.envelope, it->second.data); + if (DBG) { + ALOGD(" %s", reportFile->getId().c_str()); + } + result->push_back(reportFile); + } + return NO_ERROR; +} + +sp WorkDirectory::getReport(const string& pkg, const string& cls, const string& id, + IncidentReportArgs* args) { + unique_lock lock(mLock); + + status_t err; + int64_t timestampNs; + if (!parse_timestamp_ns(id, ×tampNs)) { + return nullptr; + } + + // Make the ReportFile object, and then see if it's valid and for pkg and cls. + sp result = new ReportFile(this, timestampNs, + make_filename(timestampNs, EXTENSION_ENVELOPE), + make_filename(timestampNs, EXTENSION_DATA)); + + err = result->tryLoadEnvelope(); + if (err != NO_ERROR) { + ALOGW("Can't open envelope file for report %s/%s %s", pkg.c_str(), cls.c_str(), id.c_str()); + return nullptr; + } + + const ReportFileProto& envelope = result->getEnvelope(); + const size_t reportCount = envelope.report_size(); + for (int i = 0; i < reportCount; i++) { + const ReportFileProto_Report& report = envelope.report(i); + if (report.pkg() == pkg && report.cls() == cls) { + if (args != nullptr) { + get_args_from_report(args, report); + } + return result; + } + + } + + return nullptr; +} + +bool WorkDirectory::hasMore(int64_t after) { + unique_lock lock(mLock); + + map files; + get_directory_contents_locked(&files, after); + return files.size() > 0; +} + +void WorkDirectory::commit(const sp& report, const string& pkg, const string& cls) { + status_t err; + ALOGI("Committing report %s for %s/%s", report->getId().c_str(), pkg.c_str(), cls.c_str()); + + unique_lock lock(mLock); + + // Load the envelope here inside the lock. + err = report->loadEnvelope(); + + report->removeReport(pkg, cls); + + delete_files_for_report_if_necessary(report); +} + +void WorkDirectory::commitAll(const string& pkg) { + status_t err; + ALOGI("All reports for %s", pkg.c_str()); + + unique_lock lock(mLock); + + map files; + get_directory_contents_locked(&files, 0); + + for (map::iterator it = files.begin(); + it != files.end(); it++) { + sp reportFile = new ReportFile(this, it->second.timestampNs, + it->second.envelope, it->second.data); + + err = reportFile->loadEnvelope(); + if (err != NO_ERROR) { + continue; + } + + reportFile->removeReports(pkg); + + delete_files_for_report_if_necessary(reportFile); + } +} + +void WorkDirectory::remove(const sp& report) { + unique_lock lock(mLock); + // Set this to false to leave files around for debugging. + if (DO_UNLINK) { + unlink(report->getDataFileName().c_str()); + unlink(report->getEnvelopeFileName().c_str()); + } +} + +int64_t WorkDirectory::make_timestamp_ns_locked() { + // Guarantee that we don't have duplicate timestamps. + // This is a little bit lame, but since reports are created on the + // same thread and are kinda slow we'll seldomly actually hit the + // condition. The bigger risk is the clock getting reset and causing + // a collision. In that case, we'll just make incident reporting a + // little bit slower. Nobody will notice if we just loop until we + // have a unique file name. + int64_t timestampNs = 0; + do { + struct timespec spec; + if (timestampNs > 0) { + spec.tv_sec = 0; + spec.tv_nsec = 1; + nanosleep(&spec, nullptr); + } + clock_gettime(CLOCK_REALTIME, &spec); + timestampNs = (spec.tv_sec) * 1000 + spec.tv_nsec; + } while (file_exists_locked(timestampNs)); + return timestampNs; +} + +/** + * It is required to hold the lock here so in case someone else adds it + * our result is still correct for the caller. + */ +bool WorkDirectory::file_exists_locked(int64_t timestampNs) { + const string filename = make_filename(timestampNs, EXTENSION_ENVELOPE); + struct stat st; + return stat(filename.c_str(), &st) == 0; +} + +string WorkDirectory::make_filename(int64_t timestampNs, const string& extension) { + // Zero pad the timestamp so it can also be alpha sorted. + stringstream result; + result << mDirectory << '/' << setfill('0') << setw(20) << timestampNs << extension; + return result.str(); +} + +off_t WorkDirectory::get_directory_contents_locked(map* files, + int64_t after) { + DIR* dir; + struct dirent* entry; + + if ((dir = opendir(mDirectory.c_str())) == NULL) { + ALOGE("Couldn't open incident directory: %s", mDirectory.c_str()); + return -1; + } + + string dirbase(mDirectory); + if (mDirectory[dirbase.size() - 1] != '/') dirbase += "/"; + + off_t totalSize = 0; + + // Enumerate, count and add up size + while ((entry = readdir(dir)) != NULL) { + if (entry->d_name[0] == '.') { + continue; + } + string entryname = entry->d_name; // local to this dir + string filename = dirbase + entryname; // fully qualified + + bool isEnvelope = ends_with(entryname, EXTENSION_ENVELOPE); + bool isData = ends_with(entryname, EXTENSION_DATA); + + // If the file isn't one of our files, just ignore it. Otherwise, + // sum up the sizes. + if (isEnvelope || isData) { + string timestamp = strip_extension(entryname); + + int64_t timestampNs; + if (!parse_timestamp_ns(timestamp, ×tampNs)) { + continue; + } + + if (after == 0 || timestampNs > after) { + struct stat st; + if (stat(filename.c_str(), &st) != 0) { + ALOGE("Unable to stat file %s", filename.c_str()); + continue; + } + if (!S_ISREG(st.st_mode)) { + continue; + } + + WorkDirectoryEntry& entry = (*files)[timestamp]; + if (isEnvelope) { + entry.envelope = filename; + } else if (isData) { + entry.data = filename; + } + entry.timestampNs = timestampNs; + entry.size += st.st_size; + totalSize += st.st_size; + } + } + } + + closedir(dir); + + // Now check if there are any data files that don't have envelope files. + // If there are, then just go ahead and delete them now. Don't wait for + // a cleaning. + + if (DO_UNLINK) { + map::iterator it = files->begin(); + while (it != files->end()) { + if (it->second.envelope.length() == 0) { + unlink(it->second.data.c_str()); + it = files->erase(it); + } else { + it++; + } + } + } + + return totalSize; +} + +void WorkDirectory::clean_directory_locked() { + DIR* dir; + struct dirent* entry; + struct stat st; + + // Map of filename without extension to the entries about it. Conveniently, + // this also keeps the list sorted by filename, which is a timestamp. + map files; + off_t totalSize = get_directory_contents_locked(&files, 0); + if (totalSize < 0) { + return; + } + int totalCount = files.size(); + + // Count or size is less than max, then we're done. + if (totalSize < mMaxDiskUsageBytes && totalCount < mMaxFileCount) { + return; + } + + // Remove files until we're under our limits. + if (DO_UNLINK) { + for (map::const_iterator it = files.begin(); + it != files.end() && (totalSize >= mMaxDiskUsageBytes + || totalCount >= mMaxFileCount); + it++) { + unlink(it->second.envelope.c_str()); + unlink(it->second.data.c_str()); + totalSize -= it->second.size; + totalCount--; + } + } +} + +void WorkDirectory::delete_files_for_report_if_necessary(const sp& report) { + if (report->getEnvelope().report_size() == 0) { + ALOGI("Report %s is finished. Deleting from storage.", report->getId().c_str()); + if (DO_UNLINK) { + unlink(report->getDataFileName().c_str()); + unlink(report->getEnvelopeFileName().c_str()); + } + } +} + +// ================================================================================ +void get_args_from_report(IncidentReportArgs* out, const ReportFileProto_Report& report) { + out->setPrivacyPolicy(report.privacy_policy()); + out->setAll(report.all_sections()); + out->setReceiverPkg(report.pkg()); + out->setReceiverCls(report.cls()); + + const int sectionCount = report.section_size(); + for (int i = 0; i < sectionCount; i++) { + out->addSection(report.section(i)); + } + + const int headerCount = report.header_size(); + for (int i = 0; i < headerCount; i++) { + const string& header = report.header(i); + vector vec(header.begin(), header.end()); + out->addHeader(vec); + } +} + + +} // namespace incidentd +} // namespace os +} // namespace android + diff --git a/cmds/incidentd/src/WorkDirectory.h b/cmds/incidentd/src/WorkDirectory.h new file mode 100644 index 0000000000000000000000000000000000000000..e344371c36824de5df93f57ea398501947ee7301 --- /dev/null +++ b/cmds/incidentd/src/WorkDirectory.h @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +#include + +#include +#include + +namespace android { +namespace os { +namespace incidentd { + +using android::content::ComponentName; +using android::os::IncidentReportArgs; +using namespace std; + +extern const ComponentName DROPBOX_SENTINEL; + +class WorkDirectory; +struct WorkDirectoryEntry; + +void get_args_from_report(IncidentReportArgs* out, const ReportFileProto_Report& report); + +/** + * A ReportFile object is backed by two files. + * - A metadata file, which contains a + */ +class ReportFile : public virtual RefBase { +public: + ReportFile(const sp& workDirectory, int64_t timestampNs, + const string& envelopeFileName, const string& dataFileName); + + virtual ~ReportFile(); + + /** + * Get the timestamp from when this file was added. + */ + int64_t getTimestampNs() const; + + /** + * Add an additional report to this ReportFile. + */ + void addReport(const IncidentReportArgs& args); + + /** + * Remove the reports for pkg/cls from this file. + */ + void removeReport(const string& pkg, const string& cls); + + /** + * Remove all reports for pkg from this file. + */ + void removeReports(const string& pkg); + + /** + * Set the metadata for this incident report. + */ + void setMetadata(const IncidentMetadata& metadata); + + /* + * Mark this incident report as finished and ready for broadcast. + */ + void markCompleted(); + + /* + * Mark this incident report as finished and ready for broadcast. + */ + status_t markApproved(const string& pkg, const string& cls); + + /** + * Set the privacy policy that is being used to pre-filter the data + * going to disk. + */ + void setMaxPersistedPrivacyPolicy(int persistedPrivacyPolicy); + + /** + * Save the metadata (envelope) information about the incident + * report. Must be called after addReport, setMetadata markCompleted + * markApproved to save those changes to disk. + */ + status_t saveEnvelope(); + + /** + * Like saveEnvelope() but will not clean up if there is an error. + */ + status_t trySaveEnvelope(); + + /** + * Read the envelope information from disk. If there was an error, the envelope and + * data file will be removed. If the proto can't be loaded, the whole file is deleted. + */ + status_t loadEnvelope(); + + /** + * Like loadEnvelope() but will not clean up if there is an error. + */ + status_t tryLoadEnvelope(); + + /** + * Get the envelope information. + */ + const ReportFileProto& getEnvelope(); + + /** + * Open the file that will contain the contents of the incident report. Call + * close() or closeDataFile() on the result of getDataFileFd() when you're done. + * This is not done automatically in the desctructor. If there is an error, returns + * it and you will not get an fd. + */ + status_t startWritingDataFile(); + + /** + * Close the data file. + */ + void closeDataFile(); + + /** + * Spawn a thread to start writing and filtering data to a pipe, the read end of which + * will be returned in fd. This thread will be detached and run until somebody finishes + * reading from the fd or closes it. If there is an error, returns it and you will not + * get an fd. + * + * Use the privacy and section configuraiton from the args parameter. + */ + status_t startFilteringData(int* fd, const IncidentReportArgs& args); + + /** + * Get the name of the data file on disk. + */ + string getDataFileName() const; + + /** + * Get the name of the envelope file on disk. + */ + string getEnvelopeFileName() const; + + /** + * Return the file descriptor for the data file, or -1 if it is not + * currently open. + */ + int getDataFileFd(); + + /** + * Record that there was an error writing to the data file. + */ + void setWriteError(status_t err); + + /** + * Get whether there was previously an error writing to the data file. + */ + status_t getWriteError(); + + /** + * Get the unique identifier for this file. + */ + string getId(); + +private: + sp mWorkDirectory; + int64_t mTimestampNs; + string mEnvelopeFileName; + string mDataFileName; + ReportFileProto mEnvelope; + int mDataFd; + status_t mError; + + status_t save_envelope_impl(bool cleanup); + status_t load_envelope_impl(bool cleanup); +}; + +/** + * For directory cleanup to work, WorkDirectory must be kept + * alive for the duration of all of the ReportFiles. In the real + * incidentd, WorkDirectory is a singleton. In tests, it may + * have a shorter duration. + */ +class WorkDirectory : public virtual RefBase { +public: + /** + * Save files to the default location. + */ + WorkDirectory(); + + /** + * Save files to a specific location (primarily for testing). + */ + WorkDirectory(const string& dir, int maxFileCount, long maxDiskUsageBytes); + + /** + * Return a new report file. Creating this object won't fail, but + * subsequent actions on the file could, if the disk is full, permissions + * aren't set correctly, etc. + */ + sp createReportFile(); + + /** + * Get the reports that are saved on-disk, with the time after (>) than the + * given timestamp. Pass 0 to start at the beginning. These files + * will be sorted by timestamp. The envelope will not have been loaded. + */ + status_t getReports(vector>* files, int64_t after); + + /** + * Get the report with the given package, class and id. Returns nullptr if + * that can't be found. The envelope will have been loaded. Returns the + * original IncidentReportArgs in *args if args != nullptr. + */ + sp getReport(const string& pkg, const string& cls, const string& id, + IncidentReportArgs* args); + + /** + * Returns whether there are more reports after the given timestamp. + */ + bool hasMore(int64_t after); + + /** + * Confirm that a particular broadcast receiver has received the data. When all + * broadcast receivers for a particular report file have finished, the envelope + * and data files will be deleted. + */ + void commit(const sp& report, const string& pkg, const string& cls); + + /** + * Commit all reports the given package. + */ + void commitAll(const string& pkg); + + /** + * Remove the envelope and data file from disk, regardless of whether there are + * more pending readers or broadcasts, for example in response to an error. + */ + void remove(const sp& report); + +private: + string mDirectory; + int mMaxFileCount; + long mMaxDiskUsageBytes; + + // Held while creating or removing envelope files, which are the file that keeps + // the directory consistent. + mutex mLock; + + int64_t make_timestamp_ns_locked(); + bool file_exists_locked(int64_t timestampNs); + off_t get_directory_contents_locked(map* files, int64_t after); + void clean_directory_locked(); + void delete_files_for_report_if_necessary(const sp& report); + + string make_filename(int64_t timestampNs, const string& extension); +}; + + +} // namespace incidentd +} // namespace os +} // namespace android + diff --git a/cmds/incidentd/src/incidentd_util.cpp b/cmds/incidentd/src/incidentd_util.cpp index af685d8adeb889e404b403e4e66dd65aa6377ad8..dfaf89392f90e9d9f0a9313f40dd507c3418f80c 100644 --- a/cmds/incidentd/src/incidentd_util.cpp +++ b/cmds/incidentd/src/incidentd_util.cpp @@ -68,7 +68,6 @@ pid_t fork_execute_cmd(char* const argv[], Fpipe* input, Fpipe* output) { // fork used in multithreaded environment, avoid adding unnecessary code in child process pid_t pid = fork(); if (pid == 0) { - VLOG("[In child]cmd %s", argv[0]); if (input != NULL && (TEMP_FAILURE_RETRY(dup2(input->readFd().get(), STDIN_FILENO)) < 0 || !input->close())) { ALOGW("Failed to dup2 stdin."); @@ -158,4 +157,4 @@ status_t wait_child(pid_t pid) { } // namespace incidentd } // namespace os -} // namespace android \ No newline at end of file +} // namespace android diff --git a/cmds/incidentd/src/incidentd_util.h b/cmds/incidentd/src/incidentd_util.h index 3dac2c4c3759d185579c4b6f2fc3f09b32ff71ce..cc30768fa704009e673b07bd18ff55ea2efae195 100644 --- a/cmds/incidentd/src/incidentd_util.h +++ b/cmds/incidentd/src/incidentd_util.h @@ -78,6 +78,8 @@ uint64_t Nanotime(); status_t kill_child(pid_t pid); status_t wait_child(pid_t pid); +status_t start_detached_thread(const function& func); + } // namespace incidentd } // namespace os } // namespace android diff --git a/cmds/incidentd/src/proto_util.cpp b/cmds/incidentd/src/proto_util.cpp new file mode 100644 index 0000000000000000000000000000000000000000..be2f24f97d0218944b29a9f2ee187652d73af7f9 --- /dev/null +++ b/cmds/incidentd/src/proto_util.cpp @@ -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. + */ + +#include "proto_util.h" + +#include + +#include +#include +#include + +namespace android { +namespace os { +namespace incidentd { + +using namespace android::base; +using namespace android::util; +using google::protobuf::io::FileOutputStream; + +// special section ids +const int FIELD_ID_INCIDENT_HEADER = 1; + +status_t write_header_section(int fd, const vector& buf) { + status_t err; + const size_t bufSize = buf.size(); + + if (buf.empty()) { + return NO_ERROR; + } + + err = write_section_header(fd, FIELD_ID_INCIDENT_HEADER, bufSize); + if (err != NO_ERROR) { + return err; + } + + err = WriteFully(fd, (uint8_t const*)buf.data(), bufSize); + if (err != NO_ERROR) { + return err; + } + + return NO_ERROR; +} + +status_t write_section_header(int fd, int sectionId, size_t size) { + uint8_t buf[20]; + uint8_t* p = write_length_delimited_tag_header(buf, sectionId, size); + return WriteFully(fd, buf, p - buf) ? NO_ERROR : -errno; +} + +status_t write_section(int fd, int sectionId, const MessageLite& message) { + status_t err; + + err = write_section_header(fd, sectionId, message.ByteSize()); + if (err != NO_ERROR) { + return err; + } + + FileOutputStream stream(fd); + if (!message.SerializeToZeroCopyStream(&stream)) { + return stream.GetErrno(); + } else { + return NO_ERROR; + } +} + + + +} // namespace incidentd +} // namespace os +} // namespace android + diff --git a/cmds/statsd/src/logd/LogListener.h b/cmds/incidentd/src/proto_util.h similarity index 53% rename from cmds/statsd/src/logd/LogListener.h rename to cmds/incidentd/src/proto_util.h index d8b06e9fab92436a47d3f60f6ee0a23100d64f73..b9df6cbf296d2343186d12af4233699f46fa8197 100644 --- a/cmds/statsd/src/logd/LogListener.h +++ b/cmds/incidentd/src/proto_util.h @@ -16,25 +16,37 @@ #pragma once -#include "logd/LogEvent.h" +#include -#include +#include + +#include namespace android { namespace os { -namespace statsd { +namespace incidentd { + +using std::vector; +using google::protobuf::MessageLite; /** - * Callback for LogReader + * Write the IncidentHeaderProto section */ -class LogListener : public virtual android::RefBase { -public: - LogListener(); - virtual ~LogListener(); +status_t write_header_section(int fd, const vector& buf); - virtual void OnLogEvent(LogEvent* msg) = 0; -}; +/** + * Write the prologue for a section in the incident report + * (This is the proto length-prefixed field format). + */ +status_t write_section_header(int fd, int sectionId, size_t size); -} // namespace statsd +/** + * Write the given protobuf object as a section. + */ +status_t write_section(int fd, int sectionId, const MessageLite& message); + +} // namespace incidentd } // namespace os } // namespace android + + diff --git a/cmds/incidentd/src/report_directory.cpp b/cmds/incidentd/src/report_directory.cpp index e2883ba0450801a6c74d30ec4226c406fa6a318c..7d20a74437ce8bb3d752105c7bd0b900f4ba0166 100644 --- a/cmds/incidentd/src/report_directory.cpp +++ b/cmds/incidentd/src/report_directory.cpp @@ -33,63 +33,6 @@ namespace android { namespace os { namespace incidentd { -status_t create_directory(const char* directory) { - struct stat st; - status_t err = NO_ERROR; - char* dir = strdup(directory); - - // Skip first slash - char* d = dir + 1; - - // Create directories, assigning them to the system user - bool last = false; - while (!last) { - d = strchr(d, '/'); - if (d != NULL) { - *d = '\0'; - } else { - last = true; - } - if (stat(dir, &st) == 0) { - if (!S_ISDIR(st.st_mode)) { - err = ALREADY_EXISTS; - goto done; - } - } else { - ALOGE("No such directory %s, something wrong.", dir); - err = -1; - goto done; - } - if (!last) { - *d++ = '/'; - } - } - - // Ensure that the final directory is owned by the system with 0770. If it isn't - // we won't write into it. - if (stat(directory, &st) != 0) { - ALOGE("No incident reports today. Can't stat: %s", directory); - err = -errno; - goto done; - } - if ((st.st_mode & 0777) != 0770) { - ALOGE("No incident reports today. Mode is %0o on report directory %s", st.st_mode, - directory); - err = BAD_VALUE; - goto done; - } - if (st.st_uid != AID_INCIDENTD || st.st_gid != AID_INCIDENTD) { - ALOGE("No incident reports today. Owner is %d and group is %d on report directory %s", - st.st_uid, st.st_gid, directory); - err = BAD_VALUE; - goto done; - } - -done: - free(dir); - return err; -} - static bool stat_mtime_cmp(const std::pair& a, const std::pair& b) { return a.second.st_mtime < b.second.st_mtime; @@ -153,4 +96,4 @@ void clean_directory(const char* directory, off_t maxSize, size_t maxCount) { } // namespace incidentd } // namespace os -} // namespace android \ No newline at end of file +} // namespace android diff --git a/cmds/incidentd/src/report_file.proto b/cmds/incidentd/src/report_file.proto new file mode 100644 index 0000000000000000000000000000000000000000..7563da2c2148106c6cae4981d11a410eed9caa43 --- /dev/null +++ b/cmds/incidentd/src/report_file.proto @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package android.os.incidentd; + +import "frameworks/base/core/proto/android/os/metadata.proto"; + +message ReportFileProto { + /** + * Metadata about each of the calls to reportIncident that + * initiated the incident report. + */ + message Report { + /** + * Package name for broadcast receiver to be told when + * the report is complete. + */ + optional string pkg = 1; + + /** + * Class name for broadcast receiver to be told when + * the report is complete. + */ + optional string cls = 2; + + /** + * Privacy policy at which this report should be shared. + */ + optional uint32 privacy_policy = 4; + + /** + * Whether all available sections should be returned. + */ + optional bool all_sections = 5; + + /** + * If all_sections is not true, then this is the + * list of sections that were requested. + */ + repeated int32 section = 6; + + /** + * Flattened IncidentHeaderProto that was passed with this + * request. + */ + repeated bytes header = 7; + + /** + * Whether the user has approved this report to be shared with + * the given client. + */ + optional bool share_approved = 8; + } + + /** + * Metadata section recorded while the incident report + * was taken. + */ + optional android.os.IncidentMetadata metadata = 1; + + /** + * Report data structures for the incident reports. + */ + repeated Report report = 2; + + /** + * The file name, relative to the work directory where + * the data file is stored. The content of the data file + * is an android.os.IncidentProto, without the metadata + * or header sections. + */ + optional string data_file = 3; + + /** + * The privacy policy to which the file is already filtered. + */ + optional uint32 privacy_policy = 4; + + /** + * How big the data file is expected to be. If the size + * recorded here and the size on disk mismatch, then we + * know there was an error. + */ + optional int64 data_file_size = 5; + + /** + * Whether this report has been finished, and is now + * ready for broadcast / dropbox / etc. + */ + optional bool completed = 6; +} + diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp index 3f92c2a183287840ab4d48d2d3ffe87b4dd9f546..7edfadf5f90db9fc93d29d443344405ae3de1971 100644 --- a/cmds/incidentd/tests/FdBuffer_test.cpp +++ b/cmds/incidentd/tests/FdBuffer_test.cpp @@ -50,9 +50,9 @@ public: void AssertBufferContent(const char* expected) { int i = 0; - EncodedBuffer::iterator it = buffer.data(); - while (it.hasNext()) { - ASSERT_EQ(it.next(), expected[i++]); + sp reader = buffer.data()->read(); + while (reader->hasNext()) { + ASSERT_EQ(reader->next(), expected[i++]); } EXPECT_EQ(expected[i], '\0'); } @@ -92,8 +92,8 @@ TEST_F(FdBufferTest, ReadAndWrite) { } TEST_F(FdBufferTest, IterateEmpty) { - EncodedBuffer::iterator it = buffer.data(); - EXPECT_FALSE(it.hasNext()); + sp reader = buffer.data()->read(); + EXPECT_FALSE(reader->hasNext()); } TEST_F(FdBufferTest, ReadAndIterate) { @@ -102,15 +102,23 @@ TEST_F(FdBufferTest, ReadAndIterate) { ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT)); int i = 0; - EncodedBuffer::iterator it = buffer.data(); - while (it.hasNext()) { - EXPECT_EQ(it.next(), (uint8_t)testdata[i++]); + sp reader = buffer.data()->read(); + + while (reader->hasNext()) { + EXPECT_EQ(reader->next(), (uint8_t)testdata[i++]); } +} + +TEST_F(FdBufferTest, Move) { + std::string testdata = "FdBuffer test string"; + ASSERT_TRUE(WriteStringToFile(testdata, tf.path)); + ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT)); + + sp reader = buffer.data()->read(); + reader->move(buffer.size()); - it.rp()->rewind(); - it.rp()->move(buffer.size()); - EXPECT_EQ(it.bytesRead(), testdata.size()); - EXPECT_FALSE(it.hasNext()); + EXPECT_EQ(reader->bytesRead(), testdata.size()); + EXPECT_FALSE(reader->hasNext()); } TEST_F(FdBufferTest, ReadTimeout) { @@ -224,7 +232,7 @@ TEST_F(FdBufferTest, ReadInStreamEmpty) { } } -TEST_F(FdBufferTest, ReadInStreamMoreThan4MB) { +TEST_F(FdBufferTest, ReadInStreamMoreThan4MBWithMove) { const std::string testFile = kTestDataPath + "morethan4MB.txt"; size_t fourMB = (size_t)4 * 1024 * 1024; unique_fd fd(open(testFile.c_str(), O_RDONLY | O_CLOEXEC)); @@ -250,15 +258,45 @@ TEST_F(FdBufferTest, ReadInStreamMoreThan4MB) { EXPECT_FALSE(buffer.timedOut()); EXPECT_TRUE(buffer.truncated()); wait(&pid); - EncodedBuffer::iterator it = buffer.data(); - it.rp()->move(fourMB); - EXPECT_EQ(it.bytesRead(), fourMB); - EXPECT_FALSE(it.hasNext()); - - it.rp()->rewind(); - while (it.hasNext()) { - char c = 'A' + (it.bytesRead() % 64 / 8); - ASSERT_TRUE(it.next() == c); + sp reader = buffer.data()->read(); + reader->move(fourMB); + + EXPECT_EQ(reader->bytesRead(), fourMB); + EXPECT_FALSE(reader->hasNext()); + } +} + +TEST_F(FdBufferTest, ReadInStreamMoreThan4MBWithNext) { + const std::string testFile = kTestDataPath + "morethan4MB.txt"; + size_t fourMB = (size_t)4 * 1024 * 1024; + unique_fd fd(open(testFile.c_str(), O_RDONLY | O_CLOEXEC)); + ASSERT_NE(fd.get(), -1); + int pid = fork(); + ASSERT_TRUE(pid != -1); + + if (pid == 0) { + p2cPipe.writeFd().reset(); + c2pPipe.readFd().reset(); + ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd())); + p2cPipe.readFd().reset(); + c2pPipe.writeFd().reset(); + _exit(EXIT_SUCCESS); + } else { + p2cPipe.readFd().reset(); + c2pPipe.writeFd().reset(); + + ASSERT_EQ(NO_ERROR, + buffer.readProcessedDataInStream(fd, std::move(p2cPipe.writeFd()), + std::move(c2pPipe.readFd()), READ_TIMEOUT)); + EXPECT_EQ(buffer.size(), fourMB); + EXPECT_FALSE(buffer.timedOut()); + EXPECT_TRUE(buffer.truncated()); + wait(&pid); + sp reader = buffer.data()->read(); + + while (reader->hasNext()) { + char c = 'A' + (reader->bytesRead() % 64 / 8); + ASSERT_TRUE(reader->next() == c); } } } diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp deleted file mode 100644 index 871226b7a6a9dfa2332a8ddcbeed106c8cd5abd5..0000000000000000000000000000000000000000 --- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#define DEBUG false -#include "Log.h" - -#include "FdBuffer.h" -#include "PrivacyBuffer.h" - -#include -#include -#include -#include -#include -#include - -using namespace android; -using namespace android::base; -using namespace android::os; -using namespace android::os::incidentd; -using ::testing::StrEq; -using ::testing::Test; -using ::testing::internal::CaptureStdout; -using ::testing::internal::GetCapturedStdout; - -const uint8_t OTHER_TYPE = 1; -const uint8_t STRING_TYPE = 9; -const uint8_t MESSAGE_TYPE = 11; -const std::string STRING_FIELD_0 = "\x02\viamtestdata"; -const std::string VARINT_FIELD_1 = "\x08\x96\x01"; // 150 -const std::string STRING_FIELD_2 = "\x12\vandroidwins"; -const std::string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 -const std::string FIX32_FIELD_4 = "\x25\xff\xff\xff\xff"; // -1 -const std::string MESSAGE_FIELD_5 = "\x2a\x10" + VARINT_FIELD_1 + STRING_FIELD_2; -const std::string NEGATIVE_VARINT_FIELD_6 = "\x30\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01"; // -1 - -class PrivacyBufferTest : public Test { -public: - virtual ~PrivacyBufferTest() { - // Delete in reverse order of construction, to be consistent with - // regular allocation/deallocation. - while (!privacies.empty()) { - delete privacies.back(); - privacies.pop_back(); - } - } - - virtual void SetUp() override { ASSERT_NE(tf.fd, -1); } - - void writeToFdBuffer(std::string str) { - ASSERT_TRUE(WriteStringToFile(str, tf.path)); - ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, 10000)); - ASSERT_EQ(str.size(), buffer.size()); - } - - void assertBuffer(PrivacyBuffer& buf, std::string expected) { - ASSERT_EQ(buf.size(), expected.size()); - CaptureStdout(); - ASSERT_EQ(buf.flush(STDOUT_FILENO), NO_ERROR); - ASSERT_THAT(GetCapturedStdout(), StrEq(expected)); - } - - void assertStrip(uint8_t dest, std::string expected, Privacy* policy) { - PrivacySpec spec = PrivacySpec::new_spec(dest); - EncodedBuffer::iterator bufData = buffer.data(); - PrivacyBuffer privacyBuf(policy, bufData); - ASSERT_EQ(privacyBuf.strip(spec), NO_ERROR); - assertBuffer(privacyBuf, expected); - } - - void assertStripByFields(uint8_t dest, std::string expected, int size, Privacy* privacy, ...) { - Privacy* list[size + 1]; - list[0] = privacy; - va_list args; - va_start(args, privacy); - for (int i = 1; i < size; i++) { - Privacy* p = va_arg(args, Privacy*); - list[i] = p; - } - va_end(args); - list[size] = NULL; - assertStrip(dest, expected, create_message_privacy(300, list)); - } - - Privacy* create_privacy(uint32_t field_id, uint8_t type, uint8_t dest) { - Privacy* p = new_uninit_privacy(); - p->field_id = field_id; - p->type = type; - p->children = NULL; - p->dest = dest; - p->patterns = NULL; - return p; - } - - Privacy* create_message_privacy(uint32_t field_id, Privacy** children) { - Privacy* p = new_uninit_privacy(); - p->field_id = field_id; - p->type = MESSAGE_TYPE; - p->children = children; - p->dest = DEST_DEFAULT_VALUE; - p->patterns = NULL; - return p; - } - - FdBuffer buffer; - -private: - TemporaryFile tf; - // Littering this code with unique_ptr (or similar) is ugly, so we just - // mass-free everything after the test completes. - std::vector privacies; - - Privacy* new_uninit_privacy() { - Privacy* p = new Privacy; - privacies.push_back(p); - return p; - } -}; - -TEST_F(PrivacyBufferTest, NullPolicy) { - writeToFdBuffer(STRING_FIELD_0); - assertStrip(DEST_EXPLICIT, STRING_FIELD_0, NULL); -} - -TEST_F(PrivacyBufferTest, StripUnsetField) { - writeToFdBuffer(STRING_FIELD_0); - assertStripByFields(DEST_AUTOMATIC, "", 1, create_privacy(0, STRING_TYPE, DEST_UNSET)); -} - -TEST_F(PrivacyBufferTest, StripVarintField) { - writeToFdBuffer(VARINT_FIELD_1); - assertStripByFields(DEST_EXPLICIT, "", 1, create_privacy(1, OTHER_TYPE, DEST_LOCAL)); -} - -TEST_F(PrivacyBufferTest, StripLengthDelimitedField_String) { - writeToFdBuffer(STRING_FIELD_2); - assertStripByFields(DEST_EXPLICIT, "", 1, create_privacy(2, STRING_TYPE, DEST_LOCAL)); -} - -TEST_F(PrivacyBufferTest, StripFixed64Field) { - writeToFdBuffer(FIX64_FIELD_3); - assertStripByFields(DEST_EXPLICIT, "", 1, create_privacy(3, OTHER_TYPE, DEST_LOCAL)); -} - -TEST_F(PrivacyBufferTest, StripFixed32Field) { - writeToFdBuffer(FIX32_FIELD_4); - assertStripByFields(DEST_EXPLICIT, "", 1, create_privacy(4, OTHER_TYPE, DEST_LOCAL)); -} - -TEST_F(PrivacyBufferTest, StripLengthDelimitedField_Message) { - writeToFdBuffer(MESSAGE_FIELD_5); - assertStripByFields(DEST_EXPLICIT, "", 1, create_privacy(5, MESSAGE_TYPE, DEST_LOCAL)); -} - -TEST_F(PrivacyBufferTest, StripNegativeVarint) { - writeToFdBuffer(NEGATIVE_VARINT_FIELD_6); - assertStripByFields(DEST_EXPLICIT, "", 1, create_privacy(6, OTHER_TYPE, DEST_LOCAL)); -} - -TEST_F(PrivacyBufferTest, NoStripVarintField) { - writeToFdBuffer(VARINT_FIELD_1); - assertStripByFields(DEST_EXPLICIT, VARINT_FIELD_1, 1, - create_privacy(1, OTHER_TYPE, DEST_AUTOMATIC)); -} - -TEST_F(PrivacyBufferTest, NoStripLengthDelimitedField_String) { - writeToFdBuffer(STRING_FIELD_2); - assertStripByFields(DEST_EXPLICIT, STRING_FIELD_2, 1, - create_privacy(2, STRING_TYPE, DEST_AUTOMATIC)); -} - -TEST_F(PrivacyBufferTest, NoStripFixed64Field) { - writeToFdBuffer(FIX64_FIELD_3); - assertStripByFields(DEST_EXPLICIT, FIX64_FIELD_3, 1, - create_privacy(3, OTHER_TYPE, DEST_AUTOMATIC)); -} - -TEST_F(PrivacyBufferTest, NoStripFixed32Field) { - writeToFdBuffer(FIX32_FIELD_4); - assertStripByFields(DEST_EXPLICIT, FIX32_FIELD_4, 1, - create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC)); -} - -TEST_F(PrivacyBufferTest, NoStripLengthDelimitedField_Message) { - writeToFdBuffer(MESSAGE_FIELD_5); - assertStripByFields(DEST_EXPLICIT, MESSAGE_FIELD_5, 1, - create_privacy(5, MESSAGE_TYPE, DEST_AUTOMATIC)); -} - -TEST_F(PrivacyBufferTest, NoStripNegativeVarintField) { - writeToFdBuffer(NEGATIVE_VARINT_FIELD_6); - assertStripByFields(DEST_EXPLICIT, NEGATIVE_VARINT_FIELD_6, 1, - create_privacy(6, OTHER_TYPE, DEST_AUTOMATIC)); -} - -TEST_F(PrivacyBufferTest, StripVarintAndString) { - writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3 + - FIX32_FIELD_4); - std::string expected = STRING_FIELD_0 + FIX64_FIELD_3 + FIX32_FIELD_4; - assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(1, OTHER_TYPE, DEST_LOCAL), - create_privacy(2, STRING_TYPE, DEST_LOCAL)); -} - -TEST_F(PrivacyBufferTest, StripVarintAndFixed64) { - writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3 + - FIX32_FIELD_4); - std::string expected = STRING_FIELD_0 + STRING_FIELD_2 + FIX32_FIELD_4; - assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(1, OTHER_TYPE, DEST_LOCAL), - create_privacy(3, OTHER_TYPE, DEST_LOCAL)); -} - -TEST_F(PrivacyBufferTest, StripVarintInNestedMessage) { - writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5); - Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL}; - std::string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2; - assertStripByFields(DEST_EXPLICIT, expected, 1, create_message_privacy(5, list)); -} - -TEST_F(PrivacyBufferTest, StripFix64AndVarintInNestedMessage) { - writeToFdBuffer(STRING_FIELD_0 + FIX64_FIELD_3 + MESSAGE_FIELD_5); - Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL}; - std::string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2; - assertStripByFields(DEST_EXPLICIT, expected, 2, create_privacy(3, OTHER_TYPE, DEST_LOCAL), - create_message_privacy(5, list)); -} - -TEST_F(PrivacyBufferTest, ClearAndStrip) { - string data = STRING_FIELD_0 + VARINT_FIELD_1; - writeToFdBuffer(data); - Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL}; - EncodedBuffer::iterator bufData = buffer.data(); - PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData); - PrivacySpec spec1 = PrivacySpec::new_spec(DEST_EXPLICIT); - PrivacySpec spec2 = PrivacySpec::new_spec(DEST_LOCAL); - - ASSERT_EQ(privacyBuf.strip(spec1), NO_ERROR); - assertBuffer(privacyBuf, STRING_FIELD_0); - ASSERT_EQ(privacyBuf.strip(spec2), NO_ERROR); - assertBuffer(privacyBuf, data); -} - -TEST_F(PrivacyBufferTest, BadDataInFdBuffer) { - writeToFdBuffer("iambaddata"); - Privacy* list[] = {create_privacy(4, OTHER_TYPE, DEST_AUTOMATIC), NULL}; - EncodedBuffer::iterator bufData = buffer.data(); - PrivacyBuffer privacyBuf(create_message_privacy(300, list), bufData); - PrivacySpec spec; - ASSERT_EQ(privacyBuf.strip(spec), BAD_VALUE); -} - -TEST_F(PrivacyBufferTest, BadDataInNestedMessage) { - writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5 + "aoeoe"); - Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL}; - Privacy* field5[] = {create_message_privacy(5, list), NULL}; - EncodedBuffer::iterator bufData = buffer.data(); - PrivacyBuffer privacyBuf(create_message_privacy(300, field5), bufData); - PrivacySpec spec; - ASSERT_EQ(privacyBuf.strip(spec), BAD_VALUE); -} - -TEST_F(PrivacyBufferTest, SelfRecursionMessage) { - string input = "\x2a\"" + VARINT_FIELD_1 + STRING_FIELD_2 + MESSAGE_FIELD_5; - writeToFdBuffer(input); - Privacy* field5 = create_message_privacy(5, NULL); - Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), field5, NULL}; - field5->children = list; - std::string expected = "\x2a\x1c" + STRING_FIELD_2 + "\x2a\xd" + STRING_FIELD_2; - assertStrip(DEST_EXPLICIT, expected, field5); -} - -TEST_F(PrivacyBufferTest, AutoMessage) { - writeToFdBuffer(STRING_FIELD_2 + MESSAGE_FIELD_5); - Privacy* list[] = {create_privacy(1, OTHER_TYPE, DEST_LOCAL), NULL}; - Privacy* autoMsg = create_privacy(5, MESSAGE_TYPE, DEST_AUTOMATIC); - autoMsg->children = list; - std::string expected = "\x2a\xd" + STRING_FIELD_2; - assertStripByFields(DEST_AUTOMATIC, expected, 1, autoMsg); -} diff --git a/cmds/incidentd/tests/PrivacyFilter_test.cpp b/cmds/incidentd/tests/PrivacyFilter_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5c05aac0ac57945d68fcec878a2187bcb1742d49 --- /dev/null +++ b/cmds/incidentd/tests/PrivacyFilter_test.cpp @@ -0,0 +1,302 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#define DEBUG false +#include "Log.h" + +#include "FdBuffer.h" +#include "PrivacyFilter.h" + +#include +#include +#include +#include +#include +#include + +using namespace android; +using namespace android::base; +using namespace android::os; +using namespace android::os::incidentd; +using ::testing::StrEq; +using ::testing::Test; +using ::testing::internal::CaptureStdout; +using ::testing::internal::GetCapturedStdout; + +const uint8_t OTHER_TYPE = 1; +const uint8_t STRING_TYPE = 9; +const uint8_t MESSAGE_TYPE = 11; +const std::string STRING_FIELD_0 = "\x02\viamtestdata"; +const std::string VARINT_FIELD_1 = "\x08\x96\x01"; // 150 +const std::string STRING_FIELD_2 = "\x12\vandroidwins"; +const std::string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 +const std::string FIX32_FIELD_4 = "\x25\xff\xff\xff\xff"; // -1 +const std::string MESSAGE_FIELD_5 = "\x2a\x10" + VARINT_FIELD_1 + STRING_FIELD_2; +const std::string NEGATIVE_VARINT_FIELD_6 = "\x30\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01"; // -1 + +#if 0 +class PrivacyFilterTest : public Test { +public: + virtual ~PrivacyFilterTest() { + // Delete in reverse order of construction, to be consistent with + // regular allocation/deallocation. + while (!privacies.empty()) { + delete privacies.back(); + privacies.pop_back(); + } + } + + virtual void SetUp() override { ASSERT_NE(tf.fd, -1); } + + void writeToFdBuffer(std::string str) { + ASSERT_TRUE(WriteStringToFile(str, tf.path)); + ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, 10000)); + ASSERT_EQ(str.size(), buffer.size()); + } + + void assertBuffer(PrivacyFilter& buf, std::string expected) { + ASSERT_EQ(buf.size(), expected.size()); + CaptureStdout(); + ASSERT_EQ(buf.flush(STDOUT_FILENO), NO_ERROR); + ASSERT_THAT(GetCapturedStdout(), StrEq(expected)); + } + + void assertStrip(uint8_t privacyPolicy, std::string expected, Privacy* policy) { + PrivacySpec spec = PrivacySpec::new_spec(privacyPolicy); + EncodedBuffer::iterator bufData = buffer.data(); + PrivacyFilter filter(policy, bufData); + ASSERT_EQ(filter.strip(spec), NO_ERROR); + assertBuffer(filter, expected); + } + + void assertStripByFields(uint8_t privacyPolicy, std::string expected, int size, + Privacy* privacy, ...) { + Privacy* list[size + 1]; + list[0] = privacy; + va_list args; + va_start(args, privacy); + for (int i = 1; i < size; i++) { + Privacy* p = va_arg(args, Privacy*); + list[i] = pPrivacyFilter + } + va_end(args); + list[size] = NULL; + assertStrip(privacyPolicy, expected, create_message_privacy(300, list)); + } + + Privacy* create_privacy(uint32_t field_id, uint8_t type, uint8_t privacyPolicy) { + Privacy* p = new_uninit_privacy(); + p->field_id = field_id; + p->type = type; + p->children = NULL; + p->policy = privacyPolicy; + p->patterns = NULL; + return p; + } + + Privacy* create_message_privacy(uint32_t field_id, Privacy** children) { + Privacy* p = new_uninit_privacy(); + p->field_id = field_id; + p->type = MESSAGE_TYPE; + p->children = children; + p->policy = PRIVACY_POLICY_UNSET; + p->patterns = NULL; + return p; + } + + FdBuffer buffer; + +private: + TemporaryFile tf; + // Littering this code with unique_ptr (or similar) is ugly, so we just + // mass-free everything after the test completes. + std::vector privacies; + + Privacy* new_uninit_privacy() { + Privacy* p = new Privacy; + privacies.push_back(p); + return p; + } +}; + +TEST_F(PrivacyFilterTest, NullPolicy) { + writeToFdBuffer(STRING_FIELD_0); + assertStrip(PRIVACY_POLICY_EXPLICIT, STRING_FIELD_0, NULL); +} + +TEST_F(PrivacyFilterTest, StripUnsetField) { + writeToFdBuffer(STRING_FIELD_0); + assertStripByFields(PRIVACY_POLICY_AUTOMATIC, "", 1, + create_privacy(0, STRING_TYPE, PRIVACY_POLICY_UNSET)); +} + +TEST_F(PrivacyFilterTest, StripVarintField) { + writeToFdBuffer(VARINT_FIELD_1); + assertStripByFields(PRIVACY_POLICY_EXPLICIT, "", 1, + create_privacy(1, OTHER_TYPE, PRIVACY_POLICY_LOCAL)); +} + +TEST_F(PrivacyFilterTest, StripLengthDelimitedField_String) { + writeToFdBuffer(STRING_FIELD_2); + assertStripByFields(PRIVACY_POLICY_EXPLICIT, "", 1, + create_privacy(2, STRING_TYPE, PRIVACY_POLICY_LOCAL)); +} + +TEST_F(PrivacyFilterTest, StripFixed64Field) { + writeToFdBuffer(FIX64_FIELD_3); + assertStripByFields(PRIVACY_POLICY_EXPLICIT, "", 1, + create_privacy(3, OTHER_TYPE, PRIVACY_POLICY_LOCAL)); +} + +TEST_F(PrivacyFilterTest, StripFixed32Field) { + writeToFdBuffer(FIX32_FIELD_4); + assertStripByFields(PRIVACY_POLICY_EXPLICIT, "", 1, + create_privacy(4, OTHER_TYPE, PRIVACY_POLICY_LOCAL)); +} + +TEST_F(PrivacyFilterTest, StripLengthDelimitedField_Message) { + writeToFdBuffer(MESSAGE_FIELD_5); + assertStripByFields(PRIVACY_POLICY_EXPLICIT, "", 1, + create_privacy(5, MESSAGE_TYPE, PRIVACY_POLICY_LOCAL)); +} + +TEST_F(PrivacyFilterTest, StripNegativeVarint) { + writeToFdBuffer(NEGATIVE_VARINT_FIELD_6); + assertStripByFields(PRIVACY_POLICY_EXPLICIT, "", 1, + create_privacy(6, OTHER_TYPE, PRIVACY_POLICY_LOCAL)); +} + +TEST_F(PrivacyFilterTest, NoStripVarintField) { + writeToFdBuffer(VARINT_FIELD_1); + assertStripByFields(PRIVACY_POLICY_EXPLICIT, VARINT_FIELD_1, 1, + create_privacy(1, OTHER_TYPE, PRIVACY_POLICY_AUTOMATIC)); +} + +TEST_F(PrivacyFilterTest, NoStripLengthDelimitedField_String) { + writeToFdBuffer(STRING_FIELD_2); + assertStripByFields(PRIVACY_POLICY_EXPLICIT, STRING_FIELD_2, 1, + create_privacy(2, STRING_TYPE, PRIVACY_POLICY_AUTOMATIC)); +} + +TEST_F(PrivacyFilterTest, NoStripFixed64Field) { + writeToFdBuffer(FIX64_FIELD_3); + assertStripByFields(PRIVACY_POLICY_EXPLICIT, FIX64_FIELD_3, 1, + create_privacy(3, OTHER_TYPE, PRIVACY_POLICY_AUTOMATIC)); +} + +TEST_F(PrivacyFilterTest, NoStripFixed32Field) { + writeToFdBuffer(FIX32_FIELD_4); + assertStripByFields(PRIVACY_POLICY_EXPLICIT, FIX32_FIELD_4, 1, + create_privacy(4, OTHER_TYPE, PRIVACY_POLICY_AUTOMATIC)); +} + +TEST_F(PrivacyFilterTest, NoStripLengthDelimitedField_Message) { + writeToFdBuffer(MESSAGE_FIELD_5); + assertStripByFields(PRIVACY_POLICY_EXPLICIT, MESSAGE_FIELD_5, 1, + create_privacy(5, MESSAGE_TYPE, PRIVACY_POLICY_AUTOMATIC)); +} + +TEST_F(PrivacyFilterTest, NoStripNegativeVarintField) { + writeToFdBuffer(NEGATIVE_VARINT_FIELD_6); + assertStripByFields(PRIVACY_POLICY_EXPLICIT, NEGATIVE_VARINT_FIELD_6, 1, + create_privacy(6, OTHER_TYPE, PRIVACY_POLICY_AUTOMATIC)); +} + +TEST_F(PrivacyFilterTest, StripVarintAndString) { + writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3 + + FIX32_FIELD_4); + std::string expected = STRING_FIELD_0 + FIX64_FIELD_3 + FIX32_FIELD_4; + assertStripByFields(PRIVACY_POLICY_EXPLICIT, expected, 2, + create_privacy(1, OTHER_TYPE, PRIVACY_POLICY_LOCAL), + create_privacy(2, STRING_TYPE, PRIVACY_POLICY_LOCAL)); +} + +TEST_F(PrivacyFilterTest, StripVarintAndFixed64) { + writeToFdBuffer(STRING_FIELD_0 + VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3 + + FIX32_FIELD_4); + std::string expected = STRING_FIELD_0 + STRING_FIELD_2 + FIX32_FIELD_4; + assertStripByFields(PRIVACY_POLICY_EXPLICIT, expected, 2, + create_privacy(1, OTHER_TYPE, PRIVACY_POLICY_LOCAL), + create_privacy(3, OTHER_TYPE, PRIVACY_POLICY_LOCAL)); +} + +TEST_F(PrivacyFilterTest, StripVarintInNestedMessage) { + writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5); + Privacy* list[] = {create_privacy(1, OTHER_TYPE, PRIVACY_POLICY_LOCAL), NULL}; + std::string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2; + assertStripByFields(PRIVACY_POLICY_EXPLICIT, expected, 1, create_message_privacy(5, list)); +} + +TEST_F(PrivacyFilterTest, StripFix64AndVarintInNestedMessage) { + writeToFdBuffer(STRING_FIELD_0 + FIX64_FIELD_3 + MESSAGE_FIELD_5); + Privacy* list[] = {create_privacy(1, OTHER_TYPE, PRIVACY_POLICY_LOCAL), NULL}; + std::string expected = STRING_FIELD_0 + "\x2a\xd" + STRING_FIELD_2; + assertStripByFields(PRIVACY_POLICY_EXPLICIT, expected, 2, + create_privacy(3, OTHER_TYPE, PRIVACY_POLICY_LOCAL), + create_message_privacy(5, list)); +} + +TEST_F(PrivacyFilterTest, ClearAndStrip) { + string data = STRING_FIELD_0 + VARINT_FIELD_1; + writeToFdBuffer(data); + Privacy* list[] = {create_privacy(1, OTHER_TYPE, PRIVACY_POLICY_LOCAL), NULL}; + EncodedBuffer::iterator bufData = buffer.data(); + PrivacyFilter filter(create_message_privacy(300, list), bufData); + PrivacySpec spec1 = PrivacySpec::new_spec(PRIVACY_POLICY_EXPLICIT); + PrivacySpec spec2 = PrivacySpec::new_spec(PRIVACY_POLICY_LOCAL); + + ASSERT_EQ(filter.strip(spec1), NO_ERROR); + assertBuffer(filter, STRING_FIELD_0); + ASSERT_EQ(filter.strip(spec2), NO_ERROR); + assertBuffer(filter, data); +} + +TEST_F(PrivacyFilterTest, BadDataInFdBuffer) { + writeToFdBuffer("iambaddata"); + Privacy* list[] = {create_privacy(4, OTHER_TYPE, PRIVACY_POLICY_AUTOMATIC), NULL}; + EncodedBuffer::iterator bufData = buffer.data(); + PrivacyFilter filter(create_message_privacy(300, list), bufData); + PrivacySpec spec; + ASSERT_EQ(filter.strip(spec), BAD_VALUE); +} + +TEST_F(PrivacyFilterTest, BadDataInNestedMessage) { + writeToFdBuffer(STRING_FIELD_0 + MESSAGE_FIELD_5 + "aoeoe"); + Privacy* list[] = {create_privacy(1, OTHER_TYPE, PRIVACY_POLICY_LOCAL), NULL}; + Privacy* field5[] = {create_message_privacy(5, list), NULL}; + EncodedBuffer::iterator bufData = buffer.data(); + PrivacyFilter filter(create_message_privacy(300, field5), bufData); + PrivacySpec spec; + ASSERT_EQ(filter.strip(spec), BAD_VALUE); +} + +TEST_F(PrivacyFilterTest, SelfRecursionMessage) { + string input = "\x2a\"" + VARINT_FIELD_1 + STRING_FIELD_2 + MESSAGE_FIELD_5; + writeToFdBuffer(input); + Privacy* field5 = create_message_privacy(5, NULL); + Privacy* list[] = {create_privacy(1, OTHER_TYPE, PRIVACY_POLICY_LOCAL), field5, NULL}; + field5->children = list; + std::string expected = "\x2a\x1c" + STRING_FIELD_2 + "\x2a\xd" + STRING_FIELD_2; + assertStrip(PRIVACY_POLICY_EXPLICIT, expected, field5); +} + +TEST_F(PrivacyFilterTest, AutoMessage) { + writeToFdBuffer(STRING_FIELD_2 + MESSAGE_FIELD_5); + Privacy* list[] = {create_privacy(1, OTHER_TYPE, PRIVACY_POLICY_LOCAL), NULL}; + Privacy* autoMsg = create_privacy(5, MESSAGE_TYPE, PRIVACY_POLICY_AUTOMATIC); + autoMsg->children = list; + std::string expected = "\x2a\xd" + STRING_FIELD_2; + assertStripByFields(PRIVACY_POLICY_AUTOMATIC, expected, 1, autoMsg); +} + +#endif diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp index b5e41d7672957c0daddead92a8ec372fa91d2ad1..9becf173ee1ae43f518cefce192340c994612a02 100644 --- a/cmds/incidentd/tests/Reporter_test.cpp +++ b/cmds/incidentd/tests/Reporter_test.cpp @@ -17,7 +17,7 @@ #include "Reporter.h" #include -#include +#include #include #include @@ -36,6 +36,7 @@ using ::testing::StrEq; using ::testing::Test; namespace { +/* void getHeaderData(const IncidentHeaderProto& headerProto, vector* out) { out->clear(); auto serialized = headerProto.SerializeAsString(); @@ -43,6 +44,7 @@ void getHeaderData(const IncidentHeaderProto& headerProto, vector* out) out->resize(serialized.length()); std::copy(serialized.begin(), serialized.end(), out->begin()); } +*/ } class TestListener : public IIncidentReportStatusListener { @@ -82,6 +84,24 @@ public: return Status::ok(); }; + int sectionStarted(int sectionId) const { + map::const_iterator found = startSections.find(sectionId); + if (found != startSections.end()) { + return found->second; + } else { + return 0; + } + }; + + int sectionFinished(int sectionId) const { + map::const_iterator found = finishSections.find(sectionId); + if (found != finishSections.end()) { + return found->second; + } else { + return 0; + } + }; + protected: virtual IBinder* onAsBinder() override { return nullptr; }; }; @@ -89,8 +109,7 @@ protected: class ReporterTest : public Test { public: virtual void SetUp() { - reporter = new Reporter(td.path); - l = new TestListener(); + listener = new TestListener(); } vector InspectFiles() { @@ -115,9 +134,7 @@ public: protected: TemporaryDir td; - ReportRequestSet requests; - sp reporter; - sp l; + sp listener; size_t size; }; @@ -132,18 +149,17 @@ TEST_F(ReporterTest, IncidentReportArgs) { ASSERT_TRUE(args1.containsSection(3)); } -TEST_F(ReporterTest, ReportRequestSetEmpty) { - requests.setMainFd(STDOUT_FILENO); - ASSERT_EQ(requests.mainFd(), STDOUT_FILENO); -} - +/* TEST_F(ReporterTest, RunReportEmpty) { + vector> requests; + sp reporter = new Reporter(requests, td.path); + ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size)); - EXPECT_EQ(l->startInvoked, 0); - EXPECT_EQ(l->finishInvoked, 0); - EXPECT_TRUE(l->startSections.empty()); - EXPECT_TRUE(l->finishSections.empty()); - EXPECT_EQ(l->failedInvoked, 0); + EXPECT_EQ(0, listener->startInvoked); + EXPECT_EQ(0, listener->finishInvoked); + EXPECT_TRUE(listener->startSections.empty()); + EXPECT_TRUE(listener->finishSections.empty()); + EXPECT_EQ(0, listener->failedInvoked); } TEST_F(ReporterTest, RunReportWithHeaders) { @@ -157,11 +173,11 @@ TEST_F(ReporterTest, RunReportWithHeaders) { vector out; getHeaderData(header, &out); args2.addHeader(out); - sp r1 = new ReportRequest(args1, l, tf.fd); - sp r2 = new ReportRequest(args2, l, tf.fd); - reporter->batch.add(r1); - reporter->batch.add(r2); + sp workDirectory = new WorkDirectory(td.path); + sp batch = new ReportBatch(); + batch->addStreamingReport(args1, listener, tf.fd); + sp reporter = new Reporter(workDirectory, batch); ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size)); @@ -170,11 +186,11 @@ TEST_F(ReporterTest, RunReportWithHeaders) { EXPECT_THAT(result, StrEq("\n\x2" "\b\f")); - EXPECT_EQ(l->startInvoked, 2); - EXPECT_EQ(l->finishInvoked, 2); - EXPECT_TRUE(l->startSections.empty()); - EXPECT_TRUE(l->finishSections.empty()); - EXPECT_EQ(l->failedInvoked, 0); + EXPECT_EQ(listener->startInvoked, 1); + EXPECT_EQ(listener->finishInvoked, 1); + EXPECT_FALSE(listener->startSections.empty()); + EXPECT_FALSE(listener->finishSections.empty()); + EXPECT_EQ(listener->failedInvoked, 0); } TEST_F(ReporterTest, RunReportToGivenDirectory) { @@ -188,8 +204,10 @@ TEST_F(ReporterTest, RunReportToGivenDirectory) { args.addHeader(out); getHeaderData(header2, &out); args.addHeader(out); - sp r = new ReportRequest(args, l, -1); - reporter->batch.add(r); + + vector> requests; + requests.push_back(new ReportRequest(args, listener, -1)); + sp reporter = new Reporter(requests, td.path); ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size)); vector results = InspectFiles(); @@ -204,14 +222,36 @@ TEST_F(ReporterTest, RunReportToGivenDirectory) { TEST_F(ReporterTest, ReportMetadata) { IncidentReportArgs args; args.addSection(1); - args.setDest(android::os::DEST_EXPLICIT); - sp r = new ReportRequest(args, l, -1); - reporter->batch.add(r); + args.setPrivacyPolicy(android::os::PRIVACY_POLICY_EXPLICIT); + vector> requests; + requests.push_back(new ReportRequest(args, listener, -1)); + sp reporter = new Reporter(requests, td.path); ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size)); - IncidentMetadata metadata = reporter->batch.metadata(); + IncidentMetadata metadata = reporter->metadata(); EXPECT_EQ(IncidentMetadata_Destination_EXPLICIT, metadata.dest()); EXPECT_EQ(1, metadata.request_size()); EXPECT_TRUE(metadata.use_dropbox()); EXPECT_EQ(0, metadata.sections_size()); } + +TEST_F(ReporterTest, RunReportLocal_1_2) { + IncidentReportArgs args; + args.addSection(1); + args.addSection(2); + args.setPrivacyPolicy(android::os::PRIVACY_POLICY_LOCAL); + + vector> requests; + requests.push_back(new ReportRequest(args, listener, -1)); + sp reporter = new Reporter(requests, td.path); + + ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size)); + + EXPECT_EQ(1, listener->sectionStarted(1)); + EXPECT_EQ(1, listener->sectionFinished(1)); + EXPECT_EQ(1, listener->sectionStarted(2)); + EXPECT_EQ(1, listener->sectionFinished(2)); + + // TODO: validate that a file was created in the directory +} +*/ diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp index 24454ed15d40100ead4dbc64400af48835fd5381..858f7d07c9306a0e6faad8a5da0adc563c070654 100644 --- a/cmds/incidentd/tests/Section_test.cpp +++ b/cmds/incidentd/tests/Section_test.cpp @@ -20,7 +20,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -62,7 +63,6 @@ public: protected: TemporaryFile tf; - ReportRequestSet requests; const std::string kTestPath = GetExecutableDirectory(); const std::string kTestDataPath = kTestPath + "/testdata/"; @@ -74,7 +74,8 @@ public: virtual ~SimpleListener(){}; virtual Status onReportStarted() { return Status::ok(); }; - virtual Status onReportSectionStatus(int /*section*/, int /*status*/) { return Status::ok(); }; + virtual Status onReportSectionStatus(int /*section*/, int /*status*/) + { return Status::ok(); }; virtual Status onReportFinished() { return Status::ok(); }; virtual Status onReportFailed() { return Status::ok(); }; @@ -82,67 +83,30 @@ protected: virtual IBinder* onAsBinder() override { return nullptr; }; }; -namespace { -void getHeaderData(const IncidentHeaderProto& headerProto, vector* out) { - out->clear(); - auto serialized = headerProto.SerializeAsString(); - if (serialized.empty()) return; - out->resize(serialized.length()); - std::copy(serialized.begin(), serialized.end(), out->begin()); -} -} - -TEST_F(SectionTest, HeaderSection) { - HeaderSection hs; - - IncidentReportArgs args1, args2; - args1.addSection(1); - args1.addSection(2); - args2.setAll(true); - - IncidentHeaderProto head1, head2; - head1.set_reason("axe"); - head2.set_reason("pup"); - - vector out; - getHeaderData(head1, &out); - args1.addHeader(out); - - getHeaderData(head2, &out); - args1.addHeader(out); - - getHeaderData(head2, &out); - args2.addHeader(out); - - requests.add(new ReportRequest(args1, new SimpleListener(), -1)); - requests.add(new ReportRequest(args2, new SimpleListener(), tf.fd)); - requests.setMainFd(STDOUT_FILENO); - - std::string content; - CaptureStdout(); - ASSERT_EQ(NO_ERROR, hs.Execute(&requests)); - EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x5" - "\x12\x3" - "axe\n\x05\x12\x03pup")); - - EXPECT_TRUE(ReadFileToString(tf.path, &content)); - EXPECT_THAT(content, StrEq("\n\x05\x12\x03pup")); -} - +/* TEST_F(SectionTest, MetadataSection) { MetadataSection ms; - const std::string testFile = kTestDataPath + "metadata.txt"; - std::string expect; - ASSERT_TRUE(ReadFileToString(testFile, &expect)); - requests.setMainFd(STDOUT_FILENO); - requests.setMainDest(android::os::DEST_LOCAL); - requests.sectionStats(1)->set_success(true); + vector> requests; + ReportRequestSet requestSet(requests); + requestSet.setMainFd(STDOUT_FILENO); + + requestSet.setMainPrivacyPolicy(android::os::PRIVACY_POLICY_LOCAL); + requestSet.editSectionStats(1)->set_success(true); CaptureStdout(); - ASSERT_EQ(NO_ERROR, ms.Execute(&requests)); - // Notice message_lite.h ParseFromString doesn't work so we just match the bytes directly. - EXPECT_THAT(GetCapturedStdout(), StrEq(expect)); + ASSERT_EQ(NO_ERROR, ms.Execute(&requestSet)); + + string out = GetCapturedStdout(); + IncidentProto expectedIncident; + expectedIncident.ParseFromArray(out.data(), out.size()); + ASSERT_TRUE(expectedIncident.has_metadata()); + const IncidentMetadata& expectedMetadata = expectedIncident.metadata(); + ASSERT_EQ(IncidentMetadata::LOCAL, expectedMetadata.dest()); + ASSERT_EQ(1, expectedMetadata.sections_size()); + ASSERT_EQ(1, expectedMetadata.sections(0).id()); + ASSERT_TRUE(expectedMetadata.sections(0).has_success()); + ASSERT_TRUE(expectedMetadata.sections(0).success()); } TEST_F(SectionTest, FileSection) { @@ -150,27 +114,35 @@ TEST_F(SectionTest, FileSection) { ASSERT_TRUE(WriteStringToFile("iamtestdata", tf.path)); - requests.setMainFd(STDOUT_FILENO); + vector> requests; + ReportRequestSet requestSet(requests); + requestSet.setMainFd(STDOUT_FILENO); CaptureStdout(); - ASSERT_EQ(NO_ERROR, fs.Execute(&requests)); + ASSERT_EQ(NO_ERROR, fs.Execute(&requestSet)); // The input string is reversed in incident helper // The length is 11, in 128Varint it is "0000 1011" -> \v EXPECT_THAT(GetCapturedStdout(), StrEq("\xa\vatadtsetmai")); } TEST_F(SectionTest, FileSectionNotExist) { + vector> requests; + ReportRequestSet requestSet(requests); + FileSection fs1(NOOP_PARSER, "notexist", QUICK_TIMEOUT_MS); - ASSERT_EQ(NO_ERROR, fs1.Execute(&requests)); + ASSERT_EQ(NO_ERROR, fs1.Execute(&requestSet)); FileSection fs2(NOOP_PARSER, "notexist", QUICK_TIMEOUT_MS); - ASSERT_EQ(NO_ERROR, fs2.Execute(&requests)); + ASSERT_EQ(NO_ERROR, fs2.Execute(&requestSet)); } TEST_F(SectionTest, FileSectionTimeout) { + vector> requests; + ReportRequestSet requestSet(requests); + FileSection fs(TIMEOUT_PARSER, tf.path, QUICK_TIMEOUT_MS); - ASSERT_EQ(NO_ERROR, fs.Execute(&requests)); - ASSERT_TRUE(requests.sectionStats(TIMEOUT_PARSER)->timed_out()); + ASSERT_EQ(NO_ERROR, fs.Execute(&requestSet)); + ASSERT_TRUE(requestSet.getSectionStats(TIMEOUT_PARSER)->timed_out()); } TEST_F(SectionTest, GZipSection) { @@ -178,10 +150,12 @@ TEST_F(SectionTest, GZipSection) { const std::string testGzFile = testFile + ".gz"; GZipSection gs(NOOP_PARSER, "/tmp/nonexist", testFile.c_str(), NULL); - requests.setMainFd(tf.fd); - requests.setMainDest(android::os::DEST_LOCAL); + vector> requests; + ReportRequestSet requestSet(requests); + requestSet.setMainFd(tf.fd); + requestSet.setMainPrivacyPolicy(android::os::PRIVACY_POLICY_LOCAL); - ASSERT_EQ(NO_ERROR, gs.Execute(&requests)); + ASSERT_EQ(NO_ERROR, gs.Execute(&requestSet)); std::string expected, gzFile, actual; ASSERT_TRUE(ReadFileToString(testGzFile, &gzFile)); ASSERT_TRUE(ReadFileToString(tf.path, &actual)); @@ -200,8 +174,10 @@ TEST_F(SectionTest, GZipSection) { TEST_F(SectionTest, GZipSectionNoFileFound) { GZipSection gs(NOOP_PARSER, "/tmp/nonexist1", "/tmp/nonexist2", NULL); - requests.setMainFd(STDOUT_FILENO); - ASSERT_EQ(NO_ERROR, gs.Execute(&requests)); + vector> requests; + ReportRequestSet requestSet(requests); + requestSet.setMainFd(STDOUT_FILENO); + ASSERT_EQ(NO_ERROR, gs.Execute(&requestSet)); } TEST_F(SectionTest, CommandSectionConstructor) { @@ -220,51 +196,65 @@ TEST_F(SectionTest, CommandSectionConstructor) { TEST_F(SectionTest, CommandSectionEcho) { CommandSection cs(REVERSE_PARSER, "/system/bin/echo", "about", NULL); - requests.setMainFd(STDOUT_FILENO); + vector> requests; + ReportRequestSet requestSet(requests); + requestSet.setMainFd(STDOUT_FILENO); CaptureStdout(); - ASSERT_EQ(NO_ERROR, cs.Execute(&requests)); + ASSERT_EQ(NO_ERROR, cs.Execute(&requestSet)); EXPECT_THAT(GetCapturedStdout(), StrEq("\xa\x06\ntuoba")); } TEST_F(SectionTest, CommandSectionCommandTimeout) { CommandSection cs(NOOP_PARSER, QUICK_TIMEOUT_MS, "/system/bin/yes", NULL); - ASSERT_EQ(NO_ERROR, cs.Execute(&requests)); - ASSERT_TRUE(requests.sectionStats(NOOP_PARSER)->timed_out()); + vector> requests; + ReportRequestSet requestSet(requests); + ASSERT_EQ(NO_ERROR, cs.Execute(&requestSet)); + ASSERT_TRUE(requestSet.getSectionStats(NOOP_PARSER)->timed_out()); } TEST_F(SectionTest, CommandSectionIncidentHelperTimeout) { CommandSection cs(TIMEOUT_PARSER, QUICK_TIMEOUT_MS, "/system/bin/echo", "about", NULL); - requests.setMainFd(STDOUT_FILENO); - ASSERT_EQ(NO_ERROR, cs.Execute(&requests)); - ASSERT_TRUE(requests.sectionStats(TIMEOUT_PARSER)->timed_out()); + vector> requests; + ReportRequestSet requestSet(requests); + requestSet.setMainFd(STDOUT_FILENO); + ASSERT_EQ(NO_ERROR, cs.Execute(&requestSet)); + ASSERT_TRUE(requestSet.getSectionStats(TIMEOUT_PARSER)->timed_out()); } TEST_F(SectionTest, CommandSectionBadCommand) { CommandSection cs(NOOP_PARSER, "echoo", "about", NULL); - ASSERT_EQ(NAME_NOT_FOUND, cs.Execute(&requests)); + vector> requests; + ReportRequestSet requestSet(requests); + ASSERT_EQ(NAME_NOT_FOUND, cs.Execute(&requestSet)); } TEST_F(SectionTest, CommandSectionBadCommandAndTimeout) { CommandSection cs(TIMEOUT_PARSER, QUICK_TIMEOUT_MS, "nonexistcommand", "-opt", NULL); // timeout will return first - ASSERT_EQ(NO_ERROR, cs.Execute(&requests)); - ASSERT_TRUE(requests.sectionStats(TIMEOUT_PARSER)->timed_out()); + vector> requests; + ReportRequestSet requestSet(requests); + ASSERT_EQ(NO_ERROR, cs.Execute(&requestSet)); + ASSERT_TRUE(requestSet.getSectionStats(TIMEOUT_PARSER)->timed_out()); } TEST_F(SectionTest, LogSectionBinary) { LogSection ls(1, LOG_ID_EVENTS); - requests.setMainFd(STDOUT_FILENO); + vector> requests; + ReportRequestSet requestSet(requests); + requestSet.setMainFd(STDOUT_FILENO); CaptureStdout(); - ASSERT_EQ(NO_ERROR, ls.Execute(&requests)); + ASSERT_EQ(NO_ERROR, ls.Execute(&requestSet)); std::string results = GetCapturedStdout(); EXPECT_FALSE(results.empty()); } TEST_F(SectionTest, LogSectionSystem) { LogSection ls(1, LOG_ID_SYSTEM); - requests.setMainFd(STDOUT_FILENO); + vector> requests; + ReportRequestSet requestSet(requests); + requestSet.setMainFd(STDOUT_FILENO); CaptureStdout(); - ASSERT_EQ(NO_ERROR, ls.Execute(&requests)); + ASSERT_EQ(NO_ERROR, ls.Execute(&requestSet)); std::string results = GetCapturedStdout(); EXPECT_FALSE(results.empty()); } @@ -274,10 +264,12 @@ TEST_F(SectionTest, TestFilterPiiTaggedFields) { ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path)); - requests.setMainFd(STDOUT_FILENO); + vector> requests; + ReportRequestSet requestSet(requests); + requestSet.setMainFd(STDOUT_FILENO); CaptureStdout(); - ASSERT_EQ(NO_ERROR, fs.Execute(&requests)); + ASSERT_EQ(NO_ERROR, fs.Execute(&requestSet)); EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2)); } @@ -287,13 +279,16 @@ TEST_F(SectionTest, TestBadFdRequest) { IncidentReportArgs args; args.setAll(true); - args.setDest(0); + args.setPrivacyPolicy(0); sp badFdRequest = new ReportRequest(args, new SimpleListener(), 1234567); - requests.add(badFdRequest); - requests.setMainFd(STDOUT_FILENO); + + vector> requests; + requests.push_back(badFdRequest); + ReportRequestSet requestSet(requests); + requestSet.setMainFd(STDOUT_FILENO); CaptureStdout(); - ASSERT_EQ(NO_ERROR, fs.Execute(&requests)); + ASSERT_EQ(NO_ERROR, fs.Execute(&requestSet)); EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2)); EXPECT_EQ(badFdRequest->err, -EBADF); } @@ -304,9 +299,13 @@ TEST_F(SectionTest, TestBadRequests) { IncidentReportArgs args; args.setAll(true); - args.setDest(0); - requests.add(new ReportRequest(args, new SimpleListener(), -1)); - EXPECT_EQ(fs.Execute(&requests), -EBADF); + args.setPrivacyPolicy(0); + + vector> requests; + requests.push_back(new ReportRequest(args, new SimpleListener(), -1)); + ReportRequestSet requestSet(requests); + + EXPECT_EQ(fs.Execute(&requestSet), -EBADF); } TEST_F(SectionTest, TestMultipleRequests) { @@ -320,17 +319,20 @@ TEST_F(SectionTest, TestMultipleRequests) { IncidentReportArgs args1, args2, args3; args1.setAll(true); - args1.setDest(android::os::DEST_LOCAL); + args1.setPrivacyPolicy(android::os::PRIVACY_POLICY_LOCAL); args2.setAll(true); - args2.setDest(android::os::DEST_EXPLICIT); + args2.setPrivacyPolicy(android::os::PRIVACY_POLICY_EXPLICIT); sp l = new SimpleListener(); - requests.add(new ReportRequest(args1, l, output1.fd)); - requests.add(new ReportRequest(args2, l, output2.fd)); - requests.add(new ReportRequest(args3, l, output3.fd)); - requests.setMainFd(STDOUT_FILENO); + + vector> requests; + requests.push_back(new ReportRequest(args1, l, output1.fd)); + requests.push_back(new ReportRequest(args2, l, output2.fd)); + requests.push_back(new ReportRequest(args3, l, output3.fd)); + ReportRequestSet requestSet(requests); + requestSet.setMainFd(STDOUT_FILENO); CaptureStdout(); - ASSERT_EQ(NO_ERROR, fs.Execute(&requests)); + ASSERT_EQ(NO_ERROR, fs.Execute(&requestSet)); EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2)); std::string content, expect; @@ -361,18 +363,21 @@ TEST_F(SectionTest, TestMultipleRequestsBySpec) { IncidentReportArgs args1, args2, args3; args1.setAll(true); - args1.setDest(android::os::DEST_EXPLICIT); + args1.setPrivacyPolicy(android::os::PRIVACY_POLICY_EXPLICIT); args2.setAll(true); - args2.setDest(android::os::DEST_EXPLICIT); + args2.setPrivacyPolicy(android::os::PRIVACY_POLICY_EXPLICIT); args3.setAll(true); sp l = new SimpleListener(); - requests.add(new ReportRequest(args1, l, output1.fd)); - requests.add(new ReportRequest(args2, l, output2.fd)); - requests.add(new ReportRequest(args3, l, output3.fd)); - requests.setMainFd(STDOUT_FILENO); + + vector> requests; + requests.push_back(new ReportRequest(args1, l, output1.fd)); + requests.push_back(new ReportRequest(args2, l, output2.fd)); + requests.push_back(new ReportRequest(args3, l, output3.fd)); + ReportRequestSet requestSet(requests); + requestSet.setMainFd(STDOUT_FILENO); CaptureStdout(); - ASSERT_EQ(NO_ERROR, fs.Execute(&requests)); + ASSERT_EQ(NO_ERROR, fs.Execute(&requestSet)); EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2)); std::string content, expect; @@ -390,3 +395,4 @@ TEST_F(SectionTest, TestMultipleRequestsBySpec) { EXPECT_TRUE(ReadFileToString(output3.path, &content)); EXPECT_THAT(content, StrEq(string("\x02") + c + STRING_FIELD_2)); } +*/ diff --git a/cmds/incidentd/tests/section_list.cpp b/cmds/incidentd/tests/section_list.cpp index 1d7f2b611f08d2b00ed9c619269425e66b137a4c..3a45af028518b68862ba4b5dfb09c6a4268a03f8 100644 --- a/cmds/incidentd/tests/section_list.cpp +++ b/cmds/incidentd/tests/section_list.cpp @@ -1,19 +1,65 @@ // This file is a dummy section_list.cpp used for test only. #include "section_list.h" +#include "frameworks/base/cmds/incidentd/tests/test_proto.pb.h" + + namespace android { namespace os { namespace incidentd { -const Section* SECTION_LIST[] = {NULL}; +class TestSection: public Section { +public: + TestSection(int id); + ~TestSection(); + virtual status_t Execute(ReportWriter* writer) const; +}; + +TestSection::TestSection(int id) + :Section(id, 5000 /* ms timeout */) { +} + +TestSection::~TestSection() { +} + +status_t TestSection::Execute(ReportWriter* writer) const { + uint8_t buf[1024]; + status_t err; + + TestSectionProto proto; + proto.set_field_1(this->id); + proto.set_field_2(this->id * 10); + + // Not infinitely scalable, but we know that our TestSectionProto will always + // fit in this many bytes. + if (!proto.SerializeToArray(buf, sizeof(buf))) { + return -1; + } + FdBuffer buffer; + err = buffer.write(buf, proto.ByteSize()); + if (err != NO_ERROR) { + return err; + } + + return writer->writeSection(buffer); +} + +TestSection section1(1); +TestSection section2(2); + +const Section* SECTION_LIST[] = { + §ion1, + §ion2, + NULL +}; -Privacy sub_field_1{1, 1, NULL, DEST_LOCAL, NULL}; -Privacy sub_field_2{2, 9, NULL, DEST_AUTOMATIC, NULL}; +Privacy sub_field_1{1, 1, NULL, PRIVACY_POLICY_LOCAL, NULL}; +Privacy sub_field_2{2, 9, NULL, PRIVACY_POLICY_AUTOMATIC, NULL}; Privacy* list[] = {&sub_field_1, &sub_field_2, NULL}; -Privacy field_0{0, 11, list, DEST_EXPLICIT, NULL}; -Privacy field_1{1, 9, NULL, DEST_AUTOMATIC, NULL}; +Privacy field_0{0, 11, list, PRIVACY_POLICY_EXPLICIT, NULL}; +Privacy field_1{1, 9, NULL, PRIVACY_POLICY_AUTOMATIC, NULL}; Privacy* final_list[] = {&field_0, &field_1}; @@ -23,4 +69,4 @@ const int PRIVACY_POLICY_COUNT = 2; } // namespace incidentd } // namespace os -} // namespace android \ No newline at end of file +} // namespace android diff --git a/services/net/java/android/net/ApfCapabilitiesParcelable.aidl b/cmds/incidentd/tests/test_proto.proto similarity index 64% rename from services/net/java/android/net/ApfCapabilitiesParcelable.aidl rename to cmds/incidentd/tests/test_proto.proto index f0645d2782d212db2649fe0460975b85585e69d5..f57070be64dec732bc95af32f90cb240b61b4e5b 100644 --- a/services/net/java/android/net/ApfCapabilitiesParcelable.aidl +++ b/cmds/incidentd/tests/test_proto.proto @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,15 @@ * limitations under the License. */ -package android.net; +syntax = "proto2"; + +package android.os.incidentd; + +message TestSectionProto { + // The id of the section, written by TestSection. + optional int32 field_1 = 1; + + // The id of the section, times 10, written by TestSection. + optional int32 field_2 = 2; +} -parcelable ApfCapabilitiesParcelable { - int apfVersionSupported; - int maximumApfProgramSize; - int apfPacketFormat; -} \ No newline at end of file diff --git a/cmds/media/Android.bp b/cmds/media/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..7879c53684a731f9ef9a8cfef7b95cd3358da5c2 --- /dev/null +++ b/cmds/media/Android.bp @@ -0,0 +1,8 @@ +// Copyright 2013 The Android Open Source Project +// + +java_binary { + name: "media", + wrapper: "media", + srcs: ["**/*.java"], +} diff --git a/cmds/media/Android.mk b/cmds/media/Android.mk deleted file mode 100644 index b9451c596da00685f9f2a5ec09a4b79b09db2f88..0000000000000000000000000000000000000000 --- a/cmds/media/Android.mk +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2013 The Android Open Source Project -# -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_MODULE := media_cmd -include $(BUILD_JAVA_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := media -LOCAL_SRC_FILES := media -LOCAL_MODULE_CLASS := EXECUTABLES -LOCAL_MODULE_TAGS := optional -include $(BUILD_PREBUILT) diff --git a/cmds/media/media b/cmds/media/media index 5c0eb2f39c2d29f3a54f1ddd8b21d8477ad93cb9..8ada9145a4f7646baabf564468215cee8fb54606 100755 --- a/cmds/media/media +++ b/cmds/media/media @@ -3,5 +3,5 @@ # shell. # base=/system -export CLASSPATH=$base/framework/media_cmd.jar +export CLASSPATH=$base/framework/media.jar exec app_process $base/bin com.android.commands.media.Media "$@" diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java index 4a6f87f09d291d3a857c08a9edf741e614dd68e0..6033655c8513c4033204d1e19297e0c328e9df7b 100644 --- a/cmds/sm/src/com/android/commands/sm/Sm.java +++ b/cmds/sm/src/com/android/commands/sm/Sm.java @@ -103,8 +103,6 @@ public final class Sm { runSetVirtualDisk(); } else if ("set-isolated-storage".equals(op)) { runIsolatedStorage(); - } else if ("set-legacy-greylist".equals(op)) { - runLegacyGreylist(); } else { throw new IllegalArgumentException(); } @@ -306,12 +304,6 @@ public final class Sm { mSm.setDebugFlags(value, mask); } - public void runLegacyGreylist() throws RemoteException { - final boolean legacyGreylist = Boolean.parseBoolean(nextArg()); - mSm.setDebugFlags(legacyGreylist ? StorageManager.DEBUG_LEGACY_GREYLIST : 0, - StorageManager.DEBUG_LEGACY_GREYLIST); - } - public void runIdleMaint() throws RemoteException { final boolean im_run = "run".equals(nextArg()); if (im_run) { diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index ce07d6d12f7271476b4b2891c986b6a54d29540d..017cb6d9221ec140a68e7318c39286f1a56bc975 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -81,7 +81,7 @@ cc_defaults { "src/external/StatsPullerManager.cpp", "src/external/puller_util.cpp", "src/logd/LogEvent.cpp", - "src/logd/LogListener.cpp", + "src/logd/LogEventQueue.cpp", "src/matchers/CombinationLogMatchingTracker.cpp", "src/matchers/EventMatcherWizard.cpp", "src/matchers/matcher_util.cpp", @@ -118,6 +118,7 @@ cc_defaults { static_libs: [ "libhealthhalutils", + "libplatformprotos", ], shared_libs: [ @@ -225,6 +226,7 @@ cc_test { "tests/indexed_priority_queue_test.cpp", "tests/LogEntryMatcher_test.cpp", "tests/LogEvent_test.cpp", + "tests/log_event/LogEventQueue_test.cpp", "tests/MetricsManager_test.cpp", "tests/StatsLogProcessor_test.cpp", "tests/StatsService_test.cpp", diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index a6699e7e61528a8debab1d85bb38f1493ed1c0f3..286e76ec108a760dcddec904556b039047be620b 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -407,12 +407,12 @@ void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTim outData->clear(); outData->resize(proto.size()); size_t pos = 0; - auto iter = proto.data(); - while (iter.readBuffer() != NULL) { - size_t toRead = iter.currentToRead(); - std::memcpy(&((*outData)[pos]), iter.readBuffer(), toRead); + sp reader = proto.data(); + while (reader->readBuffer() != NULL) { + size_t toRead = reader->currentToRead(); + std::memcpy(&((*outData)[pos]), reader->readBuffer(), toRead); pos += toRead; - iter.rp()->move(toRead); + reader->move(toRead); } } diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index f78ae38aabd8ddacab7b19233578f48e70213023..3bcebd9e3f766a744b0a6b8da4e26423d7658913 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -50,6 +50,7 @@ using android::base::StringPrintf; using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_INT64; using android::util::FIELD_TYPE_MESSAGE; +using android::util::ProtoReader; namespace android { namespace os { @@ -64,8 +65,6 @@ constexpr const char* kOpUsage = "android:get_usage_stats"; // for StatsDataDumpProto const int FIELD_ID_REPORTS_LIST = 1; -// for TrainInfo experiment id serialization -const int FIELD_ID_EXPERIMENT_ID = 1; static binder::Status ok() { return binder::Status::ok(); @@ -131,34 +130,36 @@ binder::Status checkDumpAndUsageStats(const String16& packageName) { } \ } -StatsService::StatsService(const sp& handlerLooper) - : mAnomalyAlarmMonitor(new AlarmMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, - [](const sp& sc, int64_t timeMillis) { - if (sc != nullptr) { - sc->setAnomalyAlarm(timeMillis); - StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); - } - }, - [](const sp& sc) { - if (sc != nullptr) { - sc->cancelAnomalyAlarm(); - StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); - } - })), - mPeriodicAlarmMonitor(new AlarmMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, - [](const sp& sc, int64_t timeMillis) { - if (sc != nullptr) { - sc->setAlarmForSubscriberTriggering(timeMillis); - StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged(); - } - }, - [](const sp& sc) { - if (sc != nullptr) { - sc->cancelAlarmForSubscriberTriggering(); - StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged(); - } - - })) { +StatsService::StatsService(const sp& handlerLooper, shared_ptr queue) + : mAnomalyAlarmMonitor(new AlarmMonitor( + MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, + [](const sp& sc, int64_t timeMillis) { + if (sc != nullptr) { + sc->setAnomalyAlarm(timeMillis); + StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); + } + }, + [](const sp& sc) { + if (sc != nullptr) { + sc->cancelAnomalyAlarm(); + StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged(); + } + })), + mPeriodicAlarmMonitor(new AlarmMonitor( + MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS, + [](const sp& sc, int64_t timeMillis) { + if (sc != nullptr) { + sc->setAlarmForSubscriberTriggering(timeMillis); + StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged(); + } + }, + [](const sp& sc) { + if (sc != nullptr) { + sc->cancelAlarmForSubscriberTriggering(); + StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged(); + } + })), + mEventQueue(queue) { mUidMap = UidMap::getInstance(); mPullerManager = new StatsPullerManager(); StatsPuller::SetUidMap(mUidMap); @@ -200,11 +201,33 @@ StatsService::StatsService(const sp& handlerLooper) mConfigManager->AddListener(mProcessor); init_system_properties(); + + if (mEventQueue != nullptr) { + std::thread pushedEventThread([this] { readLogs(); }); + pushedEventThread.detach(); + } } StatsService::~StatsService() { } +/* Runs on a dedicated thread to process pushed events. */ +void StatsService::readLogs() { + // Read forever..... long live statsd + while (1) { + // Block until an event is available. + auto event = mEventQueue->waitPop(); + // Pass it to StatsLogProcess to all configs/metrics + // At this point, the LogEventQueue is not blocked, so that the socketListener + // can read events from the socket and write to buffer to avoid data drop. + mProcessor->OnLogEvent(event.get()); + // The ShellSubscriber is only used by shell for local debugging. + if (mShellSubscriber != nullptr) { + mShellSubscriber->onLogEvent(*event); + } + } +} + void StatsService::init_system_properties() { mEngBuild = false; const prop_info* buildType = __system_property_find("ro.build.type"); @@ -1008,6 +1031,7 @@ void StatsService::Terminate() { } } +// Test only interface!!! void StatsService::OnLogEvent(LogEvent* event) { mProcessor->OnLogEvent(event); if (mShellSubscriber != nullptr) { @@ -1180,7 +1204,7 @@ Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& p Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainName, int64_t trainVersionCode, int options, int32_t state, - const std::vector& experimentIds) { + const std::vector& experimentIdsIn) { uid_t uid = IPCThreadState::self()->getCallingUid(); // For testing if (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL) { @@ -1200,7 +1224,7 @@ Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& tra bool readTrainInfoSuccess = false; InstallTrainInfo trainInfo; - if (trainVersionCode == -1 || experimentIds.empty() || trainName.size() == 0) { + if (trainVersionCode == -1 || experimentIdsIn.empty() || trainName.size() == 0) { readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfo); } @@ -1208,27 +1232,19 @@ Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& tra trainVersionCode = trainInfo.trainVersionCode; } - vector experimentIdsProtoBuffer; - if (readTrainInfoSuccess && experimentIds.empty()) { - experimentIdsProtoBuffer = trainInfo.experimentIds; + // Find the right experiment IDs + std::vector experimentIds; + if (readTrainInfoSuccess && experimentIdsIn.empty()) { + experimentIds = trainInfo.experimentIds; } else { - ProtoOutputStream proto; - for (const auto& expId : experimentIds) { - proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID, - (long long)expId); - } - - experimentIdsProtoBuffer.resize(proto.size()); - size_t pos = 0; - auto iter = proto.data(); - while (iter.readBuffer() != NULL) { - size_t toRead = iter.currentToRead(); - std::memcpy(&(experimentIdsProtoBuffer[pos]), iter.readBuffer(), toRead); - pos += toRead; - iter.rp()->move(toRead); - } + experimentIds = experimentIdsIn; } + // Flatten the experiment IDs to proto + vector experimentIdsProtoBuffer; + writeExperimentIdsToProto(experimentIds, &experimentIdsProtoBuffer); + + // Find the right train name std::string trainNameUtf8; if (readTrainInfoSuccess && trainName.size() == 0) { trainNameUtf8 = trainInfo.trainName; @@ -1243,7 +1259,34 @@ Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& tra LogEvent event(trainNameUtf8, trainVersionCode, requiresStaging, rollbackEnabled, requiresLowLatencyMonitor, state, experimentIdsProtoBuffer, userId); mProcessor->OnLogEvent(&event); - StorageManager::writeTrainInfo(trainVersionCode, trainNameUtf8, state, experimentIdsProtoBuffer); + StorageManager::writeTrainInfo(trainVersionCode, trainNameUtf8, state, experimentIds); + return Status::ok(); +} + +Status StatsService::getRegisteredExperimentIds(std::vector* experimentIdsOut) { + uid_t uid = IPCThreadState::self()->getCallingUid(); + + // Caller must be granted these permissions + if (!checkCallingPermission(String16(kPermissionDump))) { + return exception(binder::Status::EX_SECURITY, + StringPrintf("UID %d lacks permission %s", uid, kPermissionDump)); + } + if (!checkCallingPermission(String16(kPermissionUsage))) { + return exception(binder::Status::EX_SECURITY, + StringPrintf("UID %d lacks permission %s", uid, kPermissionUsage)); + } + // TODO: add verifier permission + + // Read the latest train info + InstallTrainInfo trainInfo; + if (!StorageManager::readTrainInfo(trainInfo)) { + // No train info means no experiment IDs, return an empty list + experimentIdsOut->clear(); + return Status::ok(); + } + + // Copy the experiment IDs to the out vector + experimentIdsOut->assign(trainInfo.experimentIds.begin(), trainInfo.experimentIds.end()); return Status::ok(); } diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index d24565a630544425fa17498e73fba6bc4ad3978c..929d260dcd95e62dcaa543c7b568e1c211edfa55 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -22,7 +22,7 @@ #include "anomaly/AlarmMonitor.h" #include "config/ConfigManager.h" #include "external/StatsPullerManager.h" -#include "logd/LogListener.h" +#include "logd/LogEventQueue.h" #include "packages/UidMap.h" #include "shell/ShellSubscriber.h" #include "statscompanion_util.h" @@ -52,11 +52,10 @@ namespace statsd { using android::hardware::Return; class StatsService : public BnStatsManager, - public LogListener, public IStats, public IBinder::DeathRecipient { public: - StatsService(const sp& handlerLooper); + StatsService(const sp& handlerLooper, std::shared_ptr queue); virtual ~StatsService(); /** The anomaly alarm registered with AlarmManager won't be updated by less than this. */ @@ -92,7 +91,7 @@ public: void Terminate(); /** - * Called by LogReader when there's a log event to process. + * Test ONLY interface. In real world, StatsService reads from LogEventQueue. */ virtual void OnLogEvent(LogEvent* event); @@ -193,6 +192,11 @@ public: const android::String16& trainName, int64_t trainVersionCode, int options, int32_t state, const std::vector& experimentIds) override; + /** + * Binder call to get registered experiment IDs. + */ + virtual Status getRegisteredExperimentIds(std::vector* expIdsOut); + /** * Binder call to get SpeakerImpedance atom. */ @@ -278,6 +282,9 @@ private: */ void print_cmd_help(int out); + /* Runs on its dedicated thread to process pushed stats event from socket. */ + void readLogs(); + /** * Trigger a broadcast. */ @@ -410,6 +417,8 @@ private: sp mShellSubscriber; + std::shared_ptr mEventQueue; + FRIEND_TEST(StatsServiceTest, TestAddConfig_simple); FRIEND_TEST(StatsServiceTest, TestAddConfig_empty); FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index c2b81e44e5d92754d0af258cac4408dfc90fd3da..d34b077b638a3fd25c94680c0183763a80f183d7 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -41,6 +41,7 @@ import "frameworks/base/core/proto/android/server/location/enums.proto"; import "frameworks/base/core/proto/android/service/procstats_enum.proto"; import "frameworks/base/core/proto/android/service/usb.proto"; import "frameworks/base/core/proto/android/stats/connectivity/network_stack.proto"; +import "frameworks/base/core/proto/android/stats/connectivity/resolv_stats.proto"; import "frameworks/base/core/proto/android/stats/enums.proto"; import "frameworks/base/core/proto/android/stats/docsui/docsui_enums.proto"; import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto"; @@ -187,7 +188,7 @@ message Atom { WifiEnabledStateChanged wifi_enabled_state_changed = 113; WifiRunningStateChanged wifi_running_state_changed = 114; AppCompacted app_compacted = 115; - NetworkDnsEventReported network_dns_event_reported = 116; + NetworkDnsEventReported network_dns_event_reported = 116 [(log_from_module) = "resolv"]; DocsUIPickerLaunchedFromReported docs_ui_picker_launched_from_reported = 117; DocsUIPickResultReported docs_ui_pick_result_reported = 118; DocsUISearchModeReported docs_ui_search_mode_reported = 119; @@ -254,10 +255,12 @@ message Atom { PrivacyIndicatorsInteracted privacy_indicators_interacted = 180; AppInstallOnExternalStorageReported app_install_on_external_storage_reported = 181; NetworkStackReported network_stack_reported = 182; + AppMovedStorageReported app_moved_storage_reported = 183; + BiometricEnrolled biometric_enrolled = 184; } // Pulled events will start at field 10000. - // Next: 10057 + // Next: 10058 oneof pulled { WifiBytesTransfer wifi_bytes_transfer = 10000; WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001; @@ -316,6 +319,7 @@ message Atom { GpuStatsGlobalInfo gpu_stats_global_info = 10054; GpuStatsAppInfo gpu_stats_app_info = 10055; SystemIonHeapSize system_ion_heap_size = 10056; + AppsOnExternalStorageInfo apps_on_external_storage_info = 10057; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -3152,6 +3156,23 @@ message BiometricSystemHealthIssueDetected { optional android.hardware.biometrics.IssueEnum issue = 2; } +/** + * Logs when a biometric enrollment occurs. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/biometrics + */ +message BiometricEnrolled { + // Biometric modality that was used. + optional android.hardware.biometrics.ModalityEnum modality = 1; + // The associated user. Eg: 0 for owners, 10+ for others. Defined in android/os/UserHandle.java + optional int32 user = 2; + // The amount of time the enrollment took in milliseconds. + optional int64 latency_millis = 3; + // Whether or not the enrollment was successful. + optional bool success = 4; +} + message Notification { // Type of notification event. @@ -3278,6 +3299,9 @@ message BinaryPushStateChanged { INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_INITIATED = 18; INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_SUCCESS = 19; INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_FAILURE = 20; + INSTALL_STAGED_CANCEL_REQUESTED = 21; + INSTALL_STAGED_CANCEL_SUCCESS = 22; + INSTALL_STAGED_CANCEL_FAILURE = 23; } optional State state = 6; // Possible experiment ids for monitoring this push. @@ -3445,6 +3469,30 @@ message PrivacyIndicatorsInteracted { optional string package_name = 2; } +/** + * Logs information about a package that is moved from the internal to external storage and vice + * versa. + * It logs the package name, the type of the external storage where the package is installed + * (if moved to external storage, or UNKNOWN if moved to internal storage), + * and the move type: if it's from internal to external or the other way around. + * + * Logged from: + frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java + */ +message AppMovedStorageReported { + enum MoveType { + UNKNOWN = 0; + TO_EXTERNAL = 1; + TO_INTERNAL = 2; + } + // The type of the external storage. + optional android.stats.storage.ExternalStorageType external_storage_type = 1; + // The type of move. + optional MoveType move_type = 2; + // The name of the package that was moved. + optional string package_name = 3; +} + ////////////////////////////////////////////////////////////////////// // Pulled atoms below this line // ////////////////////////////////////////////////////////////////////// @@ -4949,53 +4997,39 @@ message AppCompacted { } /** - * Logs the latency period(in microseconds) and the return code of - * the DNS(Domain Name System) lookups. - * These 4 methods(GETADDRINFO,GETHOSTBYNAME,GETHOSTBYADDR,RES_NSEND) - * to get info(address or hostname) from DNS server(or DNS cache). - * Logged from: - * /system/netd/server/DnsProxyListener.cpp + * Logs a DNS lookup operation initiated by the system resolver on behalf of an application + * invoking native APIs such as getaddrinfo() or Java APIs such as Network#getAllByName(). + * + * The top-level message represents the entire lookup operation, which may result one or more + * queries to the recursive DNS resolvers. Those are individually logged in DnsQueryEvent to + * enable computing error rates and network latency and timeouts broken up by query type, + * transport, network interface, etc. */ message NetworkDnsEventReported { - // The types of the DNS lookups, as defined in - //system/netd/server/binder/android/net/metrics/INetdEventListener.aidl - enum EventType { - EVENT_UNKNOWN = 0; - EVENT_GETADDRINFO = 1; - EVENT_GETHOSTBYNAME = 2; - EVENT_GETHOSTBYADDR = 3; - EVENT_RES_NSEND = 4; - } - optional EventType event_type = 1; - // The return value of the DNS resolver for each DNS lookups. - //bionic/libc/include/netdb.h - //system/netd/resolv/include/netd_resolv/resolv.h - enum ReturnCode { - EAI_NO_ERROR = 0; - EAI_ADDRFAMILY = 1; - EAI_AGAIN = 2; - EAI_BADFLAGS = 3; - EAI_FAIL = 4; - EAI_FAMILY = 5; - EAI_MEMORY = 6; - EAI_NODATA = 7; - EAI_NONAME = 8; - EAI_SERVICE = 9; - EAI_SOCKTYPE = 10; - EAI_SYSTEM = 11; - EAI_BADHINTS = 12; - EAI_PROTOCOL = 13; - EAI_OVERFLOW = 14; - RESOLV_TIMEOUT = 255; - EAI_MAX = 256; - } - optional ReturnCode return_code = 2; + optional android.stats.connectivity.EventType event_type = 1; - // The latency period(in microseconds) it took for this DNS lookup to complete. + optional android.stats.connectivity.ReturnCode return_code = 2; + + // The latency in microseconds of the entire DNS lookup operation. optional int32 latency_micros = 3; + + optional android.stats.connectivity.DnsQueryEventRe dns_query_event_re = 4 [(log_mode) = MODE_BYTES]; + + // ResNSend flags defined in android/multinetwork.h + optional int32 flags = 5; + + optional android.net.NetworkCapabilitiesProto.Transport network_type = 6; + + // The DNS over TLS mode on a specific netId. + optional android.stats.connectivity.PrivateDnsModes private_dns_modes = 7; + + // Additional pass-through fields opaque to statsd. + // The DNS resolver Mainline module can add new fields here without requiring an OS update. + optional android.stats.connectivity.DnsCallEvent dns_call_event = 8 [(log_mode) = MODE_BYTES]; } + /** * Logs when a data stall event occurs. * @@ -5556,6 +5590,9 @@ message BubbleUIChanged { // Normalized screen position of the bubble stack. The range is between 0 and 1. optional float normalized_x_position = 7; optional float normalized_y_position = 8; + + // Whether the bubble is unread. If it is unread, a dot is shown in the bubble stack icon. + optional bool is_unread = 9; } /** @@ -5738,6 +5775,9 @@ message TrainInfo { INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_INITIATED = 18; INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_SUCCESS = 19; INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_FAILURE = 20; + INSTALL_STAGED_CANCEL_REQUESTED = 21; + INSTALL_STAGED_CANCEL_SUCCESS = 22; + INSTALL_STAGED_CANCEL_FAILURE = 23; } optional Status status = 4; } @@ -5863,6 +5903,20 @@ message SystemIonHeapSize { * frameworks/base/packages/NetworkStack/ */ message NetworkStackReported { - optional int32 eventId = 1; + // The id that indicates the event reported from NetworkStack. + optional int32 event_id = 1; + // The data for the reported events. optional android.stats.connectivity.NetworkStackEventData network_stack_event = 2 [(log_mode) = MODE_BYTES]; } + +/** + * Logs the apps that are installed on the external storage. + * Pulled from: + * StatsCompanionService + */ +message AppsOnExternalStorageInfo { + // The type of the external storage. + optional android.stats.storage.ExternalStorageType external_storage_type = 1; + // The name of the package that is installed on the external storage. + optional string package_name = 2; +} diff --git a/cmds/statsd/src/external/GpuStatsPuller.cpp b/cmds/statsd/src/external/GpuStatsPuller.cpp index 130bd85d56c6db8eb8c53819013115f073dc28d8..3fa932fddd047a0f07c600f466b422c15d7fef46 100644 --- a/cmds/statsd/src/external/GpuStatsPuller.cpp +++ b/cmds/statsd/src/external/GpuStatsPuller.cpp @@ -29,6 +29,8 @@ namespace android { namespace os { namespace statsd { +using android::util::ProtoReader; + GpuStatsPuller::GpuStatsPuller(const int tagId) : StatsPuller(tagId) { } @@ -116,11 +118,11 @@ static std::string protoOutputStreamToByteString(ProtoOutputStream& proto) { if (!proto.size()) return ""; std::string byteString; - auto iter = proto.data(); - while (iter.readBuffer() != nullptr) { - const size_t toRead = iter.currentToRead(); - byteString.append((char*)iter.readBuffer(), toRead); - iter.rp()->move(toRead); + sp reader = proto.data(); + while (reader->readBuffer() != nullptr) { + const size_t toRead = reader->currentToRead(); + byteString.append((char*)reader->readBuffer(), toRead); + reader->move(toRead); } if (byteString.size() != proto.size()) return ""; diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 2abfc2450a0031590a09f6b101428408146dca01..13eee5da52cfa47b325bff37468e317c44000514 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -251,6 +251,9 @@ std::map StatsPullerManager::kAllPullAtomInfo = { // GpuStatsAppInfo {android::util::GPU_STATS_APP_INFO, {.puller = new GpuStatsPuller(android::util::GPU_STATS_APP_INFO)}}, + // AppsOnExternalStorageInfo + {android::util::APPS_ON_EXTERNAL_STORAGE_INFO, + {.puller = new StatsCompanionServicePuller(android::util::APPS_ON_EXTERNAL_STORAGE_INFO)}}, }; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index 9a00637ee8346e047aaf618890dd0bf94e148a06..74a4c87af7c343bfb6411279537931b054b0ff82 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -50,6 +50,7 @@ const int FIELD_ID_ANOMALY_ALARM_STATS = 9; const int FIELD_ID_PERIODIC_ALARM_STATS = 12; const int FIELD_ID_SYSTEM_SERVER_RESTART = 15; const int FIELD_ID_LOGGER_ERROR_STATS = 16; +const int FIELD_ID_OVERFLOW = 18; const int FIELD_ID_ATOM_STATS_TAG = 1; const int FIELD_ID_ATOM_STATS_COUNT = 2; @@ -60,6 +61,13 @@ const int FIELD_ID_PERIODIC_ALARMS_REGISTERED = 1; const int FIELD_ID_LOG_LOSS_STATS_TIME = 1; const int FIELD_ID_LOG_LOSS_STATS_COUNT = 2; const int FIELD_ID_LOG_LOSS_STATS_ERROR = 3; +const int FIELD_ID_LOG_LOSS_STATS_TAG = 4; +const int FIELD_ID_LOG_LOSS_STATS_UID = 5; +const int FIELD_ID_LOG_LOSS_STATS_PID = 6; + +const int FIELD_ID_OVERFLOW_COUNT = 1; +const int FIELD_ID_OVERFLOW_MAX_HISTORY = 2; +const int FIELD_ID_OVERFLOW_MIN_HISTORY = 3; const int FIELD_ID_CONFIG_STATS_UID = 1; const int FIELD_ID_CONFIG_STATS_ID = 2; @@ -183,12 +191,13 @@ void StatsdStats::noteConfigReset(const ConfigKey& key) { noteConfigResetInternalLocked(key); } -void StatsdStats::noteLogLost(int32_t wallClockTimeSec, int32_t count, int32_t lastError) { +void StatsdStats::noteLogLost(int32_t wallClockTimeSec, int32_t count, int32_t lastError, + int32_t lastTag, int32_t uid, int32_t pid) { lock_guard lock(mLock); if (mLogLossStats.size() == kMaxLoggerErrors) { mLogLossStats.pop_front(); } - mLogLossStats.emplace_back(wallClockTimeSec, count, lastError); + mLogLossStats.emplace_back(wallClockTimeSec, count, lastError, lastTag, uid, pid); } void StatsdStats::noteBroadcastSent(const ConfigKey& key) { @@ -231,6 +240,22 @@ void StatsdStats::noteDataDropped(const ConfigKey& key, const size_t totalBytes) noteDataDropped(key, totalBytes, getWallClockSec()); } +void StatsdStats::noteEventQueueOverflow(int64_t oldestEventTimestampNs) { + lock_guard lock(mLock); + + mOverflowCount++; + + int64_t history = getElapsedRealtimeNs() - oldestEventTimestampNs; + + if (history > mMaxQueueHistoryNs) { + mMaxQueueHistoryNs = history; + } + + if (history < mMinQueueHistoryNs) { + mMinQueueHistoryNs = history; + } +} + void StatsdStats::noteDataDropped(const ConfigKey& key, const size_t totalBytes, int32_t timeSec) { lock_guard lock(mLock); auto it = mConfigStats.find(key); @@ -530,6 +555,9 @@ void StatsdStats::resetInternalLocked() { mPeriodicAlarmRegisteredStats = 0; mSystemServerRestartSec.clear(); mLogLossStats.clear(); + mOverflowCount = 0; + mMinQueueHistoryNs = kInt64Max; + mMaxQueueHistoryNs = 0; for (auto& config : mConfigStats) { config.second->broadcast_sent_time_sec.clear(); config.second->activation_time_sec.clear(); @@ -716,9 +744,15 @@ void StatsdStats::dumpStats(int out) const { } for (const auto& loss : mLogLossStats) { - dprintf(out, "Log loss: %lld (wall clock sec) - %d (count) %d (last error)\n", - (long long)loss.mWallClockSec, loss.mCount, loss.mLastError); + dprintf(out, + "Log loss: %lld (wall clock sec) - %d (count), %d (last error), %d (last tag), %d " + "(uid), %d (pid)\n", + (long long)loss.mWallClockSec, loss.mCount, loss.mLastError, loss.mLastTag, + loss.mUid, loss.mPid); } + + dprintf(out, "Event queue overflow: %d; MaxHistoryNs: %lld; MinHistoryNs: %lld\n", + mOverflowCount, (long long)mMaxQueueHistoryNs, (long long)mMinQueueHistoryNs); } void addConfigStatsToProto(const ConfigStats& configStats, ProtoOutputStream* proto) { @@ -891,6 +925,19 @@ void StatsdStats::dumpStats(std::vector* output, bool reset) { proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_TIME, error.mWallClockSec); proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_COUNT, error.mCount); proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_ERROR, error.mLastError); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_TAG, error.mLastTag); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_UID, error.mUid); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_LOG_LOSS_STATS_PID, error.mPid); + proto.end(token); + } + + if (mOverflowCount > 0) { + uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_OVERFLOW); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_OVERFLOW_COUNT, (int32_t)mOverflowCount); + proto.write(FIELD_TYPE_INT64 | FIELD_ID_OVERFLOW_MAX_HISTORY, + (long long)mMaxQueueHistoryNs); + proto.write(FIELD_TYPE_INT64 | FIELD_ID_OVERFLOW_MIN_HISTORY, + (long long)mMinQueueHistoryNs); proto.end(token); } @@ -904,12 +951,12 @@ void StatsdStats::dumpStats(std::vector* output, bool reset) { output->resize(bufferSize); size_t pos = 0; - auto it = proto.data(); - while (it.readBuffer() != NULL) { - size_t toRead = it.currentToRead(); - std::memcpy(&((*output)[pos]), it.readBuffer(), toRead); + sp reader = proto.data(); + while (reader->readBuffer() != NULL) { + size_t toRead = reader->currentToRead(); + std::memcpy(&((*output)[pos]), reader->readBuffer(), toRead); pos += toRead; - it.rp()->move(toRead); + reader->move(toRead); } if (reset) { diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 7c2d8462c4e4ec52c9dda8dab54272fb9575ee1e..88ecccc32407ae7e01cd28b91b28ff846858463c 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -160,6 +160,8 @@ public: // Max platform atom tag number. static const int32_t kMaxPlatformAtomTag = 100000; + static const int64_t kInt64Max = 0x7fffffffffffffffLL; + /** * Report a new config has been received and report the static stats about the config. * @@ -336,7 +338,8 @@ public: /** * Records statsd skipped an event. */ - void noteLogLost(int32_t wallClockTimeSec, int32_t count, int lastError); + void noteLogLost(int32_t wallClockTimeSec, int32_t count, int32_t lastError, + int32_t lastAtomTag, int32_t uid, int32_t pid); /** * Records that the pull of an atom has failed @@ -418,6 +421,10 @@ public: */ void noteBucketUnknownCondition(int64_t metricId); + /* Reports one event has been dropped due to queue overflow, and the oldest event timestamp in + * the queue */ + void noteEventQueueOverflow(int64_t oldestEventTimestampNs); + /** * Reset the historical stats. Including all stats in icebox, and the tracked stats about * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue @@ -503,15 +510,35 @@ private: std::map mAtomMetricStats; struct LogLossStats { - LogLossStats(int32_t sec, int32_t count, int32_t error) - : mWallClockSec(sec), mCount(count), mLastError(error) { + LogLossStats(int32_t sec, int32_t count, int32_t error, int32_t tag, int32_t uid, + int32_t pid) + : mWallClockSec(sec), + mCount(count), + mLastError(error), + mLastTag(tag), + mUid(uid), + mPid(pid) { } int32_t mWallClockSec; int32_t mCount; // error code defined in linux/errno.h int32_t mLastError; + int32_t mLastTag; + int32_t mUid; + int32_t mPid; }; + // Max of {(now - oldestEventTimestamp) when overflow happens}. + // This number is helpful to understand how SLOW statsd can be. + int64_t mMaxQueueHistoryNs = 0; + + // Min of {(now - oldestEventTimestamp) when overflow happens}. + // This number is helpful to understand how FAST the events floods to statsd. + int64_t mMinQueueHistoryNs = kInt64Max; + + // Total number of events that are lost due to queue overflow. + int32_t mOverflowCount = 0; + // Timestamps when we detect log loss, and the number of logs lost. std::list mLogLossStats; diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index d9f5415463e334d4dd39eb90c87bb0777486be6f..ca874b57170e8b30dc92ac8a00b845b30f5c507a 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -27,6 +27,9 @@ namespace android { namespace os { namespace statsd { +// for TrainInfo experiment id serialization +const int FIELD_ID_EXPERIMENT_ID = 1; + using namespace android::util; using android::util::ProtoOutputStream; using std::string; @@ -118,6 +121,7 @@ void LogEvent::createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper, LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) { mLogdTimestampNs = wallClockTimestampNs; + mElapsedTimestampNs = elapsedTimestampNs; mTagId = tagId; mLogUid = 0; mContext = create_android_logger(1937006964); // the event tag shared by all stats logs @@ -241,12 +245,15 @@ LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, mValues.push_back( FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode))); - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainInfo.experimentIds))); + std::vector experimentIdsProto; + writeExperimentIdsToProto(trainInfo.experimentIds, &experimentIdsProto); + mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(experimentIdsProto))); mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value(trainInfo.trainName))); mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value(trainInfo.status))); } -LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) : LogEvent(tagId, timestampNs, 0) {} +LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) : LogEvent(tagId, timestampNs, timestampNs) { +} LogEvent::LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid) { mLogdTimestampNs = timestampNs; @@ -671,6 +678,24 @@ void LogEvent::ToProto(ProtoOutputStream& protoOutput) const { writeFieldValueTreeToStream(mTagId, getValues(), &protoOutput); } +void writeExperimentIdsToProto(const std::vector& experimentIds, std::vector* protoOut) { + ProtoOutputStream proto; + for (const auto& expId : experimentIds) { + proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID, + (long long)expId); + } + + protoOut->resize(proto.size()); + size_t pos = 0; + sp reader = proto.data(); + while (reader->readBuffer() != NULL) { + size_t toRead = reader->currentToRead(); + std::memcpy(protoOut->data() + pos, reader->readBuffer(), toRead); + pos += toRead; + reader->move(toRead); + } +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 753a9a500c57616ba058478a8e73179b52937a56..531ce299beefd09c72815182e267cb9a55db323e 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -60,8 +60,9 @@ struct InstallTrainInfo { int64_t trainVersionCode; std::string trainName; int32_t status; - std::vector experimentIds; + std::vector experimentIds; }; + /** * Wrapper for the log_msg structure. */ @@ -239,6 +240,8 @@ private: uint32_t mLogUid; }; +void writeExperimentIdsToProto(const std::vector& experimentIds, std::vector* protoOut); + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/logd/LogEventQueue.cpp b/cmds/statsd/src/logd/LogEventQueue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..146464bbe774869be307318f2d0a6587fc58c3cb --- /dev/null +++ b/cmds/statsd/src/logd/LogEventQueue.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define DEBUG false // STOPSHIP if true +#include "Log.h" + +#include "LogEventQueue.h" + +namespace android { +namespace os { +namespace statsd { + +using std::unique_lock; +using std::unique_ptr; + +unique_ptr LogEventQueue::waitPop() { + std::unique_lock lock(mMutex); + + if (mQueue.empty()) { + mCondition.wait(lock, [this] { return !this->mQueue.empty(); }); + } + + unique_ptr item = std::move(mQueue.front()); + mQueue.pop(); + + return item; +} + +bool LogEventQueue::push(unique_ptr item, int64_t* oldestTimestampNs) { + bool success; + { + std::unique_lock lock(mMutex); + if (mQueue.size() < mQueueLimit) { + mQueue.push(std::move(item)); + success = true; + } else { + // safe operation as queue must not be empty. + *oldestTimestampNs = mQueue.front()->GetElapsedTimestampNs(); + success = false; + } + } + + mCondition.notify_one(); + return success; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/logd/LogEventQueue.h b/cmds/statsd/src/logd/LogEventQueue.h new file mode 100644 index 0000000000000000000000000000000000000000..b4fd63f119e6074c8218d02cf5f93c5cb6941b14 --- /dev/null +++ b/cmds/statsd/src/logd/LogEventQueue.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "LogEvent.h" + +#include +#include +#include +#include +#include + +namespace android { +namespace os { +namespace statsd { + +/** + * A zero copy thread safe queue buffer for producing and consuming LogEvent. + */ +class LogEventQueue { +public: + explicit LogEventQueue(size_t maxSize) : mQueueLimit(maxSize){}; + + /** + * Blocking read one event from the queue. + */ + std::unique_ptr waitPop(); + + /** + * Puts a LogEvent ptr to the end of the queue. + * Returns false on failure when the queue is full, and output the oldest event timestamp + * in the queue. + */ + bool push(std::unique_ptr event, int64_t* oldestTimestampNs); + +private: + const size_t mQueueLimit; + std::condition_variable mCondition; + std::mutex mMutex; + std::queue> mQueue; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp index eddc86eca798a83af873311c2bab823d0876e2f9..68082c2dc4d2f42ec61db994a164be4f09e28e37 100644 --- a/cmds/statsd/src/main.cpp +++ b/cmds/statsd/src/main.cpp @@ -80,8 +80,11 @@ int main(int /*argc*/, char** /*argv*/) { ::android::hardware::configureRpcThreadpool(1 /*threads*/, false /*willJoin*/); + std::shared_ptr eventQueue = + std::make_shared(2000 /*buffer limit. Buffer is NOT pre-allocated*/); + // Create the service - gStatsService = new StatsService(looper); + gStatsService = new StatsService(looper, eventQueue); if (defaultServiceManager()->addService(String16("stats"), gStatsService, false, IServiceManager::DUMP_FLAG_PRIORITY_NORMAL | IServiceManager::DUMP_FLAG_PROTO) != 0) { @@ -101,13 +104,13 @@ int main(int /*argc*/, char** /*argv*/) { gStatsService->Startup(); - sp socketListener = new StatsSocketListener(gStatsService); + sp socketListener = new StatsSocketListener(eventQueue); - ALOGI("using statsd socket"); - // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value - if (socketListener->startListener(600)) { - exit(1); - } + ALOGI("Statsd starts to listen to socket."); + // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value + if (socketListener->startListener(600)) { + exit(1); + } // Loop forever -- the reports run on this thread in a handler, and the // binder calls remain responsive in their pool of one thread. diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 5435c8420519e6169c18f01056d2429aef8d0495..69816cbbd92f7362dd4b05d4dc19c77722315e12 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -90,12 +90,12 @@ std::unique_ptr> serializeProtoLocked(ProtoOutputStream& pr std::unique_ptr> buffer(new std::vector(bufferSize)); size_t pos = 0; - auto it = protoOutput.data(); - while (it.readBuffer() != NULL) { - size_t toRead = it.currentToRead(); - std::memcpy(&((*buffer)[pos]), it.readBuffer(), toRead); + sp reader = protoOutput.data(); + while (reader->readBuffer() != NULL) { + size_t toRead = reader->currentToRead(); + std::memcpy(&((*buffer)[pos]), reader->readBuffer(), toRead); pos += toRead; - it.rp()->move(toRead); + reader->move(toRead); } return buffer; diff --git a/cmds/statsd/src/socket/StatsSocketListener.cpp b/cmds/statsd/src/socket/StatsSocketListener.cpp index 6bb8cda072815510f3ee082a5e71af215c20ec14..92200f99c3ccb4d55ddd6daa08d43becfbabf031 100755 --- a/cmds/statsd/src/socket/StatsSocketListener.cpp +++ b/cmds/statsd/src/socket/StatsSocketListener.cpp @@ -41,8 +41,8 @@ namespace statsd { static const int kLogMsgHeaderSize = 28; -StatsSocketListener::StatsSocketListener(const sp& listener) - : SocketListener(getLogSocket(), false /*start listen*/), mListener(listener) { +StatsSocketListener::StatsSocketListener(std::shared_ptr queue) + : SocketListener(getLogSocket(), false /*start listen*/), mQueue(queue) { } StatsSocketListener::~StatsSocketListener() { @@ -106,13 +106,21 @@ bool StatsSocketListener::onDataAvailable(SocketClient* cli) { // Note that all normal stats logs are in the format of event_list, so there won't be confusion. // // TODO(b/80538532): In addition to log it in StatsdStats, we should properly reset the config. - if (n == sizeof(android_log_event_int_t)) { - android_log_event_int_t* int_event = reinterpret_cast(ptr); - if (int_event->payload.type == EVENT_TYPE_INT) { - ALOGE("Found dropped events: %d error %d", int_event->payload.data, - int_event->header.tag); - StatsdStats::getInstance().noteLogLost((int32_t)getWallClockSec(), - int_event->payload.data, int_event->header.tag); + if (n == sizeof(android_log_event_long_t)) { + android_log_event_long_t* long_event = reinterpret_cast(ptr); + if (long_event->payload.type == EVENT_TYPE_LONG) { + int64_t composed_long = long_event->payload.data; + + // format: + // |last_tag|dropped_count| + int32_t dropped_count = (int32_t)(0xffffffff & composed_long); + int32_t last_atom_tag = (int32_t)((0xffffffff00000000 & (uint64_t)composed_long) >> 32); + + ALOGE("Found dropped events: %d error %d last atom tag %d from uid %d", dropped_count, + long_event->header.tag, last_atom_tag, cred->uid); + StatsdStats::getInstance().noteLogLost((int32_t)getWallClockSec(), dropped_count, + long_event->header.tag, last_atom_tag, cred->uid, + cred->pid); return true; } } @@ -126,10 +134,11 @@ bool StatsSocketListener::onDataAvailable(SocketClient* cli) { msg.entry.uid = cred->uid; memcpy(msg.buf + kLogMsgHeaderSize, ptr, n + 1); - LogEvent event(msg); - // Call the listener - mListener->OnLogEvent(&event); + int64_t oldestTimestamp; + if (!mQueue->push(std::make_unique(msg), &oldestTimestamp)) { + StatsdStats::getInstance().noteEventQueueOverflow(oldestTimestamp); + } return true; } diff --git a/cmds/statsd/src/socket/StatsSocketListener.h b/cmds/statsd/src/socket/StatsSocketListener.h index b8185d2a32d22ebd82e95e84cae09d3b1545c959..2167a56445b9cad988eda04cbe1bd493929edd13 100644 --- a/cmds/statsd/src/socket/StatsSocketListener.h +++ b/cmds/statsd/src/socket/StatsSocketListener.h @@ -17,7 +17,7 @@ #include #include -#include "logd/LogListener.h" +#include "logd/LogEventQueue.h" // DEFAULT_OVERFLOWUID is defined in linux/highuid.h, which is not part of // the uapi headers for userspace to use. This value is filled in on the @@ -35,7 +35,7 @@ namespace statsd { class StatsSocketListener : public SocketListener, public virtual android::RefBase { public: - explicit StatsSocketListener(const sp& listener); + explicit StatsSocketListener(std::shared_ptr queue); virtual ~StatsSocketListener(); @@ -47,7 +47,7 @@ private: /** * Who is going to get the events when they're read. */ - sp mListener; + std::shared_ptr mQueue; }; } // namespace statsd } // namespace os diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 166e087b69dc87f0baf7c1baada3cd225d679783..1dfc433cf0e2d13fc7a6347dc645d5ce429b090f 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -455,8 +455,19 @@ message StatsdStatsReport { optional int32 detected_time_sec = 1; optional int32 count = 2; optional int32 last_error = 3; + optional int32 last_tag = 4; + optional int32 uid = 5; + optional int32 pid = 6; } repeated LogLossStats detected_log_loss = 16; + + message EventQueueOverflow { + optional int32 count = 1; + optional int64 max_queue_history_ns = 2; + optional int64 min_queue_history_ns = 3; + } + + optional EventQueueOverflow queue_overflow = 18; } message AlertTriggerDetails { diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h index 59d4865f8977b746634e2759e77bcffabf084d16..cdef87451f63130b62ca3c6928d180dfa50d757b 100644 --- a/cmds/statsd/src/stats_log_util.h +++ b/cmds/statsd/src/stats_log_util.h @@ -80,11 +80,11 @@ void writeAtomMetricStatsToStream(const std::pair bool parseProtoOutputStream(util::ProtoOutputStream& protoOutput, T* message) { std::string pbBytes; - auto iter = protoOutput.data(); - while (iter.readBuffer() != NULL) { - size_t toRead = iter.currentToRead(); - pbBytes.append(reinterpret_cast(iter.readBuffer()), toRead); - iter.rp()->move(toRead); + sp reader = protoOutput.data(); + while (reader->readBuffer() != NULL) { + size_t toRead = reader->currentToRead(); + pbBytes.append(reinterpret_cast(reader->readBuffer()), toRead); + reader->move(toRead); } return message->ParseFromArray(pbBytes.c_str(), pbBytes.size()); } diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index 65b183c6fc96c842f250cdc6a3ae7024e3373f9d..cf8b97494a067a921c3d056696f28cc71c92587e 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -36,9 +36,17 @@ using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_MESSAGE; using std::map; +/** + * NOTE: these directories are protected by SELinux, any changes here must also update + * the SELinux policies. + */ #define STATS_DATA_DIR "/data/misc/stats-data" #define STATS_SERVICE_DIR "/data/misc/stats-service" #define TRAIN_INFO_DIR "/data/misc/train-info" +#define TRAIN_INFO_PATH "/data/misc/train-info/train-info.bin" + +// Magic word at the start of the train info file, change this if changing the file format +const uint32_t TRAIN_INFO_FILE_MAGIC = 0xff7447ff; // for ConfigMetricsReportList const int FIELD_ID_REPORTS = 2; @@ -96,27 +104,42 @@ void StorageManager::writeFile(const char* file, const void* buffer, int numByte } bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string& trainName, - int32_t status, const std::vector& experimentIds) { + int32_t status, const std::vector& experimentIds) { std::lock_guard lock(sTrainInfoMutex); deleteAllFiles(TRAIN_INFO_DIR); - string file_name = StringPrintf("%s/%lld", TRAIN_INFO_DIR, (long long)trainVersionCode); - - int fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); + int fd = open(TRAIN_INFO_PATH, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); if (fd == -1) { - VLOG("Attempt to access %s but failed", file_name.c_str()); + VLOG("Attempt to access %s but failed", TRAIN_INFO_PATH); return false; } size_t result; + // Write the magic word + result = write(fd, &TRAIN_INFO_FILE_MAGIC, sizeof(TRAIN_INFO_FILE_MAGIC)); + if (result != sizeof(TRAIN_INFO_FILE_MAGIC)) { + VLOG("Failed to wrtie train info magic"); + close(fd); + return false; + } + + // Write the train version + const size_t trainVersionCodeByteCount = sizeof(trainVersionCode); + result = write(fd, &trainVersionCode, trainVersionCodeByteCount); + if (result != trainVersionCodeByteCount) { + VLOG("Failed to wrtie train version code"); + close(fd); + return false; + } + // Write # of bytes in trainName to file const size_t trainNameSize = trainName.size(); const size_t trainNameSizeByteCount = sizeof(trainNameSize); result = write(fd, (uint8_t*)&trainNameSize, trainNameSizeByteCount); if (result != trainNameSizeByteCount) { - VLOG("Failed to write train name size for %s", file_name.c_str()); + VLOG("Failed to write train name size"); close(fd); return false; } @@ -124,7 +147,7 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string& // Write trainName to file result = write(fd, trainName.c_str(), trainNameSize); if (result != trainNameSize) { - VLOG("Failed to write train name for%s", file_name.c_str()); + VLOG("Failed to write train name"); close(fd); return false; } @@ -133,34 +156,38 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string& const size_t statusByteCount = sizeof(status); result = write(fd, (uint8_t*)&status, statusByteCount); if (result != statusByteCount) { - VLOG("Failed to write status for %s", file_name.c_str()); + VLOG("Failed to write status"); close(fd); return false; } - // Write experiment id size to file. - const size_t experimentIdSize = experimentIds.size(); - const size_t experimentIdsSizeByteCount = sizeof(experimentIdSize); - result = write(fd, (uint8_t*) &experimentIdSize, experimentIdsSizeByteCount); - if (result != experimentIdsSizeByteCount) { - VLOG("Failed to write experiment id size for %s", file_name.c_str()); + // Write experiment id count to file. + const size_t experimentIdsCount = experimentIds.size(); + const size_t experimentIdsCountByteCount = sizeof(experimentIdsCount); + result = write(fd, (uint8_t*) &experimentIdsCount, experimentIdsCountByteCount); + if (result != experimentIdsCountByteCount) { + VLOG("Failed to write experiment id count"); close(fd); return false; } // Write experimentIds to file - result = write(fd, experimentIds.data(), experimentIds.size()); - if (result == experimentIds.size()) { - VLOG("Successfully wrote %s", file_name.c_str()); - } else { - VLOG("Failed to write experiment ids for %s", file_name.c_str()); - close(fd); - return false; + for (size_t i = 0; i < experimentIdsCount; i++) { + const int64_t experimentId = experimentIds[i]; + const size_t experimentIdByteCount = sizeof(experimentId); + result = write(fd, &experimentId, experimentIdByteCount); + if (result == experimentIdByteCount) { + VLOG("Successfully wrote experiment IDs"); + } else { + VLOG("Failed to write experiment ids"); + close(fd); + return false; + } } result = fchown(fd, AID_STATSD, AID_STATSD); if (result) { - VLOG("Failed to chown %s to statsd", file_name.c_str()); + VLOG("Failed to chown train info file to statsd"); close(fd); return false; } @@ -172,88 +199,96 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string& bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) { std::lock_guard lock(sTrainInfoMutex); - unique_ptr dir(opendir(TRAIN_INFO_DIR), closedir); - - if (dir == NULL) { - VLOG("Directory does not exist: %s", TRAIN_INFO_DIR); + int fd = open(TRAIN_INFO_PATH, O_RDONLY | O_CLOEXEC); + if (fd == -1) { + VLOG("Failed to open train-info.bin"); return false; } - dirent* de; - while ((de = readdir(dir.get()))) { - char* name = de->d_name; - if (name[0] == '.') { - continue; - } - - size_t result; + // Read the magic word + uint32_t magic; + size_t result = read(fd, &magic, sizeof(magic)); + if (result != sizeof(magic)) { + VLOG("Failed to read train info magic"); + close(fd); + return false; + } - trainInfo.trainVersionCode = StrToInt64(name); - string fullPath = StringPrintf("%s/%s", TRAIN_INFO_DIR, name); - int fd = open(fullPath.c_str(), O_RDONLY | O_CLOEXEC); - if (fd == -1) { - return false; - } + if (magic != TRAIN_INFO_FILE_MAGIC) { + VLOG("Train info magic was 0x%08x, expected 0x%08x", magic, TRAIN_INFO_FILE_MAGIC); + close(fd); + return false; + } - // Read # of bytes taken by trainName in the file. - size_t trainNameSize; - result = read(fd, &trainNameSize, sizeof(size_t)); - if (result != sizeof(size_t)) { - VLOG("Failed to read train name size from file %s", fullPath.c_str()); - close(fd); - return false; - } + // Read the train version code + const size_t trainVersionCodeByteCount(sizeof(trainInfo.trainVersionCode)); + result = read(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount); + if (result != trainVersionCodeByteCount) { + VLOG("Failed to read train version code from train info file"); + close(fd); + return false; + } - // Read trainName - trainInfo.trainName.resize(trainNameSize); - result = read(fd, trainInfo.trainName.data(), trainNameSize); - if (result != trainNameSize) { - VLOG("Failed to read train name from file %s", fullPath.c_str()); - close(fd); - return false; - } + // Read # of bytes taken by trainName in the file. + size_t trainNameSize; + result = read(fd, &trainNameSize, sizeof(size_t)); + if (result != sizeof(size_t)) { + VLOG("Failed to read train name size from train info file"); + close(fd); + return false; + } - // Read status - const size_t statusByteCount = sizeof(trainInfo.status); - result = read(fd, &trainInfo.status, statusByteCount); - if (result != statusByteCount) { - VLOG("Failed to read train status from file %s", fullPath.c_str()); - close(fd); - return false; - } + // Read trainName + trainInfo.trainName.resize(trainNameSize); + result = read(fd, trainInfo.trainName.data(), trainNameSize); + if (result != trainNameSize) { + VLOG("Failed to read train name from train info file"); + close(fd); + return false; + } - // Read experiment ids size. - size_t experimentIdSize; - result = read(fd, &experimentIdSize, sizeof(size_t)); - if (result != sizeof(size_t)) { - VLOG("Failed to read train experiment id size from file %s", fullPath.c_str()); - close(fd); - return false; - } + // Read status + const size_t statusByteCount = sizeof(trainInfo.status); + result = read(fd, &trainInfo.status, statusByteCount); + if (result != statusByteCount) { + VLOG("Failed to read train status from train info file"); + close(fd); + return false; + } - // Read experimentIds - trainInfo.experimentIds.resize(experimentIdSize); - result = read(fd, trainInfo.experimentIds.data(), experimentIdSize); - if (result != experimentIdSize) { - VLOG("Failed to read train experiment ids from file %s", fullPath.c_str()); - close(fd); - return false; - } + // Read experiment ids count. + size_t experimentIdsCount; + result = read(fd, &experimentIdsCount, sizeof(size_t)); + if (result != sizeof(size_t)) { + VLOG("Failed to read train experiment id count from train info file"); + close(fd); + return false; + } - // Expect to be at EOF. - char c; - result = read(fd, &c, 1); - if (result != 0) { - VLOG("Failed to read train info from file %s. Did not get expected EOF.", fullPath.c_str()); + // Read experimentIds + for (size_t i = 0; i < experimentIdsCount; i++) { + int64_t experimentId; + result = read(fd, &experimentId, sizeof(experimentId)); + if (result != sizeof(experimentId)) { + VLOG("Failed to read train experiment id from train info file"); close(fd); return false; } + trainInfo.experimentIds.push_back(experimentId); + } - VLOG("Read train info file successful: %s", fullPath.c_str()); + // Expect to be at EOF. + char c; + result = read(fd, &c, 1); + if (result != 0) { + VLOG("Failed to read train info from file. Did not get expected EOF."); close(fd); - return true; + return false; } - return false; + + VLOG("Read train info file successful"); + close(fd); + return true; } void StorageManager::deleteFile(const char* file) { diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h index 88280cf218b31723b727756a355123a7cf867394..dfcea65b0872a8ac7ae93232fa31013fb6545887 100644 --- a/cmds/statsd/src/storage/StorageManager.h +++ b/cmds/statsd/src/storage/StorageManager.h @@ -29,11 +29,6 @@ namespace statsd { using android::util::ProtoOutputStream; -struct TrainInfo { - int64_t trainVersionCode; - std::vector experimentIds; -}; - class StorageManager : public virtual RefBase { public: /** @@ -45,7 +40,7 @@ public: * Writes train info. */ static bool writeTrainInfo(int64_t trainVersionCode, const std::string& trainName, - int32_t status, const std::vector& experimentIds); + int32_t status, const std::vector& experimentIds); /** * Reads train info. diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp index 7c2d2420528c79469ff719e0eb3d06a5e8d3841e..ff1cb4ff1450559c0e917d1f614ad83a676d3b2d 100644 --- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp +++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp @@ -120,12 +120,12 @@ void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensio protoData->resize(headerProto.size()); size_t pos = 0; - auto iter = headerProto.data(); - while (iter.readBuffer() != NULL) { - size_t toRead = iter.currentToRead(); - std::memcpy(&((*protoData)[pos]), iter.readBuffer(), toRead); + sp reader = headerProto.data(); + while (reader->readBuffer() != NULL) { + size_t toRead = reader->currentToRead(); + std::memcpy(&((*protoData)[pos]), reader->readBuffer(), toRead); pos += toRead; - iter.rp()->move(toRead); + reader->move(toRead); } } } // namespace @@ -152,15 +152,15 @@ bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int uint8_t dest; switch (config.dest()) { case IncidentdDetails_Destination_AUTOMATIC: - dest = android::os::DEST_AUTOMATIC; + dest = android::os::PRIVACY_POLICY_AUTOMATIC; break; case IncidentdDetails_Destination_EXPLICIT: - dest = android::os::DEST_EXPLICIT; + dest = android::os::PRIVACY_POLICY_EXPLICIT; break; default: - dest = android::os::DEST_AUTOMATIC; + dest = android::os::PRIVACY_POLICY_AUTOMATIC; } - incidentReport.setDest(dest); + incidentReport.setPrivacyPolicy(dest); incidentReport.setReceiverPkg(config.receiver_pkg()); diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp index a9305accb1be19a69f3bc07bfba9b52110146a4e..f1cad92c336b2ab3842be30cbc20c4e100688813 100644 --- a/cmds/statsd/tests/FieldValue_test.cpp +++ b/cmds/statsd/tests/FieldValue_test.cpp @@ -24,6 +24,8 @@ #ifdef __ANDROID__ +using android::util::ProtoReader; + namespace android { namespace os { namespace statsd { @@ -252,12 +254,12 @@ TEST(AtomMatcherTest, TestWriteDimensionPath) { vector outData; outData.resize(protoOut.size()); size_t pos = 0; - auto iter = protoOut.data(); - while (iter.readBuffer() != NULL) { - size_t toRead = iter.currentToRead(); - std::memcpy(&(outData[pos]), iter.readBuffer(), toRead); + sp reader = protoOut.data(); + while (reader->readBuffer() != NULL) { + size_t toRead = reader->currentToRead(); + std::memcpy(&(outData[pos]), reader->readBuffer(), toRead); pos += toRead; - iter.rp()->move(toRead); + reader->move(toRead); } DimensionsValue result; @@ -343,12 +345,12 @@ TEST(AtomMatcherTest, TestWriteDimensionToProto) { vector outData; outData.resize(protoOut.size()); size_t pos = 0; - auto iter = protoOut.data(); - while (iter.readBuffer() != NULL) { - size_t toRead = iter.currentToRead(); - std::memcpy(&(outData[pos]), iter.readBuffer(), toRead); + sp reader = protoOut.data(); + while (reader->readBuffer() != NULL) { + size_t toRead = reader->currentToRead(); + std::memcpy(&(outData[pos]), reader->readBuffer(), toRead); pos += toRead; - iter.rp()->move(toRead); + reader->move(toRead); } DimensionsValue result; @@ -405,12 +407,12 @@ TEST(AtomMatcherTest, TestWriteDimensionLeafNodesToProto) { vector outData; outData.resize(protoOut.size()); size_t pos = 0; - auto iter = protoOut.data(); - while (iter.readBuffer() != NULL) { - size_t toRead = iter.currentToRead(); - std::memcpy(&(outData[pos]), iter.readBuffer(), toRead); + sp reader = protoOut.data(); + while (reader->readBuffer() != NULL) { + size_t toRead = reader->currentToRead(); + std::memcpy(&(outData[pos]), reader->readBuffer(), toRead); pos += toRead; - iter.rp()->move(toRead); + reader->move(toRead); } DimensionsValueTuple result; @@ -458,12 +460,12 @@ TEST(AtomMatcherTest, TestWriteAtomToProto) { vector outData; outData.resize(protoOutput.size()); size_t pos = 0; - auto iter = protoOutput.data(); - while (iter.readBuffer() != NULL) { - size_t toRead = iter.currentToRead(); - std::memcpy(&(outData[pos]), iter.readBuffer(), toRead); + sp reader = protoOutput.data(); + while (reader->readBuffer() != NULL) { + size_t toRead = reader->currentToRead(); + std::memcpy(&(outData[pos]), reader->readBuffer(), toRead); pos += toRead; - iter.rp()->move(toRead); + reader->move(toRead); } Atom result; diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp index eec3c735057c6c88a80fcd1fe5c3ea8104507569..504ee22f72e457812d09f09928d1168d6e08e30c 100644 --- a/cmds/statsd/tests/LogEvent_test.cpp +++ b/cmds/statsd/tests/LogEvent_test.cpp @@ -26,6 +26,7 @@ namespace statsd { using std::string; using util::ProtoOutputStream; +using util::ProtoReader; TEST(LogEventTest, TestLogParsing) { LogEvent event1(1, 2000); @@ -590,12 +591,12 @@ TEST(LogEventTest, TestBinaryFieldAtom) { std::vector outData; outData.resize(proto.size()); size_t pos = 0; - auto iter = proto.data(); - while (iter.readBuffer() != NULL) { - size_t toRead = iter.currentToRead(); - std::memcpy(&(outData[pos]), iter.readBuffer(), toRead); + sp reader = proto.data(); + while (reader->readBuffer() != NULL) { + size_t toRead = reader->currentToRead(); + std::memcpy(&(outData[pos]), reader->readBuffer(), toRead); pos += toRead; - iter.rp()->move(toRead); + reader->move(toRead); } std::string result_str(outData.begin(), outData.end()); @@ -629,12 +630,12 @@ TEST(LogEventTest, TestBinaryFieldAtom_empty) { std::vector outData; outData.resize(proto.size()); size_t pos = 0; - auto iter = proto.data(); - while (iter.readBuffer() != NULL) { - size_t toRead = iter.currentToRead(); - std::memcpy(&(outData[pos]), iter.readBuffer(), toRead); + sp reader = proto.data(); + while (reader->readBuffer() != NULL) { + size_t toRead = reader->currentToRead(); + std::memcpy(&(outData[pos]), reader->readBuffer(), toRead); pos += toRead; - iter.rp()->move(toRead); + reader->move(toRead); } std::string result_str(outData.begin(), outData.end()); @@ -644,6 +645,22 @@ TEST(LogEventTest, TestBinaryFieldAtom_empty) { EXPECT_EQ(orig_str, result_str); } +TEST(LogEventTest, TestWriteExperimentIdsToProto) { + std::vector expIds; + expIds.push_back(5038); + std::vector proto; + + writeExperimentIdsToProto(expIds, &proto); + + EXPECT_EQ(proto.size(), 3); + // Proto wire format for field ID 1, varint + EXPECT_EQ(proto[0], 0x08); + // varint of 5038, 2 bytes long + EXPECT_EQ(proto[1], 0xae); + EXPECT_EQ(proto[2], 0x27); +} + + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/tests/StatsService_test.cpp b/cmds/statsd/tests/StatsService_test.cpp index 560fb9f02174294cfcd2d8f6607aaf27a1d01579..7c00531d560b1527cd71b0a3ec69782400b0cba1 100644 --- a/cmds/statsd/tests/StatsService_test.cpp +++ b/cmds/statsd/tests/StatsService_test.cpp @@ -33,7 +33,7 @@ using android::util::ProtoOutputStream; #ifdef __ANDROID__ TEST(StatsServiceTest, TestAddConfig_simple) { - StatsService service(nullptr); + StatsService service(nullptr, nullptr); StatsdConfig config; config.set_id(12345); string serialized = config.SerializeAsString(); @@ -43,7 +43,7 @@ TEST(StatsServiceTest, TestAddConfig_simple) { } TEST(StatsServiceTest, TestAddConfig_empty) { - StatsService service(nullptr); + StatsService service(nullptr, nullptr); string serialized = ""; EXPECT_TRUE( @@ -51,7 +51,7 @@ TEST(StatsServiceTest, TestAddConfig_empty) { } TEST(StatsServiceTest, TestAddConfig_invalid) { - StatsService service(nullptr); + StatsService service(nullptr, nullptr); string serialized = "Invalid config!"; EXPECT_FALSE( @@ -69,7 +69,7 @@ TEST(StatsServiceTest, TestGetUidFromArgs) { int32_t uid; - StatsService service(nullptr); + StatsService service(nullptr, nullptr); service.mEngBuild = true; // "-1" diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp index c04a40cfebd94ad643e2765f8b67fbab94c2608e..d9fa4e99d54d85b855683b33d47960c703a06f98 100644 --- a/cmds/statsd/tests/UidMap_test.cpp +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -33,6 +33,7 @@ namespace os { namespace statsd { using android::util::ProtoOutputStream; +using android::util::ProtoReader; #ifdef __ANDROID__ const string kApp1 = "app1.sharing.1"; @@ -179,12 +180,12 @@ static void protoOutputStreamToUidMapping(ProtoOutputStream* proto, UidMapping* vector bytes; bytes.resize(proto->size()); size_t pos = 0; - auto iter = proto->data(); - while (iter.readBuffer() != NULL) { - size_t toRead = iter.currentToRead(); - std::memcpy(&((bytes)[pos]), iter.readBuffer(), toRead); + sp reader = proto->data(); + while (reader->readBuffer() != NULL) { + size_t toRead = reader->currentToRead(); + std::memcpy(&((bytes)[pos]), reader->readBuffer(), toRead); pos += toRead; - iter.rp()->move(toRead); + reader->move(toRead); } results->ParseFromArray(bytes.data(), bytes.size()); } diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp index 3dff7f5d1320e1adacc038baa8ba4bf799a67e1c..309d251e4a890b1b70264d6a4c6241a8dabba660 100644 --- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp @@ -111,7 +111,7 @@ StatsdConfig MakeGaugeMetricConfig(int64_t minTime) { } TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) { - StatsService service(nullptr); + StatsService service(nullptr, nullptr); SendConfig(service, MakeConfig()); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. @@ -126,7 +126,7 @@ TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) { } TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) { - StatsService service(nullptr); + StatsService service(nullptr, nullptr); SendConfig(service, MakeConfig()); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. @@ -146,7 +146,7 @@ TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) { } TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) { - StatsService service(nullptr); + StatsService service(nullptr, nullptr); SendConfig(service, MakeConfig()); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. @@ -171,7 +171,7 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) { } TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) { - StatsService service(nullptr); + StatsService service(nullptr, nullptr); SendConfig(service, MakeConfig()); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. @@ -195,7 +195,7 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) { } TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) { - StatsService service(nullptr); + StatsService service(nullptr, nullptr); // Partial buckets don't occur when app is first installed. service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16("")); SendConfig(service, MakeValueMetricConfig(0)); @@ -213,7 +213,7 @@ TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) { } TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) { - StatsService service(nullptr); + StatsService service(nullptr, nullptr); // Partial buckets don't occur when app is first installed. service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16("")); SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */)); @@ -237,7 +237,7 @@ TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) { } TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) { - StatsService service(nullptr); + StatsService service(nullptr, nullptr); // Partial buckets don't occur when app is first installed. service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16("")); SendConfig(service, MakeGaugeMetricConfig(0)); @@ -255,7 +255,7 @@ TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) { } TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) { - StatsService service(nullptr); + StatsService service(nullptr, nullptr); // Partial buckets don't occur when app is first installed. service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16("")); SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */)); diff --git a/cmds/statsd/tests/external/IncidentReportArgs_test.cpp b/cmds/statsd/tests/external/IncidentReportArgs_test.cpp index c170b12dc24285915a0553b09a053de32c86495a..38bc19452afa4c64307b0b62093ed49fb3c36d37 100644 --- a/cmds/statsd/tests/external/IncidentReportArgs_test.cpp +++ b/cmds/statsd/tests/external/IncidentReportArgs_test.cpp @@ -36,7 +36,7 @@ TEST(IncidentReportArgsTest, testSerialization) { args.addHeader(header1); args.addHeader(header2); - args.setDest(1); + args.setPrivacyPolicy(1); args.setReceiverPkg("com.android.os"); args.setReceiverCls("com.android.os.Receiver"); @@ -56,10 +56,10 @@ TEST(IncidentReportArgsTest, testSerialization) { sections.insert(1000); sections.insert(1001); EXPECT_EQ(sections, args2.sections()); - EXPECT_EQ(1, args2.dest()); + EXPECT_EQ(1, args2.getPrivacyPolicy()); - EXPECT_EQ(String16("com.android.os"), args2.receiverPkg()); - EXPECT_EQ(String16("com.android.os.Receiver"), args2.receiverCls()); + EXPECT_EQ(string("com.android.os"), args2.receiverPkg()); + EXPECT_EQ(string("com.android.os.Receiver"), args2.receiverCls()); vector> headers; headers.push_back(header1); @@ -69,4 +69,4 @@ TEST(IncidentReportArgsTest, testSerialization) { } // namespace statsd } // namespace os -} // namespace android \ No newline at end of file +} // namespace android diff --git a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f27d12957f1111075fa9b989f9c69b0a5329e1a5 --- /dev/null +++ b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp @@ -0,0 +1,100 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 "logd/LogEventQueue.h" + +#include +#include +#include + +#include + +namespace android { +namespace os { +namespace statsd { + +using namespace android; +using namespace testing; + +using std::unique_ptr; + +#ifdef __ANDROID__ +TEST(LogEventQueue_test, TestGoodConsumer) { + LogEventQueue queue(50); + int64_t timeBaseNs = 100; + std::thread writer([&queue, timeBaseNs] { + for (int i = 0; i < 100; i++) { + int64_t oldestEventNs; + bool success = queue.push(std::make_unique(10, timeBaseNs + i * 1000), + &oldestEventNs); + EXPECT_TRUE(success); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + }); + + std::thread reader([&queue, timeBaseNs] { + for (int i = 0; i < 100; i++) { + auto event = queue.waitPop(); + EXPECT_TRUE(event != nullptr); + // All events are in right order. + EXPECT_EQ(timeBaseNs + i * 1000, event->GetElapsedTimestampNs()); + } + }); + + reader.join(); + writer.join(); +} + +TEST(LogEventQueue_test, TestSlowConsumer) { + LogEventQueue queue(50); + int64_t timeBaseNs = 100; + std::thread writer([&queue, timeBaseNs] { + int failure_count = 0; + int64_t oldestEventNs; + for (int i = 0; i < 100; i++) { + bool success = queue.push(std::make_unique(10, timeBaseNs + i * 1000), + &oldestEventNs); + if (!success) failure_count++; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + // There is some remote chance that reader thread not get chance to run before writer thread + // ends. That's why the following comparison is not "==". + // There will be at least 45 events lost due to overflow. + EXPECT_TRUE(failure_count >= 45); + // The oldest event must be at least the 6th event. + EXPECT_TRUE(oldestEventNs <= (100 + 5 * 1000)); + }); + + std::thread reader([&queue, timeBaseNs] { + // The consumer quickly processed 5 events, then it got stuck (not reading anymore). + for (int i = 0; i < 5; i++) { + auto event = queue.waitPop(); + EXPECT_TRUE(event != nullptr); + // All events are in right order. + EXPECT_EQ(timeBaseNs + i * 1000, event->GetElapsedTimestampNs()); + } + }); + + reader.join(); + writer.join(); +} + +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 90b9e812182dd70a7187c095625511cdaf437b88..afa05a93c55a9a7fd940648f040ecd62213bacc0 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -26,6 +26,7 @@ using namespace testing; using android::sp; +using android::util::ProtoReader; using std::make_shared; using std::set; using std::shared_ptr; @@ -2730,12 +2731,12 @@ static StatsLogReport outputStreamToProto(ProtoOutputStream* proto) { vector bytes; bytes.resize(proto->size()); size_t pos = 0; - auto iter = proto->data(); - while (iter.readBuffer() != NULL) { - size_t toRead = iter.currentToRead(); - std::memcpy(&((bytes)[pos]), iter.readBuffer(), toRead); + sp reader = proto->data(); + while (reader->readBuffer() != NULL) { + size_t toRead = reader->currentToRead(); + std::memcpy(&((bytes)[pos]), reader->readBuffer(), toRead); pos += toRead; - iter.rp()->move(toRead); + reader->move(toRead); } StatsLogReport report; diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 023371d3b443afe20014a50c268cde8acd391b9d..80b6349ca2844b82587ee476527d20b9b550cd4a 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -4037,7 +4037,7 @@ public class ActivityManager { * continues running even if the process is killed and restarted. To remove the watch, * use {@link #clearWatchHeapLimit()}. * - *

This API only work if the calling process has been marked as + *

This API only works if the calling process has been marked as * {@link ApplicationInfo#FLAG_DEBUGGABLE} or this is running on a debuggable * (userdebug or eng) build.

* diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 08239a1e7ed9c55bd3bd972d851d661312f35a3a..b2b1e775e94a8a3bb0fd7be4709b6ab67a3e169e 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2100,6 +2100,16 @@ public final class ActivityThread extends ClientTransactionHandler { public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, int flags, int userId) { final boolean differentUser = (UserHandle.myUserId() != userId); + ApplicationInfo ai; + try { + ai = getPackageManager().getApplicationInfo(packageName, + PackageManager.GET_SHARED_LIBRARY_FILES + | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, + (userId < 0) ? UserHandle.myUserId() : userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + synchronized (mResourcesManager) { WeakReference ref; if (differentUser) { @@ -2112,11 +2122,7 @@ public final class ActivityThread extends ClientTransactionHandler { } 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 (ai != null && packageInfo != null && isLoadedApkUpToDate(packageInfo, ai)) { if (packageInfo.isSecurityViolation() && (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) { throw new SecurityException( @@ -2129,16 +2135,6 @@ public final class ActivityThread extends ClientTransactionHandler { } } - ApplicationInfo ai = null; - try { - ai = getPackageManager().getApplicationInfo(packageName, - PackageManager.GET_SHARED_LIBRARY_FILES - | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, - (userId < 0) ? UserHandle.myUserId() : userId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - if (ai != null) { return getPackageInfo(ai, compatInfo, flags); } @@ -2209,37 +2205,59 @@ public final class ActivityThread extends ClientTransactionHandler { } LoadedApk packageInfo = ref != null ? ref.get() : null; - if (packageInfo == null || (packageInfo.mResources != null - && !packageInfo.mResources.getAssets().isUpToDate())) { - if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package " + + boolean isUpToDate = packageInfo != null && isLoadedApkUpToDate(packageInfo, aInfo); + + if (isUpToDate) { + return packageInfo; + } + + if (localLOGV) { + Slog.v(TAG, (includeCode ? "Loading code package " : "Loading resource-only package ") + aInfo.packageName + " (in " + (mBoundApplication != null - ? mBoundApplication.processName : null) + ? mBoundApplication.processName : null) + ")"); - packageInfo = + } + + packageInfo = new LoadedApk(this, aInfo, compatInfo, baseLoader, - securityViolation, includeCode && - (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage); + securityViolation, includeCode + && (aInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage); - if (mSystemThread && "android".equals(aInfo.packageName)) { - packageInfo.installSystemApplicationInfo(aInfo, - getSystemContext().mPackageInfo.getClassLoader()); - } + if (mSystemThread && "android".equals(aInfo.packageName)) { + packageInfo.installSystemApplicationInfo(aInfo, + getSystemContext().mPackageInfo.getClassLoader()); + } - if (differentUser) { - // Caching not supported across users - } else if (includeCode) { - mPackages.put(aInfo.packageName, - new WeakReference(packageInfo)); - } else { - mResourcePackages.put(aInfo.packageName, - new WeakReference(packageInfo)); - } + if (differentUser) { + // Caching not supported across users + } else if (includeCode) { + mPackages.put(aInfo.packageName, + new WeakReference(packageInfo)); + } else { + mResourcePackages.put(aInfo.packageName, + new WeakReference(packageInfo)); } + return packageInfo; } } + /** + * Compares overlay/resource directories for a LoadedApk to determine if it's up to date + * with the given ApplicationInfo. + */ + private boolean isLoadedApkUpToDate(LoadedApk loadedApk, ApplicationInfo appInfo) { + Resources packageResources = loadedApk.mResources; + String[] overlayDirs = ArrayUtils.defeatNullable(loadedApk.getOverlayDirs()); + String[] resourceDirs = ArrayUtils.defeatNullable(appInfo.resourceDirs); + + return (packageResources == null || packageResources.getAssets().isUpToDate()) + && overlayDirs.length == resourceDirs.length + && ArrayUtils.containsAll(overlayDirs, resourceDirs); + } + @UnsupportedAppUsage ActivityThread() { mResourcesManager = ResourcesManager.getInstance(); @@ -5434,19 +5452,25 @@ public final class ActivityThread extends ClientTransactionHandler { ref = mResourcePackages.get(ai.packageName); resApk = ref != null ? ref.get() : null; } + + final String[] oldResDirs = new String[2]; + if (apk != null) { + oldResDirs[0] = apk.getResDir(); final ArrayList oldPaths = new ArrayList<>(); LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths); apk.updateApplicationInfo(ai, oldPaths); } if (resApk != null) { + oldResDirs[1] = resApk.getResDir(); final ArrayList oldPaths = new ArrayList<>(); LoadedApk.makePaths(this, resApk.getApplicationInfo(), oldPaths); resApk.updateApplicationInfo(ai, oldPaths); } + synchronized (mResourcesManager) { // Update all affected Resources objects to use new ResourcesImpl - mResourcesManager.applyNewResourceDirsLocked(ai.sourceDir, ai.resourceDirs); + mResourcesManager.applyNewResourceDirsLocked(ai, oldResDirs); } ApplicationPackageManager.configurationChanged(); @@ -5699,9 +5723,17 @@ public final class ActivityThread extends ClientTransactionHandler { } } } + + final String[] oldResDirs = { pkgInfo.getResDir() }; + final ArrayList oldPaths = new ArrayList<>(); LoadedApk.makePaths(this, pkgInfo.getApplicationInfo(), oldPaths); pkgInfo.updateApplicationInfo(aInfo, oldPaths); + + synchronized (mResourcesManager) { + // Update affected Resources objects to use new ResourcesImpl + mResourcesManager.applyNewResourceDirsLocked(aInfo, oldResDirs); + } } catch (RemoteException e) { } } @@ -5853,6 +5885,11 @@ public final class ActivityThread extends ClientTransactionHandler { private void handleBindApplication(AppBindData data) { // Register the UI Thread as a sensitive thread to the runtime. VMRuntime.registerSensitiveThread(); + // In the case the stack depth property exists, pass it down to the runtime. + String property = SystemProperties.get("debug.allocTracker.stackDepth"); + if (property.length() != 0) { + VMDebug.setAllocTrackerStackDepth(Integer.parseInt(property)); + } if (data.trackAllocation) { DdmVmInternal.enableRecentAllocations(true); } @@ -6905,9 +6942,6 @@ public final class ActivityThread extends ClientTransactionHandler { // If feature is disabled, we don't need to install if (!DEPRECATE_DATA_COLUMNS) return; - // If app is modern enough, we don't need to install - if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) return; - // Install interception and make sure it sticks! Os def = null; do { diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 2ef085690f8f4f9e51281d85a104e5b994b92cfb..8ec5e3a432186a8459224655de01ab78953935f9 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -27,6 +27,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.Insets; +import android.graphics.Region; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.hardware.input.InputManager; @@ -46,6 +47,7 @@ import android.view.SurfaceSession; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; +import android.view.ViewParent; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -81,6 +83,9 @@ public class ActivityView extends ViewGroup { // Temp container to store view coordinates in window. private final int[] mLocationInWindow = new int[2]; + // The latest tap exclude region that we've sent to WM. + private final Region mTapExcludeRegion = new Region(); + private TaskStackListener mTaskStackListener; private final CloseGuard mGuard = CloseGuard.get(); @@ -279,11 +284,11 @@ public class ActivityView extends ViewGroup { } /** - * Triggers an update of {@link ActivityView}'s location in window to properly set touch exclude + * Triggers an update of {@link ActivityView}'s location in window to properly set tap exclude * regions and avoid focus switches by touches on this view. */ public void onLocationChanged() { - updateLocation(); + updateTapExcludeRegion(); } @Override @@ -291,15 +296,38 @@ public class ActivityView extends ViewGroup { mSurfaceView.layout(0 /* left */, 0 /* top */, r - l /* right */, b - t /* bottom */); } - /** Send current location and size to the WM to set tap exclude region for this view. */ - private void updateLocation() { + @Override + public boolean gatherTransparentRegion(Region region) { + // The tap exclude region may be affected by any view on top of it, so we detect the + // possible change by monitoring this function. + updateTapExcludeRegion(); + return super.gatherTransparentRegion(region); + } + + /** Compute and send current tap exclude region to WM for this view. */ + private void updateTapExcludeRegion() { if (!isAttachedToWindow()) { return; } + if (!canReceivePointerEvents()) { + cleanTapExcludeRegion(); + return; + } try { getLocationInWindow(mLocationInWindow); + final int x = mLocationInWindow[0]; + final int y = mLocationInWindow[1]; + mTapExcludeRegion.set(x, y, x + getWidth(), y + getHeight()); + + // There might be views on top of us. We need to subtract those areas from the tap + // exclude region. + final ViewParent parent = getParent(); + if (parent instanceof ViewGroup) { + ((ViewGroup) parent).subtractObscuredTouchableRegion(mTapExcludeRegion, this); + } + WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(), - mLocationInWindow[0], mLocationInWindow[1], getWidth(), getHeight()); + mTapExcludeRegion); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } @@ -322,7 +350,7 @@ public class ActivityView extends ViewGroup { mVirtualDisplay.setDisplayState(true); } - updateLocation(); + updateTapExcludeRegion(); } @Override @@ -330,7 +358,7 @@ public class ActivityView extends ViewGroup { if (mVirtualDisplay != null) { mVirtualDisplay.resize(width, height, getBaseDisplayDensity()); } - updateLocation(); + updateTapExcludeRegion(); } @Override @@ -460,13 +488,14 @@ public class ActivityView extends ViewGroup { /** Report to server that tap exclude region on hosting display should be cleared. */ private void cleanTapExcludeRegion() { - if (!isAttachedToWindow()) { + if (!isAttachedToWindow() || mTapExcludeRegion.isEmpty()) { return; } - // Update tap exclude region with an empty rect to clean the state on server. + // Update tap exclude region with a null region to clean the state on server. try { WindowManagerGlobal.getWindowSession().updateTapExcludeRegion(getWindow(), hashCode(), - 0 /* left */, 0 /* top */, 0 /* width */, 0 /* height */); + null /* region */); + mTapExcludeRegion.setEmpty(); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 5ed4428b729b435dccd2ed5e6b5364bf841fe095..4b0b8cb12d5e7dbf27bc966f55bd334db107f461 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -18,6 +18,7 @@ package android.app; import android.Manifest; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -1543,11 +1544,11 @@ public class AppOpsManager { Manifest.permission.USE_BIOMETRIC, Manifest.permission.ACTIVITY_RECOGNITION, Manifest.permission.SMS_FINANCIAL_TRANSACTIONS, - Manifest.permission.READ_MEDIA_AUDIO, + null, null, // no permission for OP_WRITE_MEDIA_AUDIO - Manifest.permission.READ_MEDIA_VIDEO, + null, null, // no permission for OP_WRITE_MEDIA_VIDEO - Manifest.permission.READ_MEDIA_IMAGES, + null, null, // no permission for OP_WRITE_MEDIA_IMAGES null, // no permission for OP_LEGACY_STORAGE null, // no permission for OP_ACCESS_ACCESSIBILITY @@ -3114,7 +3115,7 @@ public class AppOpsManager { * * @see #getUidOpsAt(int) */ - public int getUidCount() { + public @IntRange(from = 0) int getUidCount() { if (mHistoricalUidOps == null) { return 0; } @@ -3130,7 +3131,7 @@ public class AppOpsManager { * * @see #getUidCount() */ - public @NonNull HistoricalUidOps getUidOpsAt(int index) { + public @NonNull HistoricalUidOps getUidOpsAt(@IntRange(from = 0) int index) { if (mHistoricalUidOps == null) { throw new IndexOutOfBoundsException(); } @@ -3391,7 +3392,7 @@ public class AppOpsManager { * * @see #getPackageOpsAt(int) */ - public int getPackageCount() { + public @IntRange(from = 0) int getPackageCount() { if (mHistoricalPackageOps == null) { return 0; } @@ -3407,7 +3408,7 @@ public class AppOpsManager { * * @see #getPackageCount() */ - public @NonNull HistoricalPackageOps getPackageOpsAt(int index) { + public @NonNull HistoricalPackageOps getPackageOpsAt(@IntRange(from = 0) int index) { if (mHistoricalPackageOps == null) { throw new IndexOutOfBoundsException(); } @@ -3626,7 +3627,7 @@ public class AppOpsManager { * @return The number historical app ops. * @see #getOpAt(int) */ - public int getOpCount() { + public @IntRange(from = 0) int getOpCount() { if (mHistoricalOps == null) { return 0; } @@ -3640,7 +3641,7 @@ public class AppOpsManager { * @return The op at the given index. * @see #getOpCount() */ - public @NonNull HistoricalOp getOpAt(int index) { + public @NonNull HistoricalOp getOpAt(@IntRange(from = 0) int index) { if (mHistoricalOps == null) { throw new IndexOutOfBoundsException(); } diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java index 9ef24c6c2aeb480e60930350b8ff9f46316da5c2..faa30f3a98b89576166b0a8d8ca836175ad8e37d 100644 --- a/core/java/android/app/ApplicationLoaders.java +++ b/core/java/android/app/ApplicationLoaders.java @@ -17,20 +17,27 @@ package android.app; import android.annotation.UnsupportedAppUsage; +import android.content.pm.SharedLibraryInfo; import android.os.Build; import android.os.GraphicsEnvironment; import android.os.Trace; import android.util.ArrayMap; +import android.util.Log; import com.android.internal.os.ClassLoaderFactory; import dalvik.system.PathClassLoader; +import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** @hide */ public class ApplicationLoaders { + private static final String TAG = "ApplicationLoaders"; + @UnsupportedAppUsage public static ApplicationLoaders getDefault() { return gApplicationLoaders; @@ -54,6 +61,26 @@ public class ApplicationLoaders { libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries); } + /** + * Gets a class loader for a shared library. Additional dependent shared libraries are allowed + * to be specified (sharedLibraries). + * + * Additionally, as an optimization, this will return a pre-created ClassLoader if one has + * been cached by createAndCacheNonBootclasspathSystemClassLoaders. + */ + ClassLoader getSharedLibraryClassLoaderWithSharedLibraries(String zip, int targetSdkVersion, + boolean isBundled, String librarySearchPath, String libraryPermittedPath, + ClassLoader parent, String classLoaderName, List sharedLibraries) { + ClassLoader loader = getCachedNonBootclasspathSystemLib(zip, parent, classLoaderName, + sharedLibraries); + if (loader != null) { + return loader; + } + + return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled, + librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries); + } + private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled, String librarySearchPath, String libraryPermittedPath, ClassLoader parent, String cacheKey, @@ -95,7 +122,9 @@ public class ApplicationLoaders { classloader, librarySearchPath, libraryPermittedPath); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - mLoaders.put(cacheKey, classloader); + if (cacheKey != null) { + mLoaders.put(cacheKey, classloader); + } return classloader; } @@ -107,6 +136,112 @@ public class ApplicationLoaders { } } + /** + * Caches system library class loaders which are not on the bootclasspath but are still used + * by many system apps. + * + * All libraries in the closure of libraries to be loaded must be in libs. A library can + * only depend on libraries that come before it in the list. + */ + public void createAndCacheNonBootclasspathSystemClassLoaders(SharedLibraryInfo[] libs) { + if (mSystemLibsCacheMap != null) { + Log.wtf(TAG, "Already cached."); + return; + } + + mSystemLibsCacheMap = new HashMap(); + + for (SharedLibraryInfo lib : libs) { + createAndCacheNonBootclasspathSystemClassLoader(lib); + } + } + + /** + * Caches a single non-bootclasspath class loader. + * + * All of this library's dependencies must have previously been cached. + */ + private void createAndCacheNonBootclasspathSystemClassLoader(SharedLibraryInfo lib) { + String path = lib.getPath(); + List dependencies = lib.getDependencies(); + + // get cached classloaders for dependencies + ArrayList sharedLibraries = null; + if (dependencies != null) { + sharedLibraries = new ArrayList(dependencies.size()); + for (SharedLibraryInfo dependency : dependencies) { + String dependencyPath = dependency.getPath(); + CachedClassLoader cached = mSystemLibsCacheMap.get(dependencyPath); + + if (cached == null) { + Log.e(TAG, "Failed to find dependency " + dependencyPath + + " of cached library " + path); + return; + } + + sharedLibraries.add(cached.loader); + } + } + + // assume cached libraries work with current sdk since they are built-in + ClassLoader classLoader = getClassLoader(path, Build.VERSION.SDK_INT, true /*isBundled*/, + null /*librarySearchPath*/, null /*libraryPermittedPath*/, null /*parent*/, + null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/); + + if (classLoader == null) { + Log.e(TAG, "Failed to cache " + path); + return; + } + + CachedClassLoader cached = new CachedClassLoader(); + cached.loader = classLoader; + cached.sharedLibraries = sharedLibraries; + + Log.d(TAG, "Created zygote-cached class loader: " + path); + mSystemLibsCacheMap.put(path, cached); + } + + private static boolean sharedLibrariesEquals(List lhs, List rhs) { + if (lhs == null) { + return rhs == null; + } + + return lhs.equals(rhs); + } + + /** + * Returns lib cached with createAndCacheNonBootclasspathSystemClassLoader. This is called by + * the zygote during caching. + * + * If there is an error or the cache is not available, this returns null. + */ + private ClassLoader getCachedNonBootclasspathSystemLib(String zip, ClassLoader parent, + String classLoaderName, List sharedLibraries) { + if (mSystemLibsCacheMap == null) { + return null; + } + + // we only cache top-level libs with the default class loader + if (parent != null || classLoaderName != null) { + return null; + } + + CachedClassLoader cached = mSystemLibsCacheMap.get(zip); + if (cached == null) { + return null; + } + + // cached must be built and loaded in the same environment + if (!sharedLibrariesEquals(sharedLibraries, cached.sharedLibraries)) { + Log.w(TAG, "Unexpected environment for cached library: (" + sharedLibraries + "|" + + cached.sharedLibraries + ")"); + return null; + } + + Log.d(TAG, "Returning zygote-cached class loader: " + zip); + return cached.loader; + } + /** * Creates a classloader for the WebView APK and places it in the cache of loaders maintained * by this class. This is used in the WebView zygote, where its presence in the cache speeds up @@ -151,4 +286,18 @@ public class ApplicationLoaders { private final ArrayMap mLoaders = new ArrayMap<>(); private static final ApplicationLoaders gApplicationLoaders = new ApplicationLoaders(); + + private static class CachedClassLoader { + ClassLoader loader; + + /** + * The shared libraries used when constructing loader for verification. + */ + List sharedLibraries; + } + + /** + * This is a map of zip to associated class loader. + */ + private Map mSystemLibsCacheMap = null; } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 404e52011c39dfc55244f037a931b3b4e95969e3..a906790c45abcd88cf676ce8aedddbe7c20886f6 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -3048,6 +3048,15 @@ public class ApplicationPackageManager extends PackageManager { } } + @Override + public String getAttentionServicePackageName() { + try { + return mPM.getAttentionServicePackageName(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + @Override public String getWellbeingPackageName() { try { diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 11000df5b9936f515cc42ed6f6807cc07091bdbc..41a4fba0434cd2322e15d5bf92aa1a5cd67e290f 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2122,8 +2122,7 @@ class ContextImpl extends Context { } private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName, - int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo, - String[] overlayDirs) { + int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) { final String[] splitResDirs; final ClassLoader classLoader; try { @@ -2135,7 +2134,7 @@ class ContextImpl extends Context { return ResourcesManager.getInstance().getResources(activityToken, pi.getResDir(), splitResDirs, - overlayDirs, + pi.getOverlayDirs(), pi.getApplicationInfo().sharedLibraryFiles, displayId, overrideConfig, @@ -2153,11 +2152,9 @@ class ContextImpl extends Context { new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null); final int displayId = getDisplayId(); - // overlayDirs is retrieved directly from ApplicationInfo since ActivityThread may have - // a LoadedApk containing Resources with stale overlays for a remote application. - final String[] overlayDirs = application.resourceDirs; + c.setResources(createResources(mActivityToken, pi, null, displayId, null, - getDisplayAdjustments(displayId).getCompatibilityInfo(), overlayDirs)); + getDisplayAdjustments(displayId).getCompatibilityInfo())); if (c.mResources != null) { return c; } @@ -2192,7 +2189,7 @@ class ContextImpl extends Context { final int displayId = getDisplayId(); c.setResources(createResources(mActivityToken, pi, null, displayId, null, - getDisplayAdjustments(displayId).getCompatibilityInfo(), pi.getOverlayDirs())); + getDisplayAdjustments(displayId).getCompatibilityInfo())); if (c.mResources != null) { return c; } @@ -2242,8 +2239,7 @@ class ContextImpl extends Context { final int displayId = getDisplayId(); context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId, - overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(), - mPackageInfo.getOverlayDirs())); + overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo())); return context; } @@ -2258,8 +2254,7 @@ class ContextImpl extends Context { final int displayId = display.getDisplayId(); context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId, - null, getDisplayAdjustments(displayId).getCompatibilityInfo(), - mPackageInfo.getOverlayDirs())); + null, getDisplayAdjustments(displayId).getCompatibilityInfo())); context.mDisplay = display; return context; } @@ -2441,7 +2436,7 @@ class ContextImpl extends Context { ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null, null, null, 0, null, null); context.setResources(createResources(null, packageInfo, null, displayId, null, - packageInfo.getCompatibilityInfo(), packageInfo.getOverlayDirs())); + packageInfo.getCompatibilityInfo())); context.updateDisplay(displayId); return context; } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 16fe7dba0fbe280338daeb094bc87dcd86be49ee..65f10808534f6178346fb6815ad88a6a45ac240f 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -346,6 +346,9 @@ interface IActivityManager { void unregisterUserSwitchObserver(in IUserSwitchObserver observer); int[] getRunningUserIds(); + // Request a heap dump for the system server. + void requestSystemServerHeapDump(); + // Deprecated - This method is only used by a few internal components and it will soon be // replaced by a proper bug report API (which will be restricted to a few, pre-defined apps). // No new code should be calling it. diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java index b1541c6b8780d04ed9b2de0ca91984a0fac7fbd3..a7be5421adb2d87f293b1c6a02df9c8cc39f67c9 100644 --- a/core/java/android/app/InstantAppResolverService.java +++ b/core/java/android/app/InstantAppResolverService.java @@ -16,6 +16,8 @@ package android.app; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.Context; import android.content.Intent; @@ -61,8 +63,8 @@ public abstract class InstantAppResolverService extends Service { * String, InstantAppResolutionCallback)}. */ @Deprecated - public void onGetInstantAppResolveInfo( - int digestPrefix[], String token, InstantAppResolutionCallback callback) { + public void onGetInstantAppResolveInfo(@Nullable int[] digestPrefix, @NonNull String token, + @NonNull InstantAppResolutionCallback callback) { throw new IllegalStateException("Must define onGetInstantAppResolveInfo"); } @@ -75,8 +77,8 @@ public abstract class InstantAppResolverService extends Service { * String, InstantAppResolutionCallback)}. */ @Deprecated - public void onGetInstantAppIntentFilter( - int digestPrefix[], String token, InstantAppResolutionCallback callback) { + public void onGetInstantAppIntentFilter(@Nullable int[] digestPrefix, @NonNull String token, + @NonNull InstantAppResolutionCallback callback) { throw new IllegalStateException("Must define onGetInstantAppIntentFilter"); } @@ -105,8 +107,9 @@ public abstract class InstantAppResolverService extends Service { * String, InstantAppResolutionCallback)}. */ @Deprecated - public void onGetInstantAppResolveInfo(Intent sanitizedIntent, int[] hostDigestPrefix, - String token, InstantAppResolutionCallback callback) { + public void onGetInstantAppResolveInfo(@NonNull Intent sanitizedIntent, + @Nullable int[] hostDigestPrefix, @NonNull String token, + @NonNull InstantAppResolutionCallback callback) { // if not overridden, forward to old methods and filter out non-web intents if (sanitizedIntent.isWebIntent()) { onGetInstantAppResolveInfo(hostDigestPrefix, token, callback); @@ -135,8 +138,9 @@ public abstract class InstantAppResolverService extends Service { * String, InstantAppResolutionCallback)}. */ @Deprecated - public void onGetInstantAppIntentFilter(Intent sanitizedIntent, int[] hostDigestPrefix, - String token, InstantAppResolutionCallback callback) { + public void onGetInstantAppIntentFilter(@NonNull Intent sanitizedIntent, + @Nullable int[] hostDigestPrefix, + @NonNull String token, @NonNull InstantAppResolutionCallback callback) { Log.e(TAG, "New onGetInstantAppIntentFilter is not overridden"); // if not overridden, forward to old methods and filter out non-web intents if (sanitizedIntent.isWebIntent()) { @@ -167,8 +171,9 @@ public abstract class InstantAppResolverService extends Service { * * @see InstantAppResolveInfo */ - public void onGetInstantAppResolveInfo(Intent sanitizedIntent, int[] hostDigestPrefix, - UserHandle userHandle, String token, InstantAppResolutionCallback callback) { + public void onGetInstantAppResolveInfo(@NonNull Intent sanitizedIntent, + @Nullable int[] hostDigestPrefix, @NonNull UserHandle userHandle, + @NonNull String token, @NonNull InstantAppResolutionCallback callback) { // If not overridden, forward to the old method. onGetInstantAppResolveInfo(sanitizedIntent, hostDigestPrefix, token, callback); } @@ -189,8 +194,9 @@ public abstract class InstantAppResolverService extends Service { * to the currently visible installer via {@link Intent#EXTRA_INSTANT_APP_TOKEN}. * @param callback The {@link InstantAppResolutionCallback} to provide results to. */ - public void onGetInstantAppIntentFilter(Intent sanitizedIntent, int[] hostDigestPrefix, - UserHandle userHandle, String token, InstantAppResolutionCallback callback) { + public void onGetInstantAppIntentFilter(@NonNull Intent sanitizedIntent, + @Nullable int[] hostDigestPrefix, @NonNull UserHandle userHandle, + @NonNull String token, @NonNull InstantAppResolutionCallback callback) { // If not overridden, forward to the old method. onGetInstantAppIntentFilter(sanitizedIntent, hostDigestPrefix, token, callback); } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 41a9921391117448ec9509a5c921c526a0121c55..25e35734202e0a5b2dc374280796d3c8359e436a 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -676,7 +676,7 @@ public final class LoadedApk { // Shared libraries get a null parent: this has the side effect of having canonicalized // shared libraries using ApplicationLoaders cache, which is the behavior we want. - return ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(jars, + return ApplicationLoaders.getDefault().getSharedLibraryClassLoaderWithSharedLibraries(jars, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath, libraryPermittedPath, /* parent */ null, /* classLoaderName */ null, sharedLibraries); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index d634aa5784292d8baa5c6ea3f8a11b2dfe8c03af..11fa343df0d362e855e4bb2a3b5c5114620675b2 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -16,12 +16,14 @@ package android.app; +import static android.annotation.Dimension.DP; import static android.graphics.drawable.Icon.TYPE_BITMAP; import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast; import android.annotation.ColorInt; import android.annotation.DimenRes; +import android.annotation.Dimension; import android.annotation.DrawableRes; import android.annotation.IdRes; import android.annotation.IntDef; @@ -615,6 +617,13 @@ public class Notification implements Parcelable */ public static final int FLAG_CAN_COLORIZE = 0x00000800; + /** + * Bit to be bitswised-ored into the {@link #flags} field that should be + * set if this notification can be shown as a bubble. + * @hide + */ + public static final int FLAG_BUBBLE = 0x00001000; + public int flags; /** @hide */ @@ -6241,6 +6250,15 @@ public class Notification implements Parcelable return false; } + /** + * @return true if this is a notification that can show as a bubble. + * + * @hide + */ + public boolean isBubbleNotification() { + return (flags & Notification.FLAG_BUBBLE) != 0; + } + private boolean hasLargeIcon() { return mLargeIcon != null || largeIcon != null; } @@ -8594,15 +8612,18 @@ public class Notification implements Parcelable /** * @return the ideal height, in DPs, for the floating window that app content defined by - * {@link #getIntent()} for this bubble. + * {@link #getIntent()} for this bubble. A value of 0 indicates a desired height has not + * been set. */ + @Dimension(unit = DP) public int getDesiredHeight() { return mDesiredHeight; } /** * @return the resId of ideal height for the floating window that app content defined by - * {@link #getIntent()} for this bubble. + * {@link #getIntent()} for this bubble. A value of 0 indicates a res value has not + * been provided for the desired height. */ @DimenRes public int getDesiredHeightResId() { @@ -8733,7 +8754,7 @@ public class Notification implements Parcelable * be used instead. */ @NonNull - public BubbleMetadata.Builder setDesiredHeight(int height) { + public BubbleMetadata.Builder setDesiredHeight(@Dimension(unit = DP) int height) { mDesiredHeight = Math.max(height, 0); mDesiredHeightResId = 0; return this; diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 5cdf85a6ca13271d059f7e1aaab59f6a78e6a8ad..69ec831b5d1d80b0e530ec8d30b4140cfed4b866 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -170,6 +170,7 @@ public final class NotificationChannel implements Parcelable { private boolean mBlockableSystem = false; private boolean mAllowBubbles = DEFAULT_ALLOW_BUBBLE; private boolean mImportanceLockedByOEM; + private boolean mImportanceLockedDefaultApp; /** * Creates a notification channel. @@ -652,6 +653,14 @@ public final class NotificationChannel implements Parcelable { mImportanceLockedByOEM = locked; } + /** + * @hide + */ + @TestApi + public void setImportanceLockedByCriticalDeviceFunction(boolean locked) { + mImportanceLockedDefaultApp = locked; + } + /** * @hide */ @@ -660,6 +669,14 @@ public final class NotificationChannel implements Parcelable { return mImportanceLockedByOEM; } + /** + * @hide + */ + @TestApi + public boolean isImportanceLockedByCriticalDeviceFunction() { + return mImportanceLockedDefaultApp; + } + /** * Returns whether the user has chosen the importance of this channel, either to affirm the * initial selection from the app, or changed it to be higher or lower. @@ -834,6 +851,9 @@ public final class NotificationChannel implements Parcelable { out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(canBubble())); } + // mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of + // truth and so aren't written to this xml file + out.endTag(null, TAG_CHANNEL); } @@ -942,7 +962,8 @@ public final class NotificationChannel implements Parcelable { return sb.toString(); } - public static final @android.annotation.NonNull Creator CREATOR = new Creator() { + public static final @android.annotation.NonNull Creator CREATOR = + new Creator() { @Override public NotificationChannel createFromParcel(Parcel in) { return new NotificationChannel(in); @@ -983,7 +1004,8 @@ public final class NotificationChannel implements Parcelable { && Arrays.equals(mVibration, that.mVibration) && Objects.equals(getGroup(), that.getGroup()) && Objects.equals(getAudioAttributes(), that.getAudioAttributes()) - && mImportanceLockedByOEM == that.mImportanceLockedByOEM; + && mImportanceLockedByOEM == that.mImportanceLockedByOEM + && mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp; } @Override @@ -993,7 +1015,7 @@ public final class NotificationChannel implements Parcelable { getUserLockedFields(), isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(), getAudioAttributes(), isBlockableSystem(), mAllowBubbles, - mImportanceLockedByOEM); + mImportanceLockedByOEM, mImportanceLockedDefaultApp); result = 31 * result + Arrays.hashCode(mVibration); return result; } @@ -1022,6 +1044,7 @@ public final class NotificationChannel implements Parcelable { + ", mBlockableSystem=" + mBlockableSystem + ", mAllowBubbles=" + mAllowBubbles + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM + + ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp + '}'; pw.println(prefix + output); } @@ -1049,6 +1072,7 @@ public final class NotificationChannel implements Parcelable { + ", mBlockableSystem=" + mBlockableSystem + ", mAllowBubbles=" + mAllowBubbles + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM + + ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp + '}'; } diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 35658fbcb989e345c218eebf15acb11c35793ffb..b93aaa2aca687fdf7d1fdfe173e5387cdd163538 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.CompatResources; @@ -32,6 +33,7 @@ import android.content.res.ResourcesImpl; import android.content.res.ResourcesKey; import android.hardware.display.DisplayManagerGlobal; import android.os.IBinder; +import android.os.Process; import android.os.Trace; import android.util.ArrayMap; import android.util.DisplayMetrics; @@ -1136,27 +1138,46 @@ public class ResourcesManager { } // TODO(adamlesinski): Make this accept more than just overlay directories. - final void applyNewResourceDirsLocked(@NonNull final String baseCodePath, - @Nullable final String[] newResourceDirs) { + final void applyNewResourceDirsLocked(@NonNull final ApplicationInfo appInfo, + @Nullable final String[] oldPaths) { try { Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#applyNewResourceDirsLocked"); + String baseCodePath = appInfo.getBaseCodePath(); + + final int myUid = Process.myUid(); + String[] newSplitDirs = appInfo.uid == myUid + ? appInfo.splitSourceDirs + : appInfo.splitPublicSourceDirs; + + // ApplicationInfo is mutable, so clone the arrays to prevent outside modification + String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs); + String[] copiedResourceDirs = ArrayUtils.cloneOrNull(appInfo.resourceDirs); + final ArrayMap updatedResourceKeys = new ArrayMap<>(); final int implCount = mResourceImpls.size(); for (int i = 0; i < implCount; i++) { final ResourcesKey key = mResourceImpls.keyAt(i); final WeakReference weakImplRef = mResourceImpls.valueAt(i); final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null; - if (impl != null && (key.mResDir == null || key.mResDir.equals(baseCodePath))) { + + if (impl == null) { + continue; + } + + if (key.mResDir == null + || key.mResDir.equals(baseCodePath) + || ArrayUtils.contains(oldPaths, key.mResDir)) { updatedResourceKeys.put(impl, new ResourcesKey( - key.mResDir, - key.mSplitResDirs, - newResourceDirs, + baseCodePath, + copiedSplitDirs, + copiedResourceDirs, key.mLibDirs, key.mDisplayId, key.mOverrideConfiguration, - key.mCompatInfo)); + key.mCompatInfo + )); } } diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java index 7746148d325a55dd54061dbb4d9243a66502c9b5..2e14d03a912c5b49d17f860d89c0da612e6e7d8a 100644 --- a/core/java/android/app/StatsManager.java +++ b/core/java/android/app/StatsManager.java @@ -18,6 +18,7 @@ package android.app; import static android.Manifest.permission.DUMP; import static android.Manifest.permission.PACKAGE_USAGE_STATS; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; @@ -295,7 +296,7 @@ public final class StatsManager { * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service */ @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) - public long[] setActiveConfigsChangedOperation(@Nullable PendingIntent pendingIntent) + public @NonNull long[] setActiveConfigsChangedOperation(@Nullable PendingIntent pendingIntent) throws StatsUnavailableException { synchronized (this) { try { @@ -409,6 +410,36 @@ public final class StatsManager { } } + /** + * Returns the experiments IDs registered with statsd, or an empty array if there aren't any. + * + * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service + * @hide + */ + @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS}) + public long[] getRegisteredExperimentIds() + throws StatsUnavailableException { + synchronized (this) { + try { + IStatsManager service = getIStatsManagerLocked(); + if (service == null) { + if (DEBUG) { + Slog.d(TAG, "Failed to find statsd when getting experiment IDs"); + } + return new long[0]; + } + return service.getRegisteredExperimentIds(); + } catch (RemoteException e) { + if (DEBUG) { + Slog.d(TAG, + "Failed to connect to StatsCompanionService when getting " + + "registered experiment IDs"); + } + return new long[0]; + } + } + } + /** * Registers a callback for an atom when that atom is to be pulled. The stats service will * invoke pullData in the callback when the stats service determines that this atom needs to be diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index a021e3cb2d78f92d4388e5a08676b89d162eb676..494467324581ee5077e9bbe4327d7839c98bd6f6 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -22,6 +22,7 @@ import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceClient; import android.accessibilityservice.IAccessibilityServiceConnection; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.graphics.Bitmap; @@ -390,10 +391,12 @@ public final class UiAutomation { * Note: Calling this method adopts only the specified shell permissions * and overrides all adopted permissions via {@link #adoptShellPermissionIdentity()}. * + * @param permissions The permissions to adopt or null to adopt all. + * * @see #adoptShellPermissionIdentity() * @see #dropShellPermissionIdentity() */ - public void adoptShellPermissionIdentity(String... permissions) { + public void adoptShellPermissionIdentity(@Nullable String... permissions) { synchronized (mLock) { throwIfNotConnectedLocked(); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 4c05497479c05601ba7b4478c2289c2c28eb89b1..8a522656a13a133ea5a8303ab87236b581c39fd7 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -54,7 +54,6 @@ import android.net.NetworkUtils; import android.net.PrivateDnsConnectivityChecker; import android.net.ProxyInfo; import android.net.Uri; -import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.ParcelFileDescriptor; @@ -2166,7 +2165,7 @@ public class DevicePolicyManager { * reported back to the IT admin to be read. */ public void onInstallUpdateError( - @InstallUpdateCallbackErrorConstants int errorCode, String errorMessage) { + @InstallUpdateCallbackErrorConstants int errorCode, @NonNull String errorMessage) { } } @@ -5440,13 +5439,14 @@ public class DevicePolicyManager { } /** - * Called by a device or profile owner to set whether auto time is required. If auto time is - * required, no user will be able set the date and time and network date and time will be used. + * Called by a device owner, or alternatively a profile owner from Android 8.0 (API level 26) or + * higher, to set whether auto time is required. If auto time is required, no user will be able + * set the date and time and network date and time will be used. *

* Note: if auto time is required the user can still manually set the time zone. *

- * The calling device admin must be a device or profile owner. If it is not, a security - * exception will be thrown. + * The calling device admin must be a device owner, or alternatively a profile owner from + * Android 8.0 (API level 26) or higher. If it is not, a security exception will be thrown. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param required Whether auto time is set required or not. @@ -6409,27 +6409,20 @@ public class DevicePolicyManager { * Returns whether the specified package can read the device identifiers. * * @param packageName The package name of the app to check for device identifier access. + * @param pid The process id of the package to be checked. + * @param uid The uid of the package to be checked. * @return whether the package can read the device identifiers. * * @hide */ - public boolean checkDeviceIdentifierAccess(String packageName) { - return checkDeviceIdentifierAccessAsUser(packageName, myUserId()); - } - - /** - * @hide - */ - @RequiresPermission(value = android.Manifest.permission.MANAGE_USERS, conditional = true) - public boolean checkDeviceIdentifierAccessAsUser(String packageName, int userId) { - throwIfParentInstance("checkDeviceIdentifierAccessAsUser"); + public boolean checkDeviceIdentifierAccess(String packageName, int pid, int uid) { + throwIfParentInstance("checkDeviceIdentifierAccess"); if (packageName == null) { return false; } if (mService != null) { try { - return mService.checkDeviceIdentifierAccess(packageName, userId, - Binder.getCallingPid(), Binder.getCallingUid()); + return mService.checkDeviceIdentifierAccess(packageName, pid, uid); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -10789,8 +10782,8 @@ public class DevicePolicyManager { } /** - * Returns whether the device is being used as a managed kiosk, as defined in the CDD. As of - * this release, these requirements are as follows: + * Returns whether the device is being used as a managed kiosk. These requirements are as + * follows: *

    *
  • The device is in Lock Task (therefore there is also a Device Owner app on the * device)
  • @@ -10829,11 +10822,11 @@ public class DevicePolicyManager { } /** - * Returns whether the device is being used as an unattended managed kiosk, as defined in the - * CDD. As of this release, these requirements are as follows: + * Returns whether the device is being used as an unattended managed kiosk. These requirements + * are as follows: *
      - *
    • The device is being used as a managed kiosk, as defined in the CDD and verified at - * {@link #isManagedKiosk()}
    • + *
    • The device is being used as a managed kiosk, as defined at {@link + * #isManagedKiosk()}
    • *
    • The device has not received user input for at least 30 minutes
    • *
    * diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 3c389e4aa38c80a4e8a0370ef8a330b9884b6d1a..2b96419990191381b62c73e007af92d6c35fda60 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -156,7 +156,7 @@ interface IDevicePolicyManager { void clearProfileOwner(in ComponentName who); boolean hasUserSetupCompleted(); - boolean checkDeviceIdentifierAccess(in String packageName, int userHandle, int pid, int uid); + boolean checkDeviceIdentifierAccess(in String packageName, int pid, int uid); void setDeviceOwnerLockScreenInfo(in ComponentName who, CharSequence deviceOwnerInfo); CharSequence getDeviceOwnerLockScreenInfo(); diff --git a/core/java/android/app/prediction/AppTarget.java b/core/java/android/app/prediction/AppTarget.java index 826c149b2e05f729adc0ebb10bd082b5755827e1..ed45b2f45383630d1a417fb9694575205776eda0 100644 --- a/core/java/android/app/prediction/AppTarget.java +++ b/core/java/android/app/prediction/AppTarget.java @@ -223,7 +223,7 @@ public final class AppTarget implements Parcelable { */ @NonNull public Builder setTarget(@NonNull String packageName, @NonNull UserHandle user) { - if (mPackageName == null) { + if (mPackageName != null) { throw new IllegalArgumentException("Target is already set"); } mPackageName = Preconditions.checkNotNull(packageName); diff --git a/core/java/android/app/role/RoleControllerManager.java b/core/java/android/app/role/RoleControllerManager.java index e96c9a541c3f304a4d138aeb5ded224ba2ac6289..bd981179e2ff3b94fced03bf333e6d8bcada133d 100644 --- a/core/java/android/app/role/RoleControllerManager.java +++ b/core/java/android/app/role/RoleControllerManager.java @@ -167,7 +167,7 @@ public class RoleControllerManager { RemoteService(@NonNull Context context, @NonNull ComponentName componentName, @NonNull Handler handler, @UserIdInt int userId) { super(context, RoleControllerService.SERVICE_INTERFACE, componentName, userId, - service -> Log.e(LOG_TAG, "RemoteService " + service + " died"), handler, false, + service -> Log.e(LOG_TAG, "RemoteService " + service + " died"), handler, 0, false, 1); } diff --git a/core/java/android/app/usage/EventList.java b/core/java/android/app/usage/EventList.java index aaae57e526a0626a82d4fb1c50335df2e94164c8..8c0340585573644b7c7e6a283c3504babb3440bb 100644 --- a/core/java/android/app/usage/EventList.java +++ b/core/java/android/app/usage/EventList.java @@ -103,4 +103,18 @@ public class EventList { } return result; } + + /** + * Merge the {@link UsageEvents.Event events} in the given {@link EventList list} into this + * list while keeping the list sorted based on the event {@link + * UsageEvents.Event#mTimeStamp timestamps}. + * + * @param events The event list to merge + */ + public void merge(EventList events) { + final int size = events.size(); + for (int i = 0; i < size; i++) { + insert(events.get(i)); + } + } } diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index fa2c9f8a52ae1d09f72d376d992c0f896ee0dd12..204d7e3ceca687d32c96937361ca5170a2ec3901 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -535,7 +535,6 @@ public final class BluetoothDevice implements Parcelable { /** * Intent to broadcast silence mode changed. * Alway contains the extra field {@link #EXTRA_DEVICE} - * Alway contains the extra field {@link #EXTRA_SILENCE_ENABLED} * * @hide */ @@ -544,16 +543,6 @@ public final class BluetoothDevice implements Parcelable { public static final String ACTION_SILENCE_MODE_CHANGED = "android.bluetooth.device.action.SILENCE_MODE_CHANGED"; - /** - * Used as an extra field in {@link #ACTION_SILENCE_MODE_CHANGED} intent, - * contains whether device is in silence mode as boolean. - * - * @hide - */ - @SystemApi - public static final String EXTRA_SILENCE_ENABLED = - "android.bluetooth.device.extra.SILENCE_ENABLED"; - /** * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intent. * @@ -1615,7 +1604,8 @@ public final class BluetoothDevice implements Parcelable { } /** - * Set the Bluetooth device silence mode. + * Sets whether the {@link BluetoothDevice} enters silence mode. Audio will not + * be routed to the {@link BluetoothDevice} if set to {@code true}. * * When the {@link BluetoothDevice} enters silence mode, and the {@link BluetoothDevice} * is an active device (for A2DP or HFP), the active device for that profile @@ -1635,6 +1625,7 @@ public final class BluetoothDevice implements Parcelable { * * @param silence true to enter silence mode, false to exit * @return true on success, false on error. + * @throws IllegalStateException if Bluetooth is not turned ON. * @hide */ @SystemApi @@ -1642,12 +1633,9 @@ public final class BluetoothDevice implements Parcelable { public boolean setSilenceMode(boolean silence) { final IBluetooth service = sService; if (service == null) { - return false; + throw new IllegalStateException("Bluetooth is not turned ON"); } try { - if (getSilenceMode() == silence) { - return true; - } return service.setSilenceMode(this, silence); } catch (RemoteException e) { Log.e(TAG, "setSilenceMode fail", e); @@ -1656,24 +1644,25 @@ public final class BluetoothDevice implements Parcelable { } /** - * Get the device silence mode status + * Check whether the {@link BluetoothDevice} is in silence mode * *

    Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}. * * @return true on device in silence mode, otherwise false. + * @throws IllegalStateException if Bluetooth is not turned ON. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean getSilenceMode() { + public boolean isInSilenceMode() { final IBluetooth service = sService; if (service == null) { - return false; + throw new IllegalStateException("Bluetooth is not turned ON"); } try { return service.getSilenceMode(this); } catch (RemoteException e) { - Log.e(TAG, "getSilenceMode fail", e); + Log.e(TAG, "isInSilenceMode fail", e); return false; } } diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java index e2e56fd02ab7917d9929ae9285d53f9c36df8e8d..5fd60e001693c679a2c261b8b3259563c8eff219 100644 --- a/core/java/android/bluetooth/BluetoothHealth.java +++ b/core/java/android/bluetooth/BluetoothHealth.java @@ -99,6 +99,11 @@ public final class BluetoothHealth implements BluetoothProfile { @Deprecated public static final int CHANNEL_TYPE_STREAMING = 11; + /** + * Hide auto-created default constructor + * @hide + */ + BluetoothHealth() {} /** * Register an application configuration that acts as a Health SINK. diff --git a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java index 88e06e58f9ce182d00350146dd86e808fc802b65..2f66df258b537dc4c2596c23586e0776662eb9cf 100644 --- a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java +++ b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java @@ -33,6 +33,13 @@ import android.os.Parcelable; */ @Deprecated public final class BluetoothHealthAppConfiguration implements Parcelable { + + /** + * Hide auto-created default constructor + * @hide + */ + BluetoothHealthAppConfiguration() {} + @Override public int describeContents() { return 0; diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java index a5460e92d73a3eca6fa5596b95f6f5b84392dbc6..18147b5721254ad405af94ea59fc219b681f7344 100644 --- a/core/java/android/content/ComponentName.java +++ b/core/java/android/content/ComponentName.java @@ -339,6 +339,8 @@ public final class ComponentName implements Parcelable, Cloneable, ComparableShould be stable across reboots and backup / restore. + *

    Locus is a new concept introduced on + * {@link android.os.Build.VERSION_CODES#Q Android Q} and it lets the intelligence service provided + * by the Android System to correlate state between different subsystems such as content capture, + * shortcuts, and notifications. * - *

    For example, a chat app could use the context to resume a conversation between 2 users. + *

    For example, if your app provides an activiy representing a chat between 2 users + * (say {@code A} and {@code B}, this chat state could be represented by: + * + *

    
    + * LocusId chatId = new LocusId("Chat_A_B");
    + * 
    + * + *

    And then you should use that {@code chatId} by: + * + *

      + *
    • Setting it in the chat notification (through + * {@link android.app.Notification.Builder#setLocusId(LocusId) + * Notification.Builder.setLocusId(chatId)}). + *
    • Setting it into the {@link android.content.pm.ShortcutInfo} (through + * {@link android.content.pm.ShortcutInfo.Builder#setLocusId(LocusId) + * ShortcutInfo.Builder.setLocusId(chatId)}), if you provide a launcher shortcut for that chat + * conversation. + *
    • Associating it with the {@link android.view.contentcapture.ContentCaptureContext} of the + * root view of the chat conversation activity (through + * {@link android.view.View#getContentCaptureSession()}, then + * {@link android.view.contentcapture.ContentCaptureContext.Builder + * new ContentCaptureContext.Builder(chatId).build()} and + * {@link android.view.contentcapture.ContentCaptureSession#setContentCaptureContext( + * android.view.contentcapture.ContentCaptureContext)} - see {@link ContentCaptureManager} + * for more info about content capture). + *
    • Configuring your app to launch the chat conversation through the + * {@link Intent#ACTION_VIEW_LOCUS} intent. + *
    */ -// TODO(b/123577059): make sure this is well documented and understandable public final class LocusId implements Parcelable { private final String mId; @@ -45,7 +76,7 @@ public final class LocusId implements Parcelable { } /** - * Gets the {@code id} associated with the locus. + * Gets the canonical {@code id} associated with the locus. */ @NonNull public String getId() { @@ -100,7 +131,7 @@ public final class LocusId implements Parcelable { parcel.writeString(mId); } - public static final @android.annotation.NonNull Parcelable.Creator CREATOR = + public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @NonNull diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java index aabe59d183834d293d1b86189416f25566257d0e..597c08392d04dffb8f9cfe204e01d47a33ec7c43 100644 --- a/core/java/android/content/om/OverlayInfo.java +++ b/core/java/android/content/om/OverlayInfo.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.UserIdInt; import android.os.Parcel; import android.os.Parcelable; @@ -44,7 +45,7 @@ public final class OverlayInfo implements Parcelable { STATE_DISABLED, STATE_ENABLED, STATE_ENABLED_STATIC, - STATE_TARGET_UPGRADING, + // @Deprecated STATE_TARGET_UPGRADING, STATE_OVERLAY_UPGRADING, }) /** @hide */ @@ -96,7 +97,14 @@ public final class OverlayInfo implements Parcelable { * The target package is currently being upgraded; the state will change * once the package installation has finished. * @hide + * + * @deprecated No longer used. Caused invalid transitions from enabled -> upgrading -> enabled, + * where an update is propagated when nothing has changed. Can occur during --dont-kill + * installs when code and resources are hot swapped and the Activity should not be relaunched. + * In all other cases, the process and therefore Activity is killed, so the state loop is + * irrelevant. */ + @Deprecated public static final int STATE_TARGET_UPGRADING = 4; /** @@ -128,7 +136,6 @@ public final class OverlayInfo implements Parcelable { * * @hide */ - @SystemApi public final String packageName; /** @@ -136,7 +143,6 @@ public final class OverlayInfo implements Parcelable { * * @hide */ - @SystemApi public final String targetPackageName; /** @@ -144,7 +150,6 @@ public final class OverlayInfo implements Parcelable { * * @hide */ - @SystemApi public final String targetOverlayableName; /** @@ -152,7 +157,6 @@ public final class OverlayInfo implements Parcelable { * * @hide */ - @SystemApi public final String category; /** @@ -171,7 +175,6 @@ public final class OverlayInfo implements Parcelable { * User handle for which this overlay applies * @hide */ - @SystemApi public final int userId; /** @@ -236,6 +239,56 @@ public final class OverlayInfo implements Parcelable { ensureValidState(); } + /** + * Returns package name of the current overlay. + * @hide + */ + @SystemApi + @NonNull + public String getPackageName() { + return packageName; + } + + /** + * Returns the target package name of the current overlay. + * @hide + */ + @SystemApi + @Nullable + public String getTargetPackageName() { + return targetPackageName; + } + + /** + * Returns the category of the current overlay. + * @hide\ + */ + @SystemApi + @Nullable + public String getCategory() { + return category; + } + + /** + * Returns user handle for which this overlay applies to. + * @hide + */ + @SystemApi + @UserIdInt + public int getUserId() { + return userId; + } + + /** + * Returns name of the target overlayable declaration. + * @hide + */ + @SystemApi + @Nullable + public String getTargetOverlayableName() { + return targetOverlayableName; + } + private void ensureValidState() { if (packageName == null) { throw new IllegalArgumentException("packageName must not be null"); diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 068a93a253ff0cbfeadc73e5345a3c8afd3a5626..5328dda03893d434b14dd32e98c2102dc677cdd8 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -678,6 +678,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public static final int PRIVATE_FLAG_IS_RESOURCE_OVERLAY = 1 << 28; + /** + * Value for {@link #privateFlags}: If {@code true} this app allows + * shared/external storage media to be a sandboxed view that only contains + * files owned by the app. + * + * @hide + */ + public static final int PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX = 1 << 29; /** @hide */ @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = { @@ -707,7 +715,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { PRIVATE_FLAG_VIRTUAL_PRELOAD, PRIVATE_FLAG_HAS_FRAGILE_USER_DATA, PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE, - PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE + PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE, + PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX, }) @Retention(RetentionPolicy.SOURCE) public @interface ApplicationInfoPrivateFlags {} @@ -1822,6 +1831,16 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return (privateFlags & PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE) != 0; } + /** + * If {@code true} this app allows shared/external storage media to be a + * sandboxed view that only contains files owned by the app. + * + * @hide + */ + public boolean isExternalStorageSandboxAllowed() { + return (privateFlags & PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX) != 0; + } + private boolean isAllowedToUseHiddenApis() { if (isSignedWithPlatformKey()) { return true; diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index c798270d1fdcf4740cecf7f07ac0412cdf293123..fb2218778c9b3849367d69e3bb42ee14dd1356d2 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -740,6 +740,8 @@ interface IPackageManager { String getSystemTextClassifierPackageName(); + String getAttentionServicePackageName(); + String getWellbeingPackageName(); String getAppPredictionServicePackageName(); diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 954deac97d6dab531701c5b58585a052601a2cb3..037a149bfe37165dece0dc7effc9ae072523ace9 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; @@ -782,8 +783,10 @@ public class LauncherApps { * @return an {@link AppUsageLimit} object describing the app time limit containing * the given package with the smallest time remaining, or {@code null} if none exist. * @throws SecurityException when the caller is not the recents app. + * @hide */ @Nullable + @SystemApi public LauncherApps.AppUsageLimit getAppUsageLimit(@NonNull String packageName, @NonNull UserHandle user) { try { @@ -1739,7 +1742,9 @@ public class LauncherApps { * in this class. * * @see #getAppUsageLimit(String, UserHandle) + * @hide */ + @SystemApi public static final class AppUsageLimit implements Parcelable { private final long mTotalUsageLimit; private final long mUsageRemaining; diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 1e6cea39b44dd786075844c8b19c154371e70e59..a96316b3d36e2cdeb579828a92caaebc5edf39a8 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1211,6 +1211,9 @@ public class PackageInstaller { * Adds a session ID to the set of sessions that will be committed atomically * when this session is committed. * + *

    If the parent is staged or has rollback enabled, all children must have + * the same properties. + * * @param sessionId the session ID to add to this multi-package session. */ public void addChildSessionId(int sessionId) { @@ -1480,6 +1483,9 @@ public class PackageInstaller { /** * Request that rollbacks be enabled or disabled for the given upgrade. * + *

    If the parent session is staged or has rollback enabled, all children sessions + * must have the same properties. + * * @param enable set to {@code true} to enable, {@code false} to disable * @hide */ @@ -1607,6 +1613,9 @@ public class PackageInstaller { * multi-package. In that case, if any of the children sessions fail to install at reboot, * all the other children sessions are aborted as well. * + *

    If the parent session is staged or has rollback enabled, all children sessions + * must have the same properties. + * * {@hide} */ @SystemApi @TestApi @@ -1626,6 +1635,11 @@ public class PackageInstaller { installFlags |= PackageManager.INSTALL_APEX; } + /** @hide */ + public boolean getEnableRollback() { + return (installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0; + } + /** {@hide} */ public void dump(IndentingPrintWriter pw) { pw.printPair("mode", mode); @@ -2152,6 +2166,7 @@ public class PackageInstaller { * Returns the set of session IDs that will be committed when this session is commited if * this session is a multi-package session. */ + @NonNull public int[] getChildSessionIds() { return childSessionIds; } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 961faa0c19b66621dc65ec38e31e7d7a90667f5e..f0539c493bb1907b7f4784afc86ddda1b5718e43 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1397,6 +1397,14 @@ public abstract class PackageManager { */ public static final int INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS = -119; + /** + * Installation failed return code: one of the child sessions does not match the parent session + * in respect to staged or rollback enabled parameters. + * + * @hide + */ + public static final int INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY = -120; + /** @hide */ @IntDef(flag = true, prefix = { "DELETE_" }, value = { DELETE_KEEP_DATA, @@ -2990,6 +2998,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @TestApi public static final int FLAG_PERMISSION_POLICY_FIXED = 1 << 2; /** @@ -3013,6 +3022,7 @@ public abstract class PackageManager { * @hide */ @SystemApi + @TestApi public static final int FLAG_PERMISSION_SYSTEM_FIXED = 1 << 4; /** @@ -3064,15 +3074,6 @@ public abstract class PackageManager { @SystemApi public static final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED = 1 << 9; - /** - * Permission flag: The permission should not be shown in the UI. - * - * @hide - */ - @SystemApi - @TestApi - public static final int FLAG_PERMISSION_HIDDEN = 1 << 10; - /** * Mask for all permission flags present in Android P * @@ -3091,7 +3092,7 @@ public abstract class PackageManager { * * @hide */ - public static final int MASK_PERMISSION_FLAGS_ALL = 0x7FF; + public static final int MASK_PERMISSION_FLAGS_ALL = 0x3FF; /** * Injected activity in app that forwards user to setting activity of that app. @@ -3214,7 +3215,8 @@ public abstract class PackageManager { * @throws NameNotFoundException if a package with the given name cannot be * found on the system. */ - public abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) + public abstract PackageInfo getPackageInfo(@NonNull String packageName, + @PackageInfoFlags int flags) throws NameNotFoundException; /** @@ -3239,7 +3241,7 @@ public abstract class PackageManager { * @throws NameNotFoundException if a package with the given name cannot be * found on the system. */ - public abstract PackageInfo getPackageInfo(VersionedPackage versionedPackage, + public abstract PackageInfo getPackageInfo(@NonNull VersionedPackage versionedPackage, @PackageInfoFlags int flags) throws NameNotFoundException; /** @@ -3263,25 +3265,25 @@ public abstract class PackageManager { */ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) @UnsupportedAppUsage - public abstract PackageInfo getPackageInfoAsUser(String packageName, + public abstract PackageInfo getPackageInfoAsUser(@NonNull String packageName, @PackageInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException; /** * Map from the current package names in use on the device to whatever * the current canonical name of that package is. - * @param names Array of current names to be mapped. + * @param packageNames Array of current names to be mapped. * @return Returns an array of the same size as the original, containing * the canonical name for each package. */ - public abstract String[] currentToCanonicalPackageNames(String[] names); + public abstract String[] currentToCanonicalPackageNames(@NonNull String[] packageNames); /** * Map from a packages canonical name to the current name in use on the device. - * @param names Array of new names to be mapped. + * @param packageNames Array of new names to be mapped. * @return Returns an array of the same size as the original, containing * the current name for each package. */ - public abstract String[] canonicalToCurrentPackageNames(String[] names); + public abstract String[] canonicalToCurrentPackageNames(@NonNull String[] packageNames); /** * Returns a "good" intent to launch a front-door activity in a package. @@ -3360,7 +3362,7 @@ public abstract class PackageManager { * @throws NameNotFoundException if a package with the given name cannot be * found on the system. */ - public abstract int[] getPackageGids(String packageName, @PackageInfoFlags int flags) + public abstract int[] getPackageGids(@NonNull String packageName, @PackageInfoFlags int flags) throws NameNotFoundException; /** @@ -3375,7 +3377,7 @@ public abstract class PackageManager { * @throws NameNotFoundException if a package with the given name can not be * found on the system. */ - public abstract int getPackageUid(String packageName, @PackageInfoFlags int flags) + public abstract int getPackageUid(@NonNull String packageName, @PackageInfoFlags int flags) throws NameNotFoundException; /** @@ -3393,7 +3395,7 @@ public abstract class PackageManager { * @hide */ @UnsupportedAppUsage - public abstract int getPackageUidAsUser(String packageName, @UserIdInt int userId) + public abstract int getPackageUidAsUser(@NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException; /** @@ -3411,13 +3413,13 @@ public abstract class PackageManager { * @hide */ @UnsupportedAppUsage - public abstract int getPackageUidAsUser(String packageName, @PackageInfoFlags int flags, - @UserIdInt int userId) throws NameNotFoundException; + public abstract int getPackageUidAsUser(@NonNull String packageName, + @PackageInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException; /** * Retrieve all of the information we know about a particular permission. * - * @param name The fully qualified name (i.e. com.google.permission.LOGIN) + * @param permissionName The fully qualified name (i.e. com.google.permission.LOGIN) * of the permission you are interested in. * @param flags Additional option flags to modify the data returned. * @return Returns a {@link PermissionInfo} containing information about the @@ -3425,13 +3427,13 @@ public abstract class PackageManager { * @throws NameNotFoundException if a package with the given name cannot be * found on the system. */ - public abstract PermissionInfo getPermissionInfo(String name, @PermissionInfoFlags int flags) - throws NameNotFoundException; + public abstract PermissionInfo getPermissionInfo(@NonNull String permissionName, + @PermissionInfoFlags int flags) throws NameNotFoundException; /** * Query for all of the permissions associated with a particular group. * - * @param group The fully qualified name (i.e. com.google.permission.LOGIN) + * @param permissionGroup The fully qualified name (i.e. com.google.permission.LOGIN) * of the permission group you are interested in. Use null to * find all of the permissions not associated with a group. * @param flags Additional option flags to modify the data returned. @@ -3440,7 +3442,8 @@ public abstract class PackageManager { * @throws NameNotFoundException if a package with the given name cannot be * found on the system. */ - public abstract List queryPermissionsByGroup(String group, + @NonNull + public abstract List queryPermissionsByGroup(@NonNull String permissionGroup, @PermissionInfoFlags int flags) throws NameNotFoundException; /** @@ -3465,7 +3468,7 @@ public abstract class PackageManager { * Retrieve all of the information we know about a particular group of * permissions. * - * @param name The fully qualified name (i.e. + * @param permissionName The fully qualified name (i.e. * com.google.permission_group.APPS) of the permission you are * interested in. * @param flags Additional option flags to modify the data returned. @@ -3474,7 +3477,8 @@ public abstract class PackageManager { * @throws NameNotFoundException if a package with the given name cannot be * found on the system. */ - public abstract PermissionGroupInfo getPermissionGroupInfo(String name, + @NonNull + public abstract PermissionGroupInfo getPermissionGroupInfo(@NonNull String permissionName, @PermissionGroupInfoFlags int flags) throws NameNotFoundException; /** @@ -3484,6 +3488,7 @@ public abstract class PackageManager { * @return Returns a list of {@link PermissionGroupInfo} containing * information about all of the known permission groups. */ + @NonNull public abstract List getAllPermissionGroups( @PermissionGroupInfoFlags int flags); @@ -3504,12 +3509,14 @@ public abstract class PackageManager { * @throws NameNotFoundException if a package with the given name cannot be * found on the system. */ - public abstract ApplicationInfo getApplicationInfo(String packageName, + @NonNull + public abstract ApplicationInfo getApplicationInfo(@NonNull String packageName, @ApplicationInfoFlags int flags) throws NameNotFoundException; /** {@hide} */ + @NonNull @UnsupportedAppUsage - public abstract ApplicationInfo getApplicationInfoAsUser(String packageName, + public abstract ApplicationInfo getApplicationInfoAsUser(@NonNull String packageName, @ApplicationInfoFlags int flags, @UserIdInt int userId) throws NameNotFoundException; /** @@ -3552,7 +3559,8 @@ public abstract class PackageManager { * @throws NameNotFoundException if a package with the given name cannot be * found on the system. */ - public abstract ActivityInfo getActivityInfo(ComponentName component, + @NonNull + public abstract ActivityInfo getActivityInfo(@NonNull ComponentName component, @ComponentInfoFlags int flags) throws NameNotFoundException; /** @@ -3568,7 +3576,8 @@ public abstract class PackageManager { * @throws NameNotFoundException if a package with the given name cannot be * found on the system. */ - public abstract ActivityInfo getReceiverInfo(ComponentName component, + @NonNull + public abstract ActivityInfo getReceiverInfo(@NonNull ComponentName component, @ComponentInfoFlags int flags) throws NameNotFoundException; /** @@ -3583,7 +3592,8 @@ public abstract class PackageManager { * @throws NameNotFoundException if a package with the given name cannot be * found on the system. */ - public abstract ServiceInfo getServiceInfo(ComponentName component, + @NonNull + public abstract ServiceInfo getServiceInfo(@NonNull ComponentName component, @ComponentInfoFlags int flags) throws NameNotFoundException; /** @@ -3599,7 +3609,8 @@ public abstract class PackageManager { * @throws NameNotFoundException if a package with the given name cannot be * found on the system. */ - public abstract ProviderInfo getProviderInfo(ComponentName component, + @NonNull + public abstract ProviderInfo getProviderInfo(@NonNull ComponentName component, @ComponentInfoFlags int flags) throws NameNotFoundException; /** @@ -3612,7 +3623,8 @@ public abstract class PackageManager { * @throws NameNotFoundException if a module with the given name cannot be * found on the system. */ - public ModuleInfo getModuleInfo(String packageName, @ModuleInfoFlags int flags) + @NonNull + public ModuleInfo getModuleInfo(@NonNull String packageName, @ModuleInfoFlags int flags) throws NameNotFoundException { throw new UnsupportedOperationException( "getModuleInfo not implemented in subclass"); @@ -3626,7 +3638,8 @@ public abstract class PackageManager { * module, containing information about the module. In the unlikely case * there are no installed modules, an empty list is returned. */ - public @NonNull List getInstalledModules(@ModuleInfoFlags int flags) { + @NonNull + public List getInstalledModules(@ModuleInfoFlags int flags) { throw new UnsupportedOperationException( "getInstalledModules not implemented in subclass"); } @@ -3644,6 +3657,7 @@ public abstract class PackageManager { * applications with data directory i.e. applications which had been * deleted with {@code DONT_DELETE_DATA} flag set). */ + @NonNull public abstract List getInstalledPackages(@PackageInfoFlags int flags); /** @@ -3661,8 +3675,9 @@ public abstract class PackageManager { * applications with data directory i.e. applications which had been * deleted with {@code DONT_DELETE_DATA} flag set). */ + @NonNull public abstract List getPackagesHoldingPermissions( - String[] permissions, @PackageInfoFlags int flags); + @NonNull String[] permissions, @PackageInfoFlags int flags); /** * Return a List of all packages that are installed on the device, for a @@ -3680,6 +3695,7 @@ public abstract class PackageManager { * deleted with {@code DONT_DELETE_DATA} flag set). * @hide */ + @NonNull @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) @@ -3690,8 +3706,8 @@ public abstract class PackageManager { * Check whether a particular package has been granted a particular * permission. * - * @param permName The name of the permission you are checking for. - * @param pkgName The name of the package you are checking against. + * @param permissionName The name of the permission you are checking for. + * @param packageName The name of the package you are checking against. * * @return If the package has the permission, PERMISSION_GRANTED is * returned. If it does not have the permission, PERMISSION_DENIED @@ -3701,7 +3717,9 @@ public abstract class PackageManager { * @see #PERMISSION_DENIED */ @CheckResult - public abstract @PermissionResult int checkPermission(String permName, String pkgName); + @PermissionResult + public abstract int checkPermission(@NonNull String permissionName, + @NonNull String packageName); /** * Checks whether a particular permissions has been revoked for a @@ -3710,14 +3728,14 @@ public abstract class PackageManager { * permissions, hence the only way for an app to get such a permission * is by a policy change. * - * @param permName The name of the permission you are checking for. - * @param pkgName The name of the package you are checking against. + * @param permissionName The name of the permission you are checking for. + * @param packageName The name of the package you are checking against. * * @return Whether the permission is restricted by policy. */ @CheckResult - public abstract boolean isPermissionRevokedByPolicy(@NonNull String permName, - @NonNull String pkgName); + public abstract boolean isPermissionRevokedByPolicy(@NonNull String permissionName, + @NonNull String packageName); /** * Gets the package name of the component controlling runtime permissions. @@ -3726,6 +3744,7 @@ public abstract class PackageManager { * * @hide */ + @NonNull @TestApi public abstract String getPermissionControllerPackageName(); @@ -3761,7 +3780,7 @@ public abstract class PackageManager { * * @see #removePermission(String) */ - public abstract boolean addPermission(PermissionInfo info); + public abstract boolean addPermission(@NonNull PermissionInfo info); /** * Like {@link #addPermission(PermissionInfo)} but asynchronously @@ -3770,7 +3789,7 @@ public abstract class PackageManager { * expense of no guarantee the added permission will be retained if * the device is rebooted before it is written. */ - public abstract boolean addPermissionAsync(PermissionInfo info); + public abstract boolean addPermissionAsync(@NonNull PermissionInfo info); /** * Removes a permission that was previously added with @@ -3778,14 +3797,14 @@ public abstract class PackageManager { * -- you are only allowed to remove permissions that you are allowed * to add. * - * @param name The name of the permission to remove. + * @param permissionName The name of the permission to remove. * * @throws SecurityException if you are not allowed to remove the * given permission name. * * @see #addPermission(PermissionInfo) */ - public abstract void removePermission(String name); + public abstract void removePermission(@NonNull String permissionName); /** * Permission flags set when granting or revoking a permission. @@ -3802,7 +3821,6 @@ public abstract class PackageManager { FLAG_PERMISSION_GRANTED_BY_DEFAULT, FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, - FLAG_PERMISSION_HIDDEN, /* FLAG_PERMISSION_REVOKE_WHEN_REQUESED */ @@ -3880,8 +3898,9 @@ public abstract class PackageManager { android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS }) - public abstract @PermissionFlags int getPermissionFlags(String permissionName, - String packageName, @NonNull UserHandle user); + @PermissionFlags + public abstract int getPermissionFlags(@NonNull String permissionName, + @NonNull String packageName, @NonNull UserHandle user); /** * Updates the flags associated with a permission by replacing the flags in @@ -3901,9 +3920,9 @@ public abstract class PackageManager { android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS }) - public abstract void updatePermissionFlags(String permissionName, - String packageName, @PermissionFlags int flagMask, @PermissionFlags int flagValues, - @NonNull UserHandle user); + public abstract void updatePermissionFlags(@NonNull String permissionName, + @NonNull String packageName, @PermissionFlags int flagMask, + @PermissionFlags int flagValues, @NonNull UserHandle user); /** * Gets whether you should show UI with rationale for requesting a permission. @@ -3911,13 +3930,13 @@ public abstract class PackageManager { * which the permission is requested does not clearly communicate to the user * what would be the benefit from grating this permission. * - * @param permission A permission your app wants to request. + * @param permissionName A permission your app wants to request. * @return Whether you can show permission rationale UI. * * @hide */ @UnsupportedAppUsage - public abstract boolean shouldShowRequestPermissionRationale(String permission); + public abstract boolean shouldShowRequestPermissionRationale(@NonNull String permissionName); /** * Returns an {@link android.content.Intent} suitable for passing to @@ -3928,6 +3947,7 @@ public abstract class PackageManager { * * @hide */ + @NonNull @UnsupportedAppUsage public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) { if (ArrayUtils.isEmpty(permissions)) { @@ -3946,8 +3966,8 @@ public abstract class PackageManager { * with each other: they can share the same user-id, run instrumentation * against each other, etc. * - * @param pkg1 First package name whose signature will be compared. - * @param pkg2 Second package name whose signature will be compared. + * @param packageName1 First package name whose signature will be compared. + * @param packageName2 Second package name whose signature will be compared. * * @return Returns an integer indicating whether all signatures on the * two packages match. The value is >= 0 ({@link #SIGNATURE_MATCH}) if @@ -3957,7 +3977,9 @@ public abstract class PackageManager { * @see #checkSignatures(int, int) */ @CheckResult - public abstract @SignatureResult int checkSignatures(String pkg1, String pkg2); + @SignatureResult + public abstract int checkSignatures(@NonNull String packageName1, + @NonNull String packageName2); /** * Like {@link #checkSignatures(String, String)}, but takes UIDs of @@ -4030,7 +4052,7 @@ public abstract class PackageManager { * @hide */ @UnsupportedAppUsage - public abstract int getUidForSharedUser(String sharedUserName) + public abstract int getUidForSharedUser(@NonNull String sharedUserName) throws NameNotFoundException; /** @@ -4049,6 +4071,7 @@ public abstract class PackageManager { * applications with data directory i.e. applications which had been * deleted with {@code DONT_DELETE_DATA} flag set). */ + @NonNull public abstract List getInstalledApplications(@ApplicationInfoFlags int flags); /** @@ -4071,6 +4094,7 @@ public abstract class PackageManager { * deleted with {@code DONT_DELETE_DATA} flag set). * @hide */ + @NonNull @TestApi public abstract List getInstalledApplicationsAsUser( @ApplicationInfoFlags int flags, @UserIdInt int userId); @@ -4121,7 +4145,7 @@ public abstract class PackageManager { * @see #getInstantAppCookieMaxBytes() * @see #clearInstantAppCookie() */ - public abstract boolean isInstantApp(String packageName); + public abstract boolean isInstantApp(@NonNull String packageName); /** * Gets the maximum size in bytes of the cookie data an instant app @@ -4208,6 +4232,7 @@ public abstract class PackageManager { * available on the system, or null if none are installed. * */ + @Nullable public abstract String[] getSystemSharedLibraryNames(); /** @@ -4296,6 +4321,7 @@ public abstract class PackageManager { * @return An array of FeatureInfo classes describing the features * that are available on the system, or null if there are none(!!). */ + @NonNull public abstract FeatureInfo[] getSystemAvailableFeatures(); /** @@ -4306,7 +4332,7 @@ public abstract class PackageManager { * * @return Returns true if the devices supports the feature, else false. */ - public abstract boolean hasSystemFeature(String name); + public abstract boolean hasSystemFeature(@NonNull String featureName); /** * Check whether the given feature name and version is one of the available @@ -4317,7 +4343,7 @@ public abstract class PackageManager { * * @return Returns true if the devices supports the feature, else false. */ - public abstract boolean hasSystemFeature(String name, int version); + public abstract boolean hasSystemFeature(@NonNull String featureName, int version); /** * Determine the best action to perform for a given Intent. This is how @@ -4345,7 +4371,9 @@ public abstract class PackageManager { * found and there is no default set, returns a ResolveInfo object * containing something else, such as the activity resolver. */ - public abstract ResolveInfo resolveActivity(Intent intent, @ResolveInfoFlags int flags); + @Nullable + public abstract ResolveInfo resolveActivity(@NonNull Intent intent, + @ResolveInfoFlags int flags); /** * Determine the best action to perform for a given Intent for a given user. @@ -4375,9 +4403,10 @@ public abstract class PackageManager { * containing something else, such as the activity resolver. * @hide */ + @Nullable @UnsupportedAppUsage - public abstract ResolveInfo resolveActivityAsUser(Intent intent, @ResolveInfoFlags int flags, - @UserIdInt int userId); + public abstract ResolveInfo resolveActivityAsUser(@NonNull Intent intent, + @ResolveInfoFlags int flags, @UserIdInt int userId); /** * Retrieve all activities that can be performed for the given intent. @@ -4394,7 +4423,8 @@ public abstract class PackageManager { * {@link #resolveActivity}. If there are no matching activities, an * empty list is returned. */ - public abstract List queryIntentActivities(Intent intent, + @Nullable + public abstract List queryIntentActivities(@NonNull Intent intent, @ResolveInfoFlags int flags); /** @@ -4414,8 +4444,9 @@ public abstract class PackageManager { * empty list is returned. * @hide */ + @Nullable @UnsupportedAppUsage - public abstract List queryIntentActivitiesAsUser(Intent intent, + public abstract List queryIntentActivitiesAsUser(@NonNull Intent intent, @ResolveInfoFlags int flags, @UserIdInt int userId); /** @@ -4469,8 +4500,9 @@ public abstract class PackageManager { * included by one of the specifics intents. If there are * no matching activities, an empty list is returned. */ + @NonNull public abstract List queryIntentActivityOptions(@Nullable ComponentName caller, - @Nullable Intent[] specifics, Intent intent, @ResolveInfoFlags int flags); + @Nullable Intent[] specifics, @NonNull Intent intent, @ResolveInfoFlags int flags); /** * Retrieve all receivers that can handle a broadcast of the given intent. @@ -4481,7 +4513,8 @@ public abstract class PackageManager { * each matching receiver, ordered from best to worst. If there are * no matching receivers, an empty list or null is returned. */ - public abstract List queryBroadcastReceivers(Intent intent, + @NonNull + public abstract List queryBroadcastReceivers(@NonNull Intent intent, @ResolveInfoFlags int flags); /** @@ -4496,9 +4529,10 @@ public abstract class PackageManager { * no matching receivers, an empty list or null is returned. * @hide */ + @NonNull @SystemApi @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) - public List queryBroadcastReceiversAsUser(Intent intent, + public List queryBroadcastReceiversAsUser(@NonNull Intent intent, @ResolveInfoFlags int flags, UserHandle userHandle) { return queryBroadcastReceiversAsUser(intent, flags, userHandle.getIdentifier()); } @@ -4506,15 +4540,17 @@ public abstract class PackageManager { /** * @hide */ + @NonNull @UnsupportedAppUsage - public abstract List queryBroadcastReceiversAsUser(Intent intent, + public abstract List queryBroadcastReceiversAsUser(@NonNull Intent intent, @ResolveInfoFlags int flags, @UserIdInt int userId); - /** {@hide} */ + /** @deprecated @hide */ + @NonNull @Deprecated @UnsupportedAppUsage - public List queryBroadcastReceivers(Intent intent, + public List queryBroadcastReceivers(@NonNull Intent intent, @ResolveInfoFlags int flags, @UserIdInt int userId) { final String msg = "Shame on you for calling the hidden API " + "queryBroadcastReceivers(). Shame!"; @@ -4536,13 +4572,15 @@ public abstract class PackageManager { * that was determined to be the best action. Returns null if no * matching service was found. */ - public abstract ResolveInfo resolveService(Intent intent, @ResolveInfoFlags int flags); + @Nullable + public abstract ResolveInfo resolveService(@NonNull Intent intent, @ResolveInfoFlags int flags); /** * @hide */ - public abstract ResolveInfo resolveServiceAsUser(Intent intent, @ResolveInfoFlags int flags, - @UserIdInt int userId); + @Nullable + public abstract ResolveInfo resolveServiceAsUser(@NonNull Intent intent, + @ResolveInfoFlags int flags, @UserIdInt int userId); /** * Retrieve all services that can match the given intent. @@ -4555,7 +4593,8 @@ public abstract class PackageManager { * {@link #resolveService}. If there are no matching services, an * empty list or null is returned. */ - public abstract List queryIntentServices(Intent intent, + @NonNull + public abstract List queryIntentServices(@NonNull Intent intent, @ResolveInfoFlags int flags); /** @@ -4571,8 +4610,9 @@ public abstract class PackageManager { * empty list or null is returned. * @hide */ + @NonNull @UnsupportedAppUsage - public abstract List queryIntentServicesAsUser(Intent intent, + public abstract List queryIntentServicesAsUser(@NonNull Intent intent, @ResolveInfoFlags int flags, @UserIdInt int userId); /** @@ -4608,9 +4648,10 @@ public abstract class PackageManager { * no matching services, an empty list or null is returned. * @hide */ + @NonNull @UnsupportedAppUsage public abstract List queryIntentContentProvidersAsUser( - Intent intent, @ResolveInfoFlags int flags, @UserIdInt int userId); + @NonNull Intent intent, @ResolveInfoFlags int flags, @UserIdInt int userId); /** * Retrieve all providers that can match the given intent. @@ -4642,7 +4683,8 @@ public abstract class PackageManager { * each matching provider, ordered from best to worst. If there are * no matching services, an empty list or null is returned. */ - public abstract List queryIntentContentProviders(Intent intent, + @NonNull + public abstract List queryIntentContentProviders(@NonNull Intent intent, @ResolveInfoFlags int flags); /** @@ -4659,21 +4701,23 @@ public abstract class PackageManager { * @return A {@link ProviderInfo} object containing information about the * provider. If a provider was not found, returns null. */ - public abstract ProviderInfo resolveContentProvider(String authority, + @Nullable + public abstract ProviderInfo resolveContentProvider(@NonNull String authority, @ComponentInfoFlags int flags); /** * Find a single content provider by its base path name. * - * @param name The name of the provider to find. + * @param providerName The name of the provider to find. * @param flags Additional option flags to modify the data returned. * @param userId The user id. * @return A {@link ProviderInfo} object containing information about the * provider. If a provider was not found, returns null. * @hide */ + @Nullable @UnsupportedAppUsage - public abstract ProviderInfo resolveContentProviderAsUser(String name, + public abstract ProviderInfo resolveContentProviderAsUser(@NonNull String providerName, @ComponentInfoFlags int flags, @UserIdInt int userId); /** @@ -4693,8 +4737,9 @@ public abstract class PackageManager { * processName is null, all known content providers. * If there are no matching providers, null is returned. */ + @NonNull public abstract List queryContentProviders( - String processName, int uid, @ComponentInfoFlags int flags); + @Nullable String processName, int uid, @ComponentInfoFlags int flags); /** * Same as {@link #queryContentProviders}, except when {@code metaDataKey} is not null, @@ -4711,8 +4756,9 @@ public abstract class PackageManager { * * @hide */ - public List queryContentProviders( - String processName, int uid, @ComponentInfoFlags int flags, String metaDataKey) { + @NonNull + public List queryContentProviders(@Nullable String processName, + int uid, @ComponentInfoFlags int flags, String metaDataKey) { // Provide the default implementation for mocks. return queryContentProviders(processName, uid, flags); } @@ -4730,7 +4776,8 @@ public abstract class PackageManager { * @throws NameNotFoundException if a package with the given name cannot be * found on the system. */ - public abstract InstrumentationInfo getInstrumentationInfo(ComponentName className, + @NonNull + public abstract InstrumentationInfo getInstrumentationInfo(@NonNull ComponentName className, @InstrumentationInfoFlags int flags) throws NameNotFoundException; /** @@ -4745,7 +4792,8 @@ public abstract class PackageManager { * entry for each matching instrumentation. If there are no * instrumentation available, returns an empty list. */ - public abstract List queryInstrumentation(String targetPackage, + @NonNull + public abstract List queryInstrumentation(@NonNull String targetPackage, @InstrumentationInfoFlags int flags); /** @@ -4765,8 +4813,9 @@ public abstract class PackageManager { * @return Returns a Drawable holding the requested image. Returns null if * an image could not be found for any reason. */ - public abstract Drawable getDrawable(String packageName, @DrawableRes int resid, - ApplicationInfo appInfo); + @Nullable + public abstract Drawable getDrawable(@NonNull String packageName, @DrawableRes int resid, + @Nullable ApplicationInfo appInfo); /** * Retrieve the icon associated with an activity. Given the full name of @@ -4783,7 +4832,8 @@ public abstract class PackageManager { * * @see #getActivityIcon(Intent) */ - public abstract Drawable getActivityIcon(ComponentName activityName) + @NonNull + public abstract Drawable getActivityIcon(@NonNull ComponentName activityName) throws NameNotFoundException; /** @@ -4803,7 +4853,8 @@ public abstract class PackageManager { * * @see #getActivityIcon(ComponentName) */ - public abstract Drawable getActivityIcon(Intent intent) + @NonNull + public abstract Drawable getActivityIcon(@NonNull Intent intent) throws NameNotFoundException; /** @@ -4819,7 +4870,8 @@ public abstract class PackageManager { * activity could not be loaded. * @see #getActivityBanner(Intent) */ - public abstract Drawable getActivityBanner(ComponentName activityName) + @Nullable + public abstract Drawable getActivityBanner(@NonNull ComponentName activityName) throws NameNotFoundException; /** @@ -4837,7 +4889,8 @@ public abstract class PackageManager { * matching the given intent could not be loaded. * @see #getActivityBanner(ComponentName) */ - public abstract Drawable getActivityBanner(Intent intent) + @Nullable + public abstract Drawable getActivityBanner(@NonNull Intent intent) throws NameNotFoundException; /** @@ -4846,6 +4899,7 @@ public abstract class PackageManager { * * @return Drawable Image of the icon. */ + @NonNull public abstract Drawable getDefaultActivityIcon(); /** @@ -4859,7 +4913,8 @@ public abstract class PackageManager { * * @see #getApplicationIcon(String) */ - public abstract Drawable getApplicationIcon(ApplicationInfo info); + @NonNull + public abstract Drawable getApplicationIcon(@NonNull ApplicationInfo info); /** * Retrieve the icon associated with an application. Given the name of the @@ -4877,7 +4932,8 @@ public abstract class PackageManager { * * @see #getApplicationIcon(ApplicationInfo) */ - public abstract Drawable getApplicationIcon(String packageName) + @NonNull + public abstract Drawable getApplicationIcon(@NonNull String packageName) throws NameNotFoundException; /** @@ -4888,7 +4944,8 @@ public abstract class PackageManager { * banner specified. * @see #getApplicationBanner(String) */ - public abstract Drawable getApplicationBanner(ApplicationInfo info); + @Nullable + public abstract Drawable getApplicationBanner(@NonNull ApplicationInfo info); /** * Retrieve the banner associated with an application. Given the name of the @@ -4904,7 +4961,8 @@ public abstract class PackageManager { * application could not be loaded. * @see #getApplicationBanner(ApplicationInfo) */ - public abstract Drawable getApplicationBanner(String packageName) + @Nullable + public abstract Drawable getApplicationBanner(@NonNull String packageName) throws NameNotFoundException; /** @@ -4920,7 +4978,8 @@ public abstract class PackageManager { * activity could not be loaded. * @see #getActivityLogo(Intent) */ - public abstract Drawable getActivityLogo(ComponentName activityName) + @Nullable + public abstract Drawable getActivityLogo(@NonNull ComponentName activityName) throws NameNotFoundException; /** @@ -4941,7 +5000,8 @@ public abstract class PackageManager { * * @see #getActivityLogo(ComponentName) */ - public abstract Drawable getActivityLogo(Intent intent) + @Nullable + public abstract Drawable getActivityLogo(@NonNull Intent intent) throws NameNotFoundException; /** @@ -4955,7 +5015,8 @@ public abstract class PackageManager { * * @see #getApplicationLogo(String) */ - public abstract Drawable getApplicationLogo(ApplicationInfo info); + @Nullable + public abstract Drawable getApplicationLogo(@NonNull ApplicationInfo info); /** * Retrieve the logo associated with an application. Given the name of the @@ -4974,7 +5035,8 @@ public abstract class PackageManager { * * @see #getApplicationLogo(ApplicationInfo) */ - public abstract Drawable getApplicationLogo(String packageName) + @Nullable + public abstract Drawable getApplicationLogo(@NonNull String packageName) throws NameNotFoundException; /** @@ -4988,12 +5050,14 @@ public abstract class PackageManager { * is performed in place and the original drawable is returned. *

    * - * @param icon The icon to badge. + * @param drawable The drawable to badge. * @param user The target user. * @return A drawable that combines the original icon and a badge as * determined by the system. */ - public abstract Drawable getUserBadgedIcon(Drawable icon, UserHandle user); + @NonNull + public abstract Drawable getUserBadgedIcon(@NonNull Drawable drawable, + @NonNull UserHandle user); /** * If the target user is a managed profile of the calling user or the caller @@ -5019,8 +5083,9 @@ public abstract class PackageManager { * @return A drawable that combines the original drawable and a badge as * determined by the system. */ - public abstract Drawable getUserBadgedDrawableForDensity(Drawable drawable, - UserHandle user, Rect badgeLocation, int badgeDensity); + @NonNull + public abstract Drawable getUserBadgedDrawableForDensity(@NonNull Drawable drawable, + @NonNull UserHandle user, @Nullable Rect badgeLocation, int badgeDensity); /** * If the target user is a managed profile of the calling user or the caller @@ -5034,8 +5099,9 @@ public abstract class PackageManager { * @return the drawable or null if no drawable is required. * @hide */ + @Nullable @UnsupportedAppUsage - public abstract Drawable getUserBadgeForDensity(UserHandle user, int density); + public abstract Drawable getUserBadgeForDensity(@NonNull UserHandle user, int density); /** * If the target user is a managed profile of the calling user or the caller @@ -5051,8 +5117,10 @@ public abstract class PackageManager { * @return the drawable or null if no drawable is required. * @hide */ + @Nullable @UnsupportedAppUsage - public abstract Drawable getUserBadgeForDensityNoBackground(UserHandle user, int density); + public abstract Drawable getUserBadgeForDensityNoBackground(@NonNull UserHandle user, + int density); /** * If the target user is a managed profile of the calling user or the caller @@ -5065,7 +5133,9 @@ public abstract class PackageManager { * @return A label that combines the original label and a badge as * determined by the system. */ - public abstract CharSequence getUserBadgedLabel(CharSequence label, UserHandle user); + @NonNull + public abstract CharSequence getUserBadgedLabel(@NonNull CharSequence label, + @NonNull UserHandle user); /** * Retrieve text from a package. This is a low-level API used by @@ -5084,8 +5154,9 @@ public abstract class PackageManager { * @return Returns a CharSequence holding the requested text. Returns null * if the text could not be found for any reason. */ - public abstract CharSequence getText(String packageName, @StringRes int resid, - ApplicationInfo appInfo); + @Nullable + public abstract CharSequence getText(@NonNull String packageName, @StringRes int resid, + @Nullable ApplicationInfo appInfo); /** * Retrieve an XML file from a package. This is a low-level API used to @@ -5103,8 +5174,9 @@ public abstract class PackageManager { * data. Returns null if the xml resource could not be found for any * reason. */ - public abstract XmlResourceParser getXml(String packageName, @XmlRes int resid, - ApplicationInfo appInfo); + @Nullable + public abstract XmlResourceParser getXml(@NonNull String packageName, @XmlRes int resid, + @Nullable ApplicationInfo appInfo); /** * Return the label to use for this application. @@ -5113,7 +5185,8 @@ public abstract class PackageManager { * it could not be found for any reason. * @param info The application to get the label of. */ - public abstract CharSequence getApplicationLabel(ApplicationInfo info); + @NonNull + public abstract CharSequence getApplicationLabel(@NonNull ApplicationInfo info); /** * Retrieve the resources associated with an activity. Given the full @@ -5130,7 +5203,8 @@ public abstract class PackageManager { * * @see #getResourcesForApplication(ApplicationInfo) */ - public abstract Resources getResourcesForActivity(ComponentName activityName) + @NonNull + public abstract Resources getResourcesForActivity(@NonNull ComponentName activityName) throws NameNotFoundException; /** @@ -5143,7 +5217,8 @@ public abstract class PackageManager { * @throws NameNotFoundException Thrown if the resources for the given * application could not be loaded (most likely because it was uninstalled). */ - public abstract Resources getResourcesForApplication(ApplicationInfo app) + @NonNull + public abstract Resources getResourcesForApplication(@NonNull ApplicationInfo app) throws NameNotFoundException; /** @@ -5152,7 +5227,7 @@ public abstract class PackageManager { * calls getResources() to return its application's resources. If the * appPackageName cannot be found, NameNotFoundException is thrown. * - * @param appPackageName Package name of the application whose resources + * @param packageName Package name of the application whose resources * are to be retrieved. * * @return Returns the application's Resources. @@ -5161,12 +5236,14 @@ public abstract class PackageManager { * * @see #getResourcesForApplication(ApplicationInfo) */ - public abstract Resources getResourcesForApplication(String appPackageName) + @NonNull + public abstract Resources getResourcesForApplication(@NonNull String packageName) throws NameNotFoundException; /** @hide */ + @NonNull @UnsupportedAppUsage - public abstract Resources getResourcesForApplicationAsUser(String appPackageName, + public abstract Resources getResourcesForApplicationAsUser(@NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException; /** @@ -5178,7 +5255,9 @@ public abstract class PackageManager { * @return A PackageInfo object containing information about the package * archive. If the package could not be parsed, returns null. */ - public PackageInfo getPackageArchiveInfo(String archiveFilePath, @PackageInfoFlags int flags) { + @Nullable + public PackageInfo getPackageArchiveInfo(@NonNull String archiveFilePath, + @PackageInfoFlags int flags) { final PackageParser parser = new PackageParser(); parser.setCallback(new PackageParser.CallbackImpl(this)); final File apkFile = new File(archiveFilePath); @@ -5212,7 +5291,8 @@ public abstract class PackageManager { */ @Deprecated @SystemApi - public abstract int installExistingPackage(String packageName) throws NameNotFoundException; + public abstract int installExistingPackage(@NonNull String packageName) + throws NameNotFoundException; /** * If there is already an application with the given package name installed @@ -5223,8 +5303,8 @@ public abstract class PackageManager { */ @Deprecated @SystemApi - public abstract int installExistingPackage(String packageName, @InstallReason int installReason) - throws NameNotFoundException; + public abstract int installExistingPackage(@NonNull String packageName, + @InstallReason int installReason) throws NameNotFoundException; /** * If there is already an application with the given package name installed @@ -5239,8 +5319,8 @@ public abstract class PackageManager { Manifest.permission.INSTALL_PACKAGES, Manifest.permission.INTERACT_ACROSS_USERS_FULL}) @UnsupportedAppUsage - public abstract int installExistingPackageAsUser(String packageName, @UserIdInt int userId) - throws NameNotFoundException; + public abstract int installExistingPackageAsUser(@NonNull String packageName, + @UserIdInt int userId) throws NameNotFoundException; /** * Allows a package listening to the @@ -5316,7 +5396,7 @@ public abstract class PackageManager { @SystemApi @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int verificationId, int verificationCode, - List failedDomains); + @NonNull List failedDomains); /** * Get the status of a Domain Verification Result for an IntentFilter. This is @@ -5340,7 +5420,8 @@ public abstract class PackageManager { */ @SystemApi @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL) - public abstract int getIntentVerificationStatusAsUser(String packageName, @UserIdInt int userId); + public abstract int getIntentVerificationStatusAsUser(@NonNull String packageName, + @UserIdInt int userId); /** * Allow to change the status of a Intent Verification status for all IntentFilter of an App. @@ -5364,8 +5445,8 @@ public abstract class PackageManager { */ @SystemApi @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) - public abstract boolean updateIntentVerificationStatusAsUser(String packageName, int status, - @UserIdInt int userId); + public abstract boolean updateIntentVerificationStatusAsUser(@NonNull String packageName, + int status, @UserIdInt int userId); /** * Get the list of IntentFilterVerificationInfo for a specific package and User. @@ -5379,9 +5460,10 @@ public abstract class PackageManager { * * @hide */ + @NonNull @SystemApi public abstract List getIntentFilterVerifications( - String packageName); + @NonNull String packageName); /** * Get the list of IntentFilter for a specific package. @@ -5394,8 +5476,9 @@ public abstract class PackageManager { * * @hide */ + @NonNull @SystemApi - public abstract List getAllIntentFilters(String packageName); + public abstract List getAllIntentFilters(@NonNull String packageName); /** * Get the default Browser package name for a specific user. @@ -5407,6 +5490,7 @@ public abstract class PackageManager { * * @hide */ + @Nullable @TestApi @SystemApi @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL) @@ -5428,7 +5512,7 @@ public abstract class PackageManager { @RequiresPermission(allOf = { Manifest.permission.SET_PREFERRED_APPLICATIONS, Manifest.permission.INTERACT_ACROSS_USERS_FULL}) - public abstract boolean setDefaultBrowserPackageNameAsUser(String packageName, + public abstract boolean setDefaultBrowserPackageNameAsUser(@Nullable String packageName, @UserIdInt int userId); /** @@ -5446,13 +5530,13 @@ public abstract class PackageManager { * @param installerPackageName The package name of the new installer. May be * null to clear the association. */ - public abstract void setInstallerPackageName(String targetPackage, - String installerPackageName); + public abstract void setInstallerPackageName(@NonNull String targetPackage, + @Nullable String installerPackageName); /** @hide */ @SystemApi @RequiresPermission(Manifest.permission.INSTALL_PACKAGES) - public abstract void setUpdateAvailable(String packageName, boolean updateAvaialble); + public abstract void setUpdateAvailable(@NonNull String packageName, boolean updateAvaialble); /** * Attempts to delete a package. Since this may take a little while, the @@ -5472,8 +5556,8 @@ public abstract class PackageManager { */ @RequiresPermission(Manifest.permission.DELETE_PACKAGES) @UnsupportedAppUsage - public abstract void deletePackage(String packageName, IPackageDeleteObserver observer, - @DeleteFlags int flags); + public abstract void deletePackage(@NonNull String packageName, + @Nullable IPackageDeleteObserver observer, @DeleteFlags int flags); /** * Attempts to delete a package. Since this may take a little while, the @@ -5495,7 +5579,8 @@ public abstract class PackageManager { Manifest.permission.INTERACT_ACROSS_USERS_FULL}) @UnsupportedAppUsage public abstract void deletePackageAsUser(@NonNull String packageName, - IPackageDeleteObserver observer, @DeleteFlags int flags, @UserIdInt int userId); + @Nullable IPackageDeleteObserver observer, @DeleteFlags int flags, + @UserIdInt int userId); /** * Retrieve the package name of the application that installed a package. This identifies @@ -5505,7 +5590,7 @@ public abstract class PackageManager { * @throws IllegalArgumentException if the given package name is not installed */ @Nullable - public abstract String getInstallerPackageName(String packageName); + public abstract String getInstallerPackageName(@NonNull String packageName); /** * Attempts to clear the user data directory of an application. @@ -5522,8 +5607,8 @@ public abstract class PackageManager { * @hide */ @UnsupportedAppUsage - public abstract void clearApplicationUserData(String packageName, - IPackageDataObserver observer); + public abstract void clearApplicationUserData(@NonNull String packageName, + @Nullable IPackageDataObserver observer); /** * Attempts to delete the cache files associated with an application. * Since this may take a little while, the result will @@ -5541,8 +5626,8 @@ public abstract class PackageManager { * @hide */ @UnsupportedAppUsage - public abstract void deleteApplicationCacheFiles(String packageName, - IPackageDataObserver observer); + public abstract void deleteApplicationCacheFiles(@NonNull String packageName, + @Nullable IPackageDataObserver observer); /** * Attempts to delete the cache files associated with an application for a given user. Since @@ -5563,8 +5648,8 @@ public abstract class PackageManager { * @hide */ @UnsupportedAppUsage - public abstract void deleteApplicationCacheFilesAsUser(String packageName, int userId, - IPackageDataObserver observer); + public abstract void deleteApplicationCacheFilesAsUser(@NonNull String packageName, + @UserIdInt int userId, @Nullable IPackageDataObserver observer); /** * Free storage by deleting LRU sorted list of cache files across @@ -5589,14 +5674,15 @@ public abstract class PackageManager { * @hide */ @UnsupportedAppUsage - public void freeStorageAndNotify(long freeStorageSize, IPackageDataObserver observer) { + public void freeStorageAndNotify(long freeStorageSize, + @Nullable IPackageDataObserver observer) { freeStorageAndNotify(null, freeStorageSize, observer); } /** {@hide} */ @UnsupportedAppUsage - public abstract void freeStorageAndNotify(String volumeUuid, long freeStorageSize, - IPackageDataObserver observer); + public abstract void freeStorageAndNotify(@Nullable String volumeUuid, long freeStorageSize, + @Nullable IPackageDataObserver observer); /** * Free storage by deleting LRU sorted list of cache files across @@ -5622,13 +5708,14 @@ public abstract class PackageManager { * @hide */ @UnsupportedAppUsage - public void freeStorage(long freeStorageSize, IntentSender pi) { + public void freeStorage(long freeStorageSize, @Nullable IntentSender pi) { freeStorage(null, freeStorageSize, pi); } /** {@hide} */ @UnsupportedAppUsage - public abstract void freeStorage(String volumeUuid, long freeStorageSize, IntentSender pi); + public abstract void freeStorage(@Nullable String volumeUuid, long freeStorageSize, + @Nullable IntentSender pi); /** * Retrieve the size information for a package. @@ -5651,8 +5738,8 @@ public abstract class PackageManager { */ @Deprecated @UnsupportedAppUsage - public abstract void getPackageSizeInfoAsUser(String packageName, @UserIdInt int userId, - IPackageStatsObserver observer); + public abstract void getPackageSizeInfoAsUser(@NonNull String packageName, + @UserIdInt int userId, @Nullable IPackageStatsObserver observer); /** * Like {@link #getPackageSizeInfoAsUser(String, int, IPackageStatsObserver)}, but @@ -5663,7 +5750,7 @@ public abstract class PackageManager { */ @Deprecated @UnsupportedAppUsage - public void getPackageSizeInfo(String packageName, IPackageStatsObserver observer) { + public void getPackageSizeInfo(@NonNull String packageName, IPackageStatsObserver observer) { getPackageSizeInfoAsUser(packageName, getUserId(), observer); } @@ -5676,7 +5763,7 @@ public abstract class PackageManager { * holders, see {@link android.app.role.RoleManager}. */ @Deprecated - public abstract void addPackageToPreferred(String packageName); + public abstract void addPackageToPreferred(@NonNull String packageName); /** * @deprecated This function no longer does anything. It is the platform's @@ -5687,7 +5774,7 @@ public abstract class PackageManager { * holders, see {@link android.app.role.RoleManager}. */ @Deprecated - public abstract void removePackageFromPreferred(String packageName); + public abstract void removePackageFromPreferred(@NonNull String packageName); /** * Retrieve the list of all currently configured preferred packages. The @@ -5705,6 +5792,7 @@ public abstract class PackageManager { * an app to be responsible for a particular role and to check current role * holders, see {@link android.app.role.RoleManager}. */ + @NonNull @Deprecated public abstract List getPreferredPackages(@PackageInfoFlags int flags); @@ -5731,8 +5819,8 @@ public abstract class PackageManager { * holders, see {@link android.app.role.RoleManager}. */ @Deprecated - public abstract void addPreferredActivity(IntentFilter filter, int match, - ComponentName[] set, ComponentName activity); + public abstract void addPreferredActivity(@NonNull IntentFilter filter, int match, + @Nullable ComponentName[] set, @NonNull ComponentName activity); /** * Same as {@link #addPreferredActivity(IntentFilter, int, @@ -5749,8 +5837,8 @@ public abstract class PackageManager { */ @Deprecated @UnsupportedAppUsage - public void addPreferredActivityAsUser(IntentFilter filter, int match, - ComponentName[] set, ComponentName activity, @UserIdInt int userId) { + public void addPreferredActivityAsUser(@NonNull IntentFilter filter, int match, + @Nullable ComponentName[] set, @NonNull ComponentName activity, @UserIdInt int userId) { throw new RuntimeException("Not implemented. Must override in a subclass."); } @@ -5781,8 +5869,8 @@ public abstract class PackageManager { */ @Deprecated @UnsupportedAppUsage - public abstract void replacePreferredActivity(IntentFilter filter, int match, - ComponentName[] set, ComponentName activity); + public abstract void replacePreferredActivity(@NonNull IntentFilter filter, int match, + @Nullable ComponentName[] set, @NonNull ComponentName activity); /** * Replaces an existing preferred activity mapping to the system, and if that were not present @@ -5826,8 +5914,8 @@ public abstract class PackageManager { */ @Deprecated @UnsupportedAppUsage - public void replacePreferredActivityAsUser(IntentFilter filter, int match, - ComponentName[] set, ComponentName activity, @UserIdInt int userId) { + public void replacePreferredActivityAsUser(@NonNull IntentFilter filter, int match, + @Nullable ComponentName[] set, @NonNull ComponentName activity, @UserIdInt int userId) { throw new RuntimeException("Not implemented. Must override in a subclass."); } @@ -5848,7 +5936,7 @@ public abstract class PackageManager { * holders, see {@link android.app.role.RoleManager}. */ @Deprecated - public abstract void clearPackagePreferredActivities(String packageName); + public abstract void clearPackagePreferredActivities(@NonNull String packageName); /** * Retrieve all preferred activities, previously added with @@ -5876,15 +5964,16 @@ public abstract class PackageManager { */ @Deprecated public abstract int getPreferredActivities(@NonNull List outFilters, - @NonNull List outActivities, String packageName); + @NonNull List outActivities, @Nullable String packageName); /** * Ask for the set of available 'home' activities and the current explicit * default, if any. * @hide */ + @Nullable @UnsupportedAppUsage - public abstract ComponentName getHomeActivities(List outActivities); + public abstract ComponentName getHomeActivities(@NonNull List outActivities); /** * Set the enabled setting for a package component (activity, receiver, service, provider). @@ -5984,7 +6073,7 @@ public abstract class PackageManager { * @hide */ @UnsupportedAppUsage - public abstract void flushPackageRestrictionsAsUser(int userId); + public abstract void flushPackageRestrictionsAsUser(@UserIdInt int userId); /** * Puts the package in a hidden state, which is almost like an uninstalled state, @@ -5994,8 +6083,8 @@ public abstract class PackageManager { * @hide */ @UnsupportedAppUsage - public abstract boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, - UserHandle userHandle); + public abstract boolean setApplicationHiddenSettingAsUser(@NonNull String packageName, + boolean hidden, @NonNull UserHandle userHandle); /** * Returns the hidden state of a package. @@ -6003,8 +6092,8 @@ public abstract class PackageManager { * @hide */ @UnsupportedAppUsage - public abstract boolean getApplicationHiddenSettingAsUser(String packageName, - UserHandle userHandle); + public abstract boolean getApplicationHiddenSettingAsUser(@NonNull String packageName, + @NonNull UserHandle userHandle); /** * Return whether the device has been booted into safe mode. @@ -6020,7 +6109,8 @@ public abstract class PackageManager { */ @SystemApi @RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS) - public abstract void addOnPermissionsChangeListener(OnPermissionsChangedListener listener); + public abstract void addOnPermissionsChangeListener( + @NonNull OnPermissionsChangedListener listener); /** * Remvoes a listener for permission changes for installed packages. @@ -6031,7 +6121,8 @@ public abstract class PackageManager { */ @SystemApi @RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS) - public abstract void removeOnPermissionsChangeListener(OnPermissionsChangedListener listener); + public abstract void removeOnPermissionsChangeListener( + @NonNull OnPermissionsChangedListener listener); /** * Return the {@link KeySet} associated with the String alias for this @@ -6041,14 +6132,16 @@ public abstract class PackageManager { * application's AndroidManifest.xml. * @hide */ + @NonNull @UnsupportedAppUsage - public abstract KeySet getKeySetByAlias(String packageName, String alias); + public abstract KeySet getKeySetByAlias(@NonNull String packageName, @NonNull String alias); /** Return the signing {@link KeySet} for this application. * @hide */ + @NonNull @UnsupportedAppUsage - public abstract KeySet getSigningKeySet(String packageName); + public abstract KeySet getSigningKeySet(@NonNull String packageName); /** * Return whether the package denoted by packageName has been signed by all @@ -6058,7 +6151,7 @@ public abstract class PackageManager { * @hide */ @UnsupportedAppUsage - public abstract boolean isSignedBy(String packageName, KeySet ks); + public abstract boolean isSignedBy(@NonNull String packageName, @NonNull KeySet ks); /** * Return whether the package denoted by packageName has been signed by all @@ -6067,7 +6160,7 @@ public abstract class PackageManager { * @hide */ @UnsupportedAppUsage - public abstract boolean isSignedByExactly(String packageName, KeySet ks); + public abstract boolean isSignedByExactly(@NonNull String packageName, @NonNull KeySet ks); /** * Flag to denote no restrictions. This should be used to clear any restrictions that may have @@ -6284,7 +6377,7 @@ public abstract class PackageManager { * @hide */ @UnsupportedAppUsage - public abstract boolean isPackageSuspendedForUser(String packageName, int userId); + public abstract boolean isPackageSuspendedForUser(@NonNull String packageName, int userId); /** * Query if an app is currently suspended. @@ -6294,7 +6387,7 @@ public abstract class PackageManager { * * @see #isPackageSuspended() */ - public boolean isPackageSuspended(String packageName) throws NameNotFoundException { + public boolean isPackageSuspended(@NonNull String packageName) throws NameNotFoundException { throw new UnsupportedOperationException("isPackageSuspended not implemented"); } @@ -6378,23 +6471,26 @@ public abstract class PackageManager { /** {@hide} */ @UnsupportedAppUsage - public abstract void registerMoveCallback(MoveCallback callback, Handler handler); + public abstract void registerMoveCallback(@NonNull MoveCallback callback, + @NonNull Handler handler); /** {@hide} */ @UnsupportedAppUsage - public abstract void unregisterMoveCallback(MoveCallback callback); + public abstract void unregisterMoveCallback(@NonNull MoveCallback callback); /** {@hide} */ @UnsupportedAppUsage - public abstract int movePackage(String packageName, VolumeInfo vol); + public abstract int movePackage(@NonNull String packageName, @NonNull VolumeInfo vol); /** {@hide} */ @UnsupportedAppUsage - public abstract @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app); + public abstract @Nullable VolumeInfo getPackageCurrentVolume(@NonNull ApplicationInfo app); /** {@hide} */ + @NonNull @UnsupportedAppUsage - public abstract @NonNull List getPackageCandidateVolumes(ApplicationInfo app); + public abstract List getPackageCandidateVolumes( + @NonNull ApplicationInfo app); /** {@hide} */ - public abstract int movePrimaryStorage(VolumeInfo vol); + public abstract int movePrimaryStorage(@NonNull VolumeInfo vol); /** {@hide} */ public abstract @Nullable VolumeInfo getPrimaryStorageCurrentVolume(); /** {@hide} */ @@ -6407,6 +6503,7 @@ public abstract class PackageManager { * @return identity that uniquely identifies current device * @hide */ + @NonNull public abstract VerifierDeviceIdentity getVerifierDeviceIdentity(); /** @@ -6437,8 +6534,8 @@ public abstract class PackageManager { * @hide */ @UnsupportedAppUsage - public abstract void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, - int targetUserId, int flags); + public abstract void addCrossProfileIntentFilter(@NonNull IntentFilter filter, + @UserIdInt int sourceUserId, @UserIdInt int targetUserId, int flags); /** * Clearing {@code CrossProfileIntentFilter}s which have the specified user @@ -6448,27 +6545,32 @@ public abstract class PackageManager { * @hide */ @UnsupportedAppUsage - public abstract void clearCrossProfileIntentFilters(int sourceUserId); + public abstract void clearCrossProfileIntentFilters(@UserIdInt int sourceUserId); /** * @hide */ + @NonNull @UnsupportedAppUsage - public abstract Drawable loadItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo); + public abstract Drawable loadItemIcon(@NonNull PackageItemInfo itemInfo, + @Nullable ApplicationInfo appInfo); /** * @hide */ + @NonNull @UnsupportedAppUsage - public abstract Drawable loadUnbadgedItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo); + public abstract Drawable loadUnbadgedItemIcon(@NonNull PackageItemInfo itemInfo, + @Nullable ApplicationInfo appInfo); /** {@hide} */ @UnsupportedAppUsage - public abstract boolean isPackageAvailable(String packageName); + public abstract boolean isPackageAvailable(@NonNull String packageName); /** {@hide} */ + @NonNull @UnsupportedAppUsage - public static String installStatusToString(int status, String msg) { + public static String installStatusToString(int status, @Nullable String msg) { final String str = installStatusToString(status); if (msg != null) { return str + ": " + msg; @@ -6478,6 +6580,7 @@ public abstract class PackageManager { } /** {@hide} */ + @NonNull @UnsupportedAppUsage public static String installStatusToString(int status) { switch (status) { @@ -6582,7 +6685,8 @@ public abstract class PackageManager { } /** {@hide} */ - public static String deleteStatusToString(int status, String msg) { + @NonNull + public static String deleteStatusToString(int status, @Nullable String msg) { final String str = deleteStatusToString(status); if (msg != null) { return str + ": " + msg; @@ -6592,6 +6696,7 @@ public abstract class PackageManager { } /** {@hide} */ + @NonNull @UnsupportedAppUsage public static String deleteStatusToString(int status) { switch (status) { @@ -6621,6 +6726,7 @@ public abstract class PackageManager { } /** {@hide} */ + @NonNull public static String permissionFlagToString(int flag) { switch (flag) { case FLAG_PERMISSION_GRANTED_BY_DEFAULT: return "GRANTED_BY_DEFAULT"; @@ -6633,7 +6739,6 @@ public abstract class PackageManager { case FLAG_PERMISSION_REVOKE_WHEN_REQUESTED: return "REVOKE_WHEN_REQUESTED"; case FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED: return "USER_SENSITIVE_WHEN_GRANTED"; case FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED: return "USER_SENSITIVE_WHEN_DENIED"; - case FLAG_PERMISSION_HIDDEN: return "HIDDEN"; default: return Integer.toString(flag); } } @@ -6668,8 +6773,8 @@ public abstract class PackageManager { * @hide */ @TestApi - public abstract @InstallReason int getInstallReason(String packageName, - @NonNull UserHandle user); + @InstallReason + public abstract int getInstallReason(@NonNull String packageName, @NonNull UserHandle user); /** * Checks whether the calling package is allowed to request package installs through package @@ -6695,6 +6800,7 @@ public abstract class PackageManager { * @see {@link android.content.Intent#ACTION_INSTANT_APP_RESOLVER_SETTINGS} * @hide */ + @Nullable @SystemApi public abstract ComponentName getInstantAppResolverSettingsComponent(); @@ -6705,6 +6811,7 @@ public abstract class PackageManager { * @see {@link android.content.Intent#ACTION_INSTALL_INSTANT_APP_PACKAGE} * @hide */ + @Nullable @SystemApi public abstract ComponentName getInstantAppInstallerComponent(); @@ -6714,7 +6821,9 @@ public abstract class PackageManager { * @see {@link android.provider.Settings.Secure#ANDROID_ID} * @hide */ - public abstract String getInstantAppAndroidId(String packageName, @NonNull UserHandle user); + @Nullable + public abstract String getInstantAppAndroidId(@NonNull String packageName, + @NonNull UserHandle user); /** * Callback use to notify the callers of module registration that the operation @@ -6757,7 +6866,7 @@ public abstract class PackageManager { * @hide */ @SystemApi - public abstract void registerDexModule(String dexModulePath, + public abstract void registerDexModule(@NonNull String dexModulePath, @Nullable DexModuleRegisterCallback callback); /** @@ -6837,8 +6946,8 @@ public abstract class PackageManager { * @param type representation of the {@code certificate} * @return true if this package was or is signed by exactly the certificate {@code certificate} */ - public boolean hasSigningCertificate( - String packageName, byte[] certificate, @CertificateInputType int type) { + public boolean hasSigningCertificate(@NonNull String packageName, @NonNull byte[] certificate, + @CertificateInputType int type) { throw new UnsupportedOperationException( "hasSigningCertificate not implemented in subclass"); } @@ -6862,7 +6971,7 @@ public abstract class PackageManager { * @return true if this package was or is signed by exactly the certificate {@code certificate} */ public boolean hasSigningCertificate( - int uid, byte[] certificate, @CertificateInputType int type) { + int uid, @NonNull byte[] certificate, @CertificateInputType int type) { throw new UnsupportedOperationException( "hasSigningCertificate not implemented in subclass"); } @@ -6872,16 +6981,28 @@ public abstract class PackageManager { * * @hide */ + @Nullable public String getSystemTextClassifierPackageName() { throw new UnsupportedOperationException( "getSystemTextClassifierPackageName not implemented in subclass"); } + /** + * @return attention service package name, or null if there's none. + * + * @hide + */ + public String getAttentionServicePackageName() { + throw new UnsupportedOperationException( + "getAttentionServicePackageName not implemented in subclass"); + } + /** * @return the wellbeing app package name, or null if it's not defined by the OEM. * * @hide */ + @Nullable @TestApi public String getWellbeingPackageName() { throw new UnsupportedOperationException( @@ -6893,6 +7014,7 @@ public abstract class PackageManager { * * @hide */ + @Nullable public String getAppPredictionServicePackageName() { throw new UnsupportedOperationException( "getAppPredictionServicePackageName not implemented in subclass"); @@ -6903,6 +7025,7 @@ public abstract class PackageManager { * * @hide */ + @Nullable public String getSystemCaptionsServicePackageName() { throw new UnsupportedOperationException( "getSystemCaptionsServicePackageName not implemented in subclass"); @@ -6916,6 +7039,7 @@ public abstract class PackageManager { */ @SystemApi @TestApi + @Nullable public String getIncidentReportApproverPackageName() { throw new UnsupportedOperationException( "getIncidentReportApproverPackageName not implemented in subclass"); @@ -6927,7 +7051,7 @@ public abstract class PackageManager { * * @hide */ - public boolean isPackageStateProtected(String packageName, int userId) { + public boolean isPackageStateProtected(@NonNull String packageName, @UserIdInt int userId) { throw new UnsupportedOperationException( "isPackageStateProtected not implemented in subclass"); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index b480939ae450cea6e521fd03c17f17dcf7a72db3..93bc6d7eb583e9b3727aa9c0db5d035c9aa5ffbb 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -166,6 +166,9 @@ public class PackageParser { private static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO = 1.333f; private static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH = 1f; + private static final int DEFAULT_MIN_SDK_VERSION = 1; + private static final int DEFAULT_TARGET_SDK_VERSION = 0; + // TODO: switch outError users to PackageParserException // TODO: refactor "codePath" to "apkPath" @@ -466,6 +469,8 @@ public class PackageParser { public final int versionCodeMajor; public final int revisionCode; public final int installLocation; + public final int minSdkVersion; + public final int targetSdkVersion; public final VerifierInfo[] verifiers; public final SigningDetails signingDetails; public final boolean coreApp; @@ -484,7 +489,8 @@ public class PackageParser { int revisionCode, int installLocation, List verifiers, SigningDetails signingDetails, boolean coreApp, boolean debuggable, boolean multiArch, boolean use32bitAbi, - boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits) { + boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits, + int minSdkVersion, int targetSdkVersion) { this.codePath = codePath; this.packageName = packageName; this.splitName = splitName; @@ -505,6 +511,8 @@ public class PackageParser { this.extractNativeLibs = extractNativeLibs; this.isolatedSplits = isolatedSplits; this.isSplitRequired = isSplitRequired; + this.minSdkVersion = minSdkVersion; + this.targetSdkVersion = targetSdkVersion; } public long getLongVersionCode() { @@ -1712,6 +1720,8 @@ public class PackageParser { int installLocation = PARSE_DEFAULT_INSTALL_LOCATION; int versionCode = 0; int versionCodeMajor = 0; + int targetSdkVersion = DEFAULT_TARGET_SDK_VERSION; + int minSdkVersion = DEFAULT_MIN_SDK_VERSION; int revisionCode = 0; boolean coreApp = false; boolean debuggable = false; @@ -1749,7 +1759,7 @@ public class PackageParser { } } - // Only search the tree when the tag is directly below + // Only search the tree when the tag is the direct child of tag int type; final int searchDepth = parser.getDepth() + 1; @@ -1800,13 +1810,25 @@ public class PackageParser { PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, " tag requires 'android:name' attribute"); } + } else if (TAG_USES_SDK.equals(parser.getName())) { + for (int i = 0; i < attrs.getAttributeCount(); ++i) { + final String attr = attrs.getAttributeName(i); + if ("targetSdkVersion".equals(attr)) { + targetSdkVersion = attrs.getAttributeIntValue(i, + DEFAULT_TARGET_SDK_VERSION); + } + if ("minSdkVersion".equals(attr)) { + minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION); + } + } } } return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit, configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor, revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable, - multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, isolatedSplits); + multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, isolatedSplits, + minSdkVersion, targetSdkVersion); } /** @@ -2525,46 +2547,26 @@ public class PackageParser { // STOPSHIP(b/112545973): remove once feature enabled by default if (!StorageManager.hasIsolatedStorage()) { if ("android".equals(pkg.packageName)) { - final ArraySet newGroups = new ArraySet<>(); - newGroups.add(android.Manifest.permission_group.MEDIA_AURAL); - newGroups.add(android.Manifest.permission_group.MEDIA_VISUAL); - - for (int i = pkg.permissionGroups.size() - 1; i >= 0; i--) { - final PermissionGroup pg = pkg.permissionGroups.get(i); - if (newGroups.contains(pg.info.name)) { - pkg.permissionGroups.remove(i); - } - } - final ArraySet newPermissions = new ArraySet<>(); - newPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO); - newPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO); - newPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES); newPermissions.add(android.Manifest.permission.ACCESS_MEDIA_LOCATION); newPermissions.add(android.Manifest.permission.WRITE_OBB); - final ArraySet removedPermissions = new ArraySet<>(); - removedPermissions.add(android.Manifest.permission.READ_EXTERNAL_STORAGE); - removedPermissions.add(android.Manifest.permission.WRITE_EXTERNAL_STORAGE); - for (int i = pkg.permissions.size() - 1; i >= 0; i--) { final Permission p = pkg.permissions.get(i); if (newPermissions.contains(p.info.name)) { pkg.permissions.remove(i); - } else if (removedPermissions.contains(p.info.name)) { - p.info.flags &= ~PermissionInfo.FLAG_REMOVED; } } } } else { if (FORCE_AUDIO_PACKAGES.contains(pkg.packageName)) { - pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO); + pkg.requestedPermissions.add(android.Manifest.permission.READ_EXTERNAL_STORAGE); } if (FORCE_VIDEO_PACKAGES.contains(pkg.packageName)) { - pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO); + pkg.requestedPermissions.add(android.Manifest.permission.READ_EXTERNAL_STORAGE); } if (FORCE_IMAGES_PACKAGES.contains(pkg.packageName)) { - pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES); + pkg.requestedPermissions.add(android.Manifest.permission.READ_EXTERNAL_STORAGE); } } @@ -3667,6 +3669,12 @@ public class PackageParser { ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE; } + if (sa.getBoolean( + R.styleable.AndroidManifestApplication_allowExternalStorageSandbox, + owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q)) { + ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX; + } + ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0); ai.minAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_minAspectRatio, 0); @@ -4774,7 +4782,7 @@ public class PackageParser { // except for watches which always supported 1:1. minAspectRatio = owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q ? 0 - : mCallback.hasFeature(FEATURE_WATCH) + : (mCallback != null && mCallback.hasFeature(FEATURE_WATCH)) ? DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH : DEFAULT_PRE_Q_MIN_ASPECT_RATIO; } @@ -8350,69 +8358,37 @@ public class PackageParser { } } - public static PackageInfo generatePackageInfoFromApex(File apexFile, boolean collectCerts) + // TODO(b/129261524): Clean up API + /** + * PackageInfo parser specifically for apex files. + * NOTE: It will collect certificates + * + * @param apexFile + * @return PackageInfo + * @throws PackageParserException + */ + public static PackageInfo generatePackageInfoFromApex(File apexFile, int flags) throws PackageParserException { - PackageInfo pi = new PackageInfo(); - int parseFlags = 0; - if (collectCerts) { - parseFlags |= PARSE_COLLECT_CERTIFICATES; - try { - if (apexFile.getCanonicalPath().startsWith("/system")) { - // Don't need verify the APK integrity of APEXes on /system, just like - // we don't do that for APKs. - // TODO(b/126514108): we may be able to do this for APEXes on /data as well. - parseFlags |= PARSE_IS_SYSTEM_DIR; - } - } catch (IOException e) { - throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, - "Failed to get path for " + apexFile.getPath(), e); - } - } - - // TODO(b/123086053) properly fill in the ApplicationInfo with data from AndroidManifest - // Add ApplicationInfo to the PackageInfo. - ApplicationInfo ai = new ApplicationInfo(); - ai.sourceDir = apexFile.getPath(); - ai.flags = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED; - ai.enabled = true; - ai.targetSdkVersion = 28; - ai.targetSandboxVersion = 0; - pi.applicationInfo = ai; - - - // TODO(b/123052859): We should avoid these repeated calls to parseApkLite each time - // we want to generate information for APEX modules. - PackageParser.ApkLite apk = PackageParser.parseApkLite(apexFile, parseFlags); - - pi.packageName = apk.packageName; - ai.packageName = apk.packageName; - pi.setLongVersionCode(apk.getLongVersionCode()); - ai.setVersionCode(apk.getLongVersionCode()); - - if (collectCerts) { - if (apk.signingDetails.hasPastSigningCertificates()) { - // Package has included signing certificate rotation information. Return - // the oldest cert so that programmatic checks keep working even if unaware - // of key rotation. - pi.signatures = new Signature[1]; - pi.signatures[0] = apk.signingDetails.pastSigningCertificates[0]; - } else if (apk.signingDetails.hasSignatures()) { - // otherwise keep old behavior - int numberOfSigs = apk.signingDetails.signatures.length; - pi.signatures = new Signature[numberOfSigs]; - System.arraycopy(apk.signingDetails.signatures, 0, pi.signatures, 0, - numberOfSigs); - } + PackageParser pp = new PackageParser(); + final Package p = pp.parsePackage(apexFile, flags, false); + PackageUserState state = new PackageUserState(); + PackageInfo pi = generatePackageInfo(p, EmptyArray.INT, flags, 0, 0, + Collections.emptySet(), state); + + pi.applicationInfo.sourceDir = apexFile.getPath(); + pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED; + pi.isApex = true; - if (apk.signingDetails != SigningDetails.UNKNOWN) { + // Collect certificates + if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) { + collectCertificates(p, apexFile, false); + if (p.mSigningDetails != SigningDetails.UNKNOWN) { // only return a valid SigningInfo if there is signing information to report - pi.signingInfo = new SigningInfo(apk.signingDetails); + pi.signingInfo = new SigningInfo(p.mSigningDetails); } else { pi.signingInfo = null; } } - - pi.isApex = true; return pi; } } diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 49b4cb01c6a6ec7fd07055808d56492749a7d432..514015fe0c86a6c104a64b90d94611f405715311 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -1263,12 +1263,19 @@ public final class AssetManager implements AutoCloseable { */ @UnsupportedAppUsage public boolean isUpToDate() { - for (ApkAssets apkAssets : getApkAssets()) { - if (!apkAssets.isUpToDate()) { + synchronized (this) { + if (!mOpen) { return false; } + + for (ApkAssets apkAssets : mApkAssets) { + if (!apkAssets.isUpToDate()) { + return false; + } + } + + return true; } - return true; } /** diff --git a/core/java/android/database/TranslatingCursor.java b/core/java/android/database/TranslatingCursor.java index d9165b4c100891d3416b578a8d968bbb04969a34..35cbdc7648191c084e118682801434431146a8d6 100644 --- a/core/java/android/database/TranslatingCursor.java +++ b/core/java/android/database/TranslatingCursor.java @@ -22,6 +22,7 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.os.CancellationSignal; +import android.util.ArraySet; import com.android.internal.util.ArrayUtils; @@ -59,7 +60,7 @@ public class TranslatingCursor extends CrossProcessCursorWrapper { private final boolean mDropLast; private final int mAuxiliaryColumnIndex; - private final int[] mTranslateColumnIndices; + private final ArraySet mTranslateColumnIndices; public TranslatingCursor(@NonNull Cursor cursor, @NonNull Config config, @NonNull Translator translator, boolean dropLast) { @@ -70,9 +71,12 @@ public class TranslatingCursor extends CrossProcessCursorWrapper { mDropLast = dropLast; mAuxiliaryColumnIndex = cursor.getColumnIndexOrThrow(config.auxiliaryColumn); - mTranslateColumnIndices = new int[config.translateColumns.length]; - for (int i = 0; i < mTranslateColumnIndices.length; ++i) { - mTranslateColumnIndices[i] = cursor.getColumnIndex(config.translateColumns[i]); + mTranslateColumnIndices = new ArraySet<>(); + for (int i = 0; i < cursor.getColumnCount(); ++i) { + String columnName = cursor.getColumnName(i); + if (ArrayUtils.contains(config.translateColumns, columnName)) { + mTranslateColumnIndices.add(i); + } } } diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java index a73a71946bc76874125a3971ce4e2a418b9adc96..014bc242e17aa952129559c7b5ab9319bb3ca6e0 100644 --- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java +++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java @@ -39,6 +39,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; +import java.util.regex.Matcher; import java.util.regex.Pattern; /** @@ -47,11 +48,15 @@ import java.util.regex.Pattern; */ public class SQLiteQueryBuilder { private static final String TAG = "SQLiteQueryBuilder"; + private static final Pattern sLimitPattern = Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?"); + private static final Pattern sAggregationPattern = Pattern.compile( + "(?i)(AVG|COUNT|MAX|MIN|SUM|TOTAL)\\((.+)\\)"); private Map mProjectionMap = null; private List mProjectionGreylist = null; + private boolean mProjectionAggregationAllowed = false; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private String mTables = ""; @@ -80,7 +85,7 @@ public class SQLiteQueryBuilder { * Get if the query is marked as {@code DISTINCT}, as last configured by * {@link #setDistinct(boolean)}. */ - public boolean getDistinct() { + public boolean isDistinct() { return mDistinct; } @@ -203,6 +208,16 @@ public class SQLiteQueryBuilder { return mProjectionGreylist; } + /** {@hide} */ + public void setProjectionAggregationAllowed(boolean projectionAggregationAllowed) { + mProjectionAggregationAllowed = projectionAggregationAllowed; + } + + /** {@hide} */ + public boolean isProjectionAggregationAllowed() { + return mProjectionAggregationAllowed; + } + /** * Sets the cursor factory to be used for the query. You can use * one factory for all queries on a database but it is normally @@ -215,7 +230,7 @@ public class SQLiteQueryBuilder { } /** - * Sets the cursor factory to be used for the query, as last configured by + * Gets the cursor factory to be used for the query, as last configured by * {@link #setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory)}. */ public @Nullable SQLiteDatabase.CursorFactory getCursorFactory() { @@ -251,7 +266,7 @@ public class SQLiteQueryBuilder { * Get if the query is marked as strict, as last configured by * {@link #setStrict(boolean)}. */ - public boolean getStrict() { + public boolean isStrict() { return mStrict; } @@ -842,26 +857,48 @@ public class SQLiteQueryBuilder { return query.toString(); } + private static @NonNull String maybeWithOperator(@Nullable String operator, + @NonNull String column) { + if (operator != null) { + return operator + "(" + column + ")"; + } else { + return column; + } + } + + /** {@hide} */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - private String[] computeProjection(String[] projectionIn) { + public String[] computeProjection(String[] projectionIn) { if (projectionIn != null && projectionIn.length > 0) { if (mProjectionMap != null) { String[] projection = new String[projectionIn.length]; int length = projectionIn.length; for (int i = 0; i < length; i++) { + String operator = null; String userColumn = projectionIn[i]; String column = mProjectionMap.get(userColumn); + // If aggregation is allowed, extract the underlying column + // that may be aggregated + if (mProjectionAggregationAllowed) { + final Matcher matcher = sAggregationPattern.matcher(userColumn); + if (matcher.matches()) { + operator = matcher.group(1); + userColumn = matcher.group(2); + column = mProjectionMap.get(userColumn); + } + } + if (column != null) { - projection[i] = column; + projection[i] = maybeWithOperator(operator, column); continue; } if (!mStrict && ( userColumn.contains(" AS ") || userColumn.contains(" as "))) { /* A column alias already exist */ - projection[i] = userColumn; + projection[i] = maybeWithOperator(operator, userColumn); continue; } @@ -878,7 +915,7 @@ public class SQLiteQueryBuilder { if (match) { Log.w(TAG, "Allowing abusive custom column: " + userColumn); - projection[i] = userColumn; + projection[i] = maybeWithOperator(operator, userColumn); continue; } } @@ -911,7 +948,8 @@ public class SQLiteQueryBuilder { return null; } - private @Nullable String computeWhere(@Nullable String selection) { + /** {@hide} */ + public @Nullable String computeWhere(@Nullable String selection) { final boolean hasInternal = !TextUtils.isEmpty(mWhereClause); final boolean hasExternal = !TextUtils.isEmpty(selection); diff --git a/core/java/android/hardware/camera2/CaptureFailure.java b/core/java/android/hardware/camera2/CaptureFailure.java index cd2bc5f943e7b4e221bec7afe1061cd951911747..20ca4a338f01b53c6e85e85762d31a41247bd16e 100644 --- a/core/java/android/hardware/camera2/CaptureFailure.java +++ b/core/java/android/hardware/camera2/CaptureFailure.java @@ -17,6 +17,7 @@ package android.hardware.camera2; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -61,17 +62,19 @@ public class CaptureFailure { private final boolean mDropped; private final int mSequenceId; private final long mFrameNumber; + private final String mErrorPhysicalCameraId; /** * @hide */ public CaptureFailure(CaptureRequest request, int reason, - boolean dropped, int sequenceId, long frameNumber) { + boolean dropped, int sequenceId, long frameNumber, String errorPhysicalCameraId) { mRequest = request; mReason = reason; mDropped = dropped; mSequenceId = sequenceId; mFrameNumber = frameNumber; + mErrorPhysicalCameraId = errorPhysicalCameraId; } /** @@ -155,4 +158,17 @@ public class CaptureFailure { public int getSequenceId() { return mSequenceId; } + + /** + * The physical camera device ID in case the capture failure comes from a {@link CaptureRequest} + * with configured physical camera streams for a logical camera. + * + * @return String The physical camera device ID of the respective failing output. + * {@code null} in case the capture request has no associated physical camera device. + * @see CaptureRequest.Builder#setPhysicalCameraKey + * @see android.hardware.camera2.params.OutputConfiguration#setPhysicalCameraId + */ + public @Nullable String getPhysicalCameraId() { + return mErrorPhysicalCameraId; + } } diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 57b608f0fd84767aa467aa3cce00433a5cbd246a..fc12b090b2f3f7051c5fdaf7af8940e3d5a248dd 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -2232,6 +2232,7 @@ public class CameraDeviceImpl extends CameraDevice final int requestId = resultExtras.getRequestId(); final int subsequenceId = resultExtras.getSubsequenceId(); final long frameNumber = resultExtras.getFrameNumber(); + final String errorPhysicalCameraId = resultExtras.getErrorPhysicalCameraId(); final CaptureCallbackHolder holder = CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); @@ -2287,7 +2288,8 @@ public class CameraDeviceImpl extends CameraDevice reason, /*dropped*/ mayHaveBuffers, requestId, - frameNumber); + frameNumber, + errorPhysicalCameraId); failureDispatch = new Runnable() { @Override diff --git a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java index edc3d91eaf121d7c0691ffdefc36d37709aedca1..1ff5bd562f2e7bbb724b4b700c3149bd66c8d320 100644 --- a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java +++ b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java @@ -29,6 +29,7 @@ public class CaptureResultExtras implements Parcelable { private long frameNumber; private int partialResultCount; private int errorStreamId; + private String errorPhysicalCameraId; public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -49,7 +50,8 @@ public class CaptureResultExtras implements Parcelable { public CaptureResultExtras(int requestId, int subsequenceId, int afTriggerId, int precaptureTriggerId, long frameNumber, - int partialResultCount, int errorStreamId) { + int partialResultCount, int errorStreamId, + String errorPhysicalCameraId) { this.requestId = requestId; this.subsequenceId = subsequenceId; this.afTriggerId = afTriggerId; @@ -57,6 +59,7 @@ public class CaptureResultExtras implements Parcelable { this.frameNumber = frameNumber; this.partialResultCount = partialResultCount; this.errorStreamId = errorStreamId; + this.errorPhysicalCameraId = errorPhysicalCameraId; } @Override @@ -73,6 +76,12 @@ public class CaptureResultExtras implements Parcelable { dest.writeLong(frameNumber); dest.writeInt(partialResultCount); dest.writeInt(errorStreamId); + if ((errorPhysicalCameraId != null) && !errorPhysicalCameraId.isEmpty()) { + dest.writeBoolean(true); + dest.writeString(errorPhysicalCameraId); + } else { + dest.writeBoolean(false); + } } public void readFromParcel(Parcel in) { @@ -83,6 +92,14 @@ public class CaptureResultExtras implements Parcelable { frameNumber = in.readLong(); partialResultCount = in.readInt(); errorStreamId = in.readInt(); + boolean errorPhysicalCameraIdPresent = in.readBoolean(); + if (errorPhysicalCameraIdPresent) { + errorPhysicalCameraId = in.readString(); + } + } + + public String getErrorPhysicalCameraId() { + return errorPhysicalCameraId; } public int getRequestId() { diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java index aff09f2a45f266dfccf7a02e0a94de8db90c2b86..908ed0913ffc0f6076eb5a7db6d51e48ce16bf4e 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java +++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java @@ -109,11 +109,11 @@ public class LegacyCameraDevice implements AutoCloseable { } if (holder == null) { return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, - ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE); + ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, null); } return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(), /*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(), - /*partialResultCount*/1, errorStreamId); + /*partialResultCount*/1, errorStreamId, null); } /** diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java index da0899be8cfa60ebaf7ae6abbfd04274721bcd6a..690df1acc7e70469cb8d0bf87e0fa966603db402 100644 --- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java @@ -333,6 +333,16 @@ public class RequestThreadManager { startPreview(); } + private void disconnectCallbackSurfaces() { + for (Surface s : mCallbackOutputs) { + try { + LegacyCameraDevice.disconnectSurface(s); + } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { + Log.d(TAG, "Surface abandoned, skipping...", e); + } + } + } + private void configureOutputs(Collection> outputs) { if (DEBUG) { String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces"); @@ -370,14 +380,8 @@ public class RequestThreadManager { mGLThreadManager.waitUntilIdle(); } resetJpegSurfaceFormats(mCallbackOutputs); + disconnectCallbackSurfaces(); - for (Surface s : mCallbackOutputs) { - try { - LegacyCameraDevice.disconnectSurface(s); - } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { - Log.w(TAG, "Surface abandoned, skipping...", e); - } - } mPreviewOutputs.clear(); mCallbackOutputs.clear(); mJpegSurfaceIds.clear(); @@ -972,11 +976,11 @@ public class RequestThreadManager { mGLThreadManager.quit(); mGLThreadManager = null; } + disconnectCallbackSurfaces(); if (mCamera != null) { mCamera.release(); mCamera = null; } - resetJpegSurfaceFormats(mCallbackOutputs); break; case RequestHandlerThread.MSG_POKE_IDLE_HANDLER: // OK: Ignore message. diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java index b122f199bc6ff5288254a6199b09bd3f4fbedeb4..c45b8ed52187907f966a48a05b5ca4f898f0f8c8 100644 --- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java +++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java @@ -48,7 +48,10 @@ public class AmbientDisplayConfiguration { return pulseOnNotificationEnabled(user) || pulseOnLongPressEnabled(user) || alwaysOnEnabled(user) - || wakeScreenGestureEnabled(user); + || wakeScreenGestureEnabled(user) + || pickupGestureEnabled(user) + || tapGestureEnabled(user) + || doubleTapGestureEnabled(user); } /** {@hide} */ diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index ae93cf01977629b598e7ab1ee825935df66bcae5..6ba4a30abe2243bd5bcc601824430ec1bd27c105 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -1934,6 +1934,8 @@ public class ConnectivityManager { @NonNull Callback callback) { ParcelFileDescriptor dup; try { + // Dup is needed here as the pfd inside the socket is owned by the IpSecService, + // which cannot be obtained by the app process. dup = ParcelFileDescriptor.dup(socket.getFileDescriptor()); } catch (IOException ignored) { // Construct an invalid fd, so that if the user later calls start(), it will fail with @@ -1975,6 +1977,7 @@ public class ConnectivityManager { @NonNull Callback callback) { ParcelFileDescriptor dup; try { + // TODO: Consider remove unnecessary dup. dup = pfd.dup(); } catch (IOException ignored) { // Construct an invalid fd, so that if the user later calls start(), it will fail with @@ -3231,7 +3234,7 @@ public class ConnectivityManager { * * @hide */ - public void onPreCheck(Network network) {} + public void onPreCheck(@NonNull Network network) {} /** * Called when the framework connects and has declared a new network ready for use. @@ -3244,8 +3247,9 @@ public class ConnectivityManager { * @param blocked Whether access to the {@link Network} is blocked due to system policy. * @hide */ - public void onAvailable(Network network, NetworkCapabilities networkCapabilities, - LinkProperties linkProperties, boolean blocked) { + public void onAvailable(@NonNull Network network, + @NonNull NetworkCapabilities networkCapabilities, + @NonNull LinkProperties linkProperties, boolean blocked) { // Internally only this method is called when a new network is available, and // it calls the callback in the same way and order that older versions used // to call so as not to change the behavior. @@ -3269,7 +3273,7 @@ public class ConnectivityManager { * * @param network The {@link Network} of the satisfying network. */ - public void onAvailable(Network network) {} + public void onAvailable(@NonNull Network network) {} /** * Called when the network is about to be disconnected. Often paired with an @@ -3285,7 +3289,7 @@ public class ConnectivityManager { * network connected. Note that the network may suffer a * hard loss at any time. */ - public void onLosing(Network network, int maxMsToLive) {} + public void onLosing(@NonNull Network network, int maxMsToLive) {} /** * Called when the framework has a hard loss of the network or when the @@ -3293,7 +3297,7 @@ public class ConnectivityManager { * * @param network The {@link Network} lost. */ - public void onLost(Network network) {} + public void onLost(@NonNull Network network) {} /** * Called if no network is found in the timeout time specified in @@ -3313,8 +3317,8 @@ public class ConnectivityManager { * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this * network. */ - public void onCapabilitiesChanged(Network network, - NetworkCapabilities networkCapabilities) {} + public void onCapabilitiesChanged(@NonNull Network network, + @NonNull NetworkCapabilities networkCapabilities) {} /** * Called when the network the framework connected to for this request @@ -3323,7 +3327,8 @@ public class ConnectivityManager { * @param network The {@link Network} whose link properties have changed. * @param linkProperties The new {@link LinkProperties} for this network. */ - public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {} + public void onLinkPropertiesChanged(@NonNull Network network, + @NonNull LinkProperties linkProperties) {} /** * Called when the network the framework connected to for this request @@ -3334,7 +3339,7 @@ public class ConnectivityManager { * a tunnel, etc. * @hide */ - public void onNetworkSuspended(Network network) {} + public void onNetworkSuspended(@NonNull Network network) {} /** * Called when the network the framework connected to for this request @@ -3342,7 +3347,7 @@ public class ConnectivityManager { * preceded by a matching {@link NetworkCallback#onNetworkSuspended} call. * @hide */ - public void onNetworkResumed(Network network) {} + public void onNetworkResumed(@NonNull Network network) {} /** * Called when access to the specified network is blocked or unblocked. diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java index 59802514c7a3c33a027332072a5d2f5531fe2bf6..06c32c675a3199c8ea0e60432a0d3a7f4569cc26 100644 --- a/core/java/android/net/DnsResolver.java +++ b/core/java/android/net/DnsResolver.java @@ -22,6 +22,10 @@ import static android.net.NetworkUtils.resNetworkResult; import static android.net.NetworkUtils.resNetworkSend; import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR; import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; +import static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.AF_INET6; +import static android.system.OsConstants.IPPROTO_UDP; +import static android.system.OsConstants.SOCK_DGRAM; import android.annotation.CallbackExecutor; import android.annotation.IntDef; @@ -30,12 +34,18 @@ import android.annotation.Nullable; import android.os.CancellationSignal; import android.os.Looper; import android.system.ErrnoException; +import android.system.Os; import android.util.Log; +import libcore.io.IoUtils; + import java.io.FileDescriptor; +import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; @@ -52,6 +62,7 @@ public final class DnsResolver { private static final String TAG = "DnsResolver"; private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR; private static final int MAXPACKET = 8 * 1024; + private static final int SLEEP_TIME_MS = 2; @IntDef(prefix = { "CLASS_" }, value = { CLASS_IN @@ -188,9 +199,9 @@ public final class DnsResolver { * Send a raw DNS query. * The answer will be provided asynchronously through the provided {@link AnswerCallback}. * - * @param network {@link Network} specifying which network for querying. + * @param network {@link Network} specifying which network to query on. * {@code null} for query on default network. - * @param query blob message + * @param query blob message to query * @param flags flags as a combination of the FLAGS_* constants * @param executor The {@link Executor} that the callback should be executed on. * @param cancellationSignal used by the caller to signal if the query should be @@ -205,26 +216,32 @@ public final class DnsResolver { if (cancellationSignal != null && cancellationSignal.isCanceled()) { return; } + final Object lock = new Object(); final FileDescriptor queryfd; try { queryfd = resNetworkSend((network != null ? network.netId : NETID_UNSET), query, query.length, flags); } catch (ErrnoException e) { - callback.onQueryException(e); + executor.execute(() -> { + callback.onQueryException(e); + }); return; } - maybeAddCancellationSignal(cancellationSignal, queryfd); - registerFDListener(executor, queryfd, callback); + synchronized (lock) { + registerFDListener(executor, queryfd, callback, cancellationSignal, lock); + if (cancellationSignal == null) return; + addCancellationSignal(cancellationSignal, queryfd, lock); + } } /** * Send a DNS query with the specified name, class and query type. * The answer will be provided asynchronously through the provided {@link AnswerCallback}. * - * @param network {@link Network} specifying which network for querying. + * @param network {@link Network} specifying which network to query on. * {@code null} for query on default network. - * @param domain domain name for querying + * @param domain domain name to query * @param nsClass dns class as one of the CLASS_* constants * @param nsType dns resource record (RR) type as one of the TYPE_* constants * @param flags flags as a combination of the FLAGS_* constants @@ -242,40 +259,187 @@ public final class DnsResolver { if (cancellationSignal != null && cancellationSignal.isCanceled()) { return; } + final Object lock = new Object(); final FileDescriptor queryfd; try { queryfd = resNetworkQuery((network != null ? network.netId : NETID_UNSET), domain, nsClass, nsType, flags); } catch (ErrnoException e) { - callback.onQueryException(e); + executor.execute(() -> { + callback.onQueryException(e); + }); + return; + } + synchronized (lock) { + registerFDListener(executor, queryfd, callback, cancellationSignal, lock); + if (cancellationSignal == null) return; + addCancellationSignal(cancellationSignal, queryfd, lock); + } + } + + private class InetAddressAnswerAccumulator extends InetAddressAnswerCallback { + private final List mAllAnswers; + private ParseException mParseException; + private ErrnoException mErrnoException; + private final InetAddressAnswerCallback mUserCallback; + private final int mTargetAnswerCount; + private int mReceivedAnswerCount = 0; + + InetAddressAnswerAccumulator(int size, @NonNull InetAddressAnswerCallback callback) { + mTargetAnswerCount = size; + mAllAnswers = new ArrayList<>(); + mUserCallback = callback; + } + + private boolean maybeReportException() { + if (mErrnoException != null) { + mUserCallback.onQueryException(mErrnoException); + return true; + } + if (mParseException != null) { + mUserCallback.onParseException(mParseException); + return true; + } + return false; + } + + private void maybeReportAnswer() { + if (++mReceivedAnswerCount != mTargetAnswerCount) return; + if (mAllAnswers.isEmpty() && maybeReportException()) return; + // TODO: Do RFC6724 sort. + mUserCallback.onAnswer(mAllAnswers); + } + + @Override + public void onAnswer(@NonNull List answer) { + mAllAnswers.addAll(answer); + maybeReportAnswer(); + } + + @Override + public void onParseException(@NonNull ParseException e) { + mParseException = e; + maybeReportAnswer(); + } + + @Override + public void onQueryException(@NonNull ErrnoException e) { + mErrnoException = e; + maybeReportAnswer(); + } + } + + /** + * Send a DNS query with the specified name, get back a set of InetAddresses asynchronously. + * The answer will be provided asynchronously through the provided + * {@link InetAddressAnswerCallback}. + * + * @param network {@link Network} specifying which network to query on. + * {@code null} for query on default network. + * @param domain domain name to query + * @param flags flags as a combination of the FLAGS_* constants + * @param executor The {@link Executor} that the callback should be executed on. + * @param cancellationSignal used by the caller to signal if the query should be + * cancelled. May be {@code null}. + * @param callback an {@link InetAddressAnswerCallback} which will be called to notify the + * caller of the result of dns query. + */ + public void query(@Nullable Network network, @NonNull String domain, @QueryFlag int flags, + @NonNull @CallbackExecutor Executor executor, + @Nullable CancellationSignal cancellationSignal, + @NonNull InetAddressAnswerCallback callback) { + if (cancellationSignal != null && cancellationSignal.isCanceled()) { return; } + final Object lock = new Object(); + final boolean queryIpv6 = haveIpv6(network); + final boolean queryIpv4 = haveIpv4(network); + + final FileDescriptor v4fd; + final FileDescriptor v6fd; + + int queryCount = 0; + + if (queryIpv6) { + try { + v6fd = resNetworkQuery((network != null + ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_AAAA, flags); + } catch (ErrnoException e) { + executor.execute(() -> { + callback.onQueryException(e); + }); + return; + } + queryCount++; + } else v6fd = null; + + // TODO: Use device flag to control the sleep time. + // Avoiding gateways drop packets if queries are sent too close together + try { + Thread.sleep(SLEEP_TIME_MS); + } catch (InterruptedException ex) { } + + if (queryIpv4) { + try { + v4fd = resNetworkQuery((network != null + ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_A, flags); + } catch (ErrnoException e) { + if (queryIpv6) resNetworkCancel(v6fd); // Closes fd, marks it invalid. + executor.execute(() -> { + callback.onQueryException(e); + }); + return; + } + queryCount++; + } else v4fd = null; + + final InetAddressAnswerAccumulator accumulator = + new InetAddressAnswerAccumulator(queryCount, callback); - maybeAddCancellationSignal(cancellationSignal, queryfd); - registerFDListener(executor, queryfd, callback); + synchronized (lock) { + if (queryIpv6) { + registerFDListener(executor, v6fd, accumulator, cancellationSignal, lock); + } + if (queryIpv4) { + registerFDListener(executor, v4fd, accumulator, cancellationSignal, lock); + } + if (cancellationSignal == null) return; + cancellationSignal.setOnCancelListener(() -> { + synchronized (lock) { + if (queryIpv4) cancelQuery(v4fd); + if (queryIpv6) cancelQuery(v6fd); + } + }); + } } private void registerFDListener(@NonNull Executor executor, - @NonNull FileDescriptor queryfd, @NonNull AnswerCallback answerCallback) { + @NonNull FileDescriptor queryfd, @NonNull AnswerCallback answerCallback, + @Nullable CancellationSignal cancellationSignal, @NonNull Object lock) { Looper.getMainLooper().getQueue().addOnFileDescriptorEventListener( queryfd, FD_EVENTS, (fd, events) -> { executor.execute(() -> { - byte[] answerbuf = null; - try { - answerbuf = resNetworkResult(fd); - } catch (ErrnoException e) { - Log.e(TAG, "resNetworkResult:" + e.toString()); - answerCallback.onQueryException(e); - return; - } - - try { - answerCallback.onAnswer( - answerCallback.parser.parse(answerbuf)); - } catch (ParseException e) { - answerCallback.onParseException(e); + synchronized (lock) { + if (cancellationSignal != null && cancellationSignal.isCanceled()) { + return; + } + byte[] answerbuf = null; + try { + answerbuf = resNetworkResult(fd); // Closes fd, marks it invalid. + } catch (ErrnoException e) { + Log.e(TAG, "resNetworkResult:" + e.toString()); + answerCallback.onQueryException(e); + return; + } + + try { + answerCallback.onAnswer( + answerCallback.parser.parse(answerbuf)); + } catch (ParseException e) { + answerCallback.onParseException(e); + } } }); // Unregister this fd listener @@ -283,15 +447,51 @@ public final class DnsResolver { }); } - private void maybeAddCancellationSignal(@Nullable CancellationSignal cancellationSignal, - @NonNull FileDescriptor queryfd) { - if (cancellationSignal == null) return; - cancellationSignal.setOnCancelListener( - () -> { - Looper.getMainLooper().getQueue() - .removeOnFileDescriptorEventListener(queryfd); - resNetworkCancel(queryfd); - }); + private void cancelQuery(@NonNull FileDescriptor queryfd) { + if (!queryfd.valid()) return; + Looper.getMainLooper().getQueue().removeOnFileDescriptorEventListener(queryfd); + resNetworkCancel(queryfd); // Closes fd, marks it invalid. + } + + private void addCancellationSignal(@NonNull CancellationSignal cancellationSignal, + @NonNull FileDescriptor queryfd, @NonNull Object lock) { + cancellationSignal.setOnCancelListener(() -> { + synchronized (lock) { + cancelQuery(queryfd); + } + }); + } + + // These two functions match the behaviour of have_ipv4 and have_ipv6 in the native resolver. + private boolean haveIpv4(@Nullable Network network) { + final SocketAddress addrIpv4 = + new InetSocketAddress(InetAddresses.parseNumericAddress("8.8.8.8"), 0); + return checkConnectivity(network, AF_INET, addrIpv4); + } + + private boolean haveIpv6(@Nullable Network network) { + final SocketAddress addrIpv6 = + new InetSocketAddress(InetAddresses.parseNumericAddress("2000::"), 0); + return checkConnectivity(network, AF_INET6, addrIpv6); + } + + private boolean checkConnectivity(@Nullable Network network, + int domain, @NonNull SocketAddress addr) { + final FileDescriptor socket; + try { + socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP); + } catch (ErrnoException e) { + return false; + } + try { + if (network != null) network.bindSocket(socket); + Os.connect(socket, addr); + } catch (IOException | ErrnoException e) { + return false; + } finally { + IoUtils.closeQuietly(socket); + } + return true; } private static class DnsAddressAnswer extends DnsPacket { diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java index 402bffdc2a97b8b6478c05a65bee7a42bd4820e7..8cfe6df678c79bc26257388f9b5f23087e928474 100644 --- a/core/java/android/net/IpPrefix.java +++ b/core/java/android/net/IpPrefix.java @@ -16,6 +16,7 @@ package android.net; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -71,7 +72,7 @@ public final class IpPrefix implements Parcelable { * * @hide */ - public IpPrefix(@NonNull byte[] address, int prefixLength) { + public IpPrefix(@NonNull byte[] address, @IntRange(from = 0, to = 128) int prefixLength) { this.address = address.clone(); this.prefixLength = prefixLength; checkAndMaskAddressAndPrefixLength(); @@ -88,7 +89,7 @@ public final class IpPrefix implements Parcelable { */ @SystemApi @TestApi - public IpPrefix(@NonNull InetAddress address, int prefixLength) { + public IpPrefix(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength) { // We don't reuse the (byte[], int) constructor because it calls clone() on the byte array, // which is unnecessary because getAddress() already returns a clone. this.address = address.getAddress(); @@ -150,13 +151,13 @@ public final class IpPrefix implements Parcelable { * * @return the address in the form of a byte array. */ - public InetAddress getAddress() { + public @NonNull InetAddress getAddress() { try { return InetAddress.getByAddress(address); } catch (UnknownHostException e) { // Cannot happen. InetAddress.getByAddress can only throw an exception if the byte // array is the wrong length, but we check that in the constructor. - return null; + throw new IllegalArgumentException("Address is invalid"); } } @@ -166,7 +167,7 @@ public final class IpPrefix implements Parcelable { * * @return the address in the form of a byte array. */ - public byte[] getRawAddress() { + public @NonNull byte[] getRawAddress() { return address.clone(); } @@ -175,6 +176,7 @@ public final class IpPrefix implements Parcelable { * * @return the prefix length. */ + @IntRange(from = 0, to = 128) public int getPrefixLength() { return prefixLength; } @@ -183,10 +185,10 @@ public final class IpPrefix implements Parcelable { * Determines whether the prefix contains the specified address. * * @param address An {@link InetAddress} to test. - * @return {@code true} if the prefix covers the given address. + * @return {@code true} if the prefix covers the given address. {@code false} otherwise. */ - public boolean contains(InetAddress address) { - byte[] addrBytes = (address == null) ? null : address.getAddress(); + public boolean contains(@NonNull InetAddress address) { + byte[] addrBytes = address.getAddress(); if (addrBytes == null || addrBytes.length != this.address.length) { return false; } @@ -201,7 +203,7 @@ public final class IpPrefix implements Parcelable { * @param otherPrefix the prefix to test * @hide */ - public boolean containsPrefix(IpPrefix otherPrefix) { + public boolean containsPrefix(@NonNull IpPrefix otherPrefix) { if (otherPrefix.getPrefixLength() < prefixLength) return false; final byte[] otherAddress = otherPrefix.getRawAddress(); NetworkUtils.maskRawAddress(otherAddress, prefixLength); diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java index 333603f3a0f24d36b4f0fb4107bce2dcf6c64fe5..93dd2e4d7717b9d1b4e42336b91c499ec69bd9da 100644 --- a/core/java/android/net/LinkAddress.java +++ b/core/java/android/net/LinkAddress.java @@ -25,6 +25,7 @@ import static android.system.OsConstants.RT_SCOPE_LINK; import static android.system.OsConstants.RT_SCOPE_SITE; import static android.system.OsConstants.RT_SCOPE_UNIVERSE; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -170,7 +171,7 @@ public class LinkAddress implements Parcelable { * Constructs a new {@code LinkAddress} from an {@code InetAddress} and prefix length, with * the specified flags and scope. Flags and scope are not checked for validity. * @param address The IP address. - * @param prefixLength The prefix length. + * @param prefixLength The prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6). * @param flags A bitmask of {@code IFA_F_*} values representing properties of the address. * @param scope An integer defining the scope in which the address is unique (e.g., * {@link OsConstants#RT_SCOPE_LINK} or {@link OsConstants#RT_SCOPE_SITE}). @@ -178,7 +179,8 @@ public class LinkAddress implements Parcelable { */ @SystemApi @TestApi - public LinkAddress(InetAddress address, int prefixLength, int flags, int scope) { + public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength, + int flags, int scope) { init(address, prefixLength, flags, scope); } @@ -186,12 +188,13 @@ public class LinkAddress implements Parcelable { * Constructs a new {@code LinkAddress} from an {@code InetAddress} and a prefix length. * The flags are set to zero and the scope is determined from the address. * @param address The IP address. - * @param prefixLength The prefix length. + * @param prefixLength The prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6). * @hide */ @SystemApi @TestApi - public LinkAddress(@NonNull InetAddress address, int prefixLength) { + public LinkAddress(@NonNull InetAddress address, + @IntRange(from = 0, to = 128) int prefixLength) { this(address, prefixLength, 0, 0); this.scope = scopeForUnicastAddress(address); } @@ -202,7 +205,7 @@ public class LinkAddress implements Parcelable { * @param interfaceAddress The interface address. * @hide */ - public LinkAddress(InterfaceAddress interfaceAddress) { + public LinkAddress(@NonNull InterfaceAddress interfaceAddress) { this(interfaceAddress.getAddress(), interfaceAddress.getNetworkPrefixLength()); } @@ -306,6 +309,7 @@ public class LinkAddress implements Parcelable { /** * Returns the prefix length of this {@code LinkAddress}. */ + @IntRange(from = 0, to = 128) public int getPrefixLength() { return prefixLength; } @@ -316,6 +320,7 @@ public class LinkAddress implements Parcelable { * @hide */ @UnsupportedAppUsage + @IntRange(from = 0, to = 128) public int getNetworkPrefixLength() { return getPrefixLength(); } diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index d5ca6642a3c378a509e0a67028dde11304fc39db..d3f48acdd40ea0d2e98be3dd6f9588ad8e10b115 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -316,9 +316,6 @@ public final class LinkProperties implements Parcelable { @SystemApi @TestApi public boolean removeLinkAddress(@NonNull LinkAddress toRemove) { - if (toRemove == null) { - return false; - } int i = findLinkAddressIndex(toRemove); if (i >= 0) { mLinkAddresses.remove(i); @@ -391,10 +388,7 @@ public final class LinkProperties implements Parcelable { @TestApi @SystemApi public boolean removeDnsServer(@NonNull InetAddress dnsServer) { - if (dnsServer != null) { - return mDnses.remove(dnsServer); - } - return false; + return mDnses.remove(dnsServer); } /** diff --git a/core/java/android/net/NetworkCapabilities.aidl b/core/java/android/net/NetworkCapabilities.aidl index cd7d71cad9786442e7cb5511272040264f29cc86..01d328605de49c4f49404f55bdc714d4a1fa777b 100644 --- a/core/java/android/net/NetworkCapabilities.aidl +++ b/core/java/android/net/NetworkCapabilities.aidl @@ -17,5 +17,5 @@ package android.net; -parcelable NetworkCapabilities; +@JavaOnlyStableParcelable parcelable NetworkCapabilities; diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 51cbed48e02175baec847a0a11e345fc7528430f..4270740cc72234f1984ee355ded757ed2f404325 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.net.NetworkCapabilities.NetCapability; @@ -343,10 +344,14 @@ public class NetworkRequest implements Parcelable { * current value. A value of {@code SIGNAL_STRENGTH_UNSPECIFIED} means no value when * received and has no effect when requesting a callback. * + *

    This method requires the caller to hold the + * {@link android.Manifest.permission#NETWORK_SIGNAL_STRENGTH_WAKEUP} permission + * * @param signalStrength the bearer-specific signal strength. * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public @NonNull Builder setSignalStrength(int signalStrength) { mNetworkCapabilities.setSignalStrength(signalStrength); return this; diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java index b0239c839348d8a3913e76387227e12f1be664c3..52d3fc48a3a5848dd5c439d03e0ef594dcd96457 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -16,6 +16,8 @@ package android.net; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -24,6 +26,8 @@ import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; @@ -51,20 +55,32 @@ import java.util.Objects; * (IPv4 or IPv6). */ public final class RouteInfo implements Parcelable { + /** @hide */ + @IntDef(value = { + RTN_UNICAST, + RTN_UNREACHABLE, + RTN_THROW, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RouteType {} + /** * The IP destination address for this route. */ + @NonNull private final IpPrefix mDestination; /** * The gateway address for this route. */ @UnsupportedAppUsage + @Nullable private final InetAddress mGateway; /** * The interface for this route. */ + @Nullable private final String mInterface; @@ -108,13 +124,14 @@ public final class RouteInfo implements Parcelable { * @param destination the destination prefix * @param gateway the IP address to route packets through * @param iface the interface name to send packets on + * @param type the type of this route * * @hide */ @SystemApi @TestApi public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway, - @Nullable String iface, int type) { + @Nullable String iface, @RouteType int type) { switch (type) { case RTN_UNICAST: case RTN_UNREACHABLE: @@ -173,10 +190,24 @@ public final class RouteInfo implements Parcelable { } /** - * @hide + * Constructs a {@code RouteInfo} object. + * + * If destination is null, then gateway must be specified and the + * constructed route is either the IPv4 default route 0.0.0.0 + * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default + * route ::/0 if gateway is an instance of {@link Inet6Address}. + *

    + * Destination and gateway may not both be null. + * + * @param destination the destination address and prefix in an {@link IpPrefix} + * @param gateway the {@link InetAddress} to route packets through + * @param iface the interface name to send packets on + * + * @hide */ @UnsupportedAppUsage - public RouteInfo(IpPrefix destination, InetAddress gateway, String iface) { + public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway, + @Nullable String iface) { this(destination, gateway, iface, RTN_UNICAST); } @@ -184,7 +215,8 @@ public final class RouteInfo implements Parcelable { * @hide */ @UnsupportedAppUsage - public RouteInfo(LinkAddress destination, InetAddress gateway, String iface) { + public RouteInfo(@Nullable LinkAddress destination, @Nullable InetAddress gateway, + @Nullable String iface) { this(destination == null ? null : new IpPrefix(destination.getAddress(), destination.getPrefixLength()), gateway, iface); @@ -205,7 +237,7 @@ public final class RouteInfo implements Parcelable { * * @hide */ - public RouteInfo(IpPrefix destination, InetAddress gateway) { + public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway) { this(destination, gateway, null); } @@ -215,7 +247,7 @@ public final class RouteInfo implements Parcelable { * TODO: Remove this. */ @UnsupportedAppUsage - public RouteInfo(LinkAddress destination, InetAddress gateway) { + public RouteInfo(@Nullable LinkAddress destination, @Nullable InetAddress gateway) { this(destination, gateway, null); } @@ -227,7 +259,7 @@ public final class RouteInfo implements Parcelable { * @hide */ @UnsupportedAppUsage - public RouteInfo(InetAddress gateway) { + public RouteInfo(@NonNull InetAddress gateway) { this((IpPrefix) null, gateway, null); } @@ -239,35 +271,36 @@ public final class RouteInfo implements Parcelable { * * @hide */ - public RouteInfo(IpPrefix destination) { + public RouteInfo(@NonNull IpPrefix destination) { this(destination, null, null); } /** * @hide */ - public RouteInfo(LinkAddress destination) { + public RouteInfo(@NonNull LinkAddress destination) { this(destination, null, null); } /** * @hide */ - public RouteInfo(IpPrefix destination, int type) { + public RouteInfo(@NonNull IpPrefix destination, @RouteType int type) { this(destination, null, null, type); } /** * @hide */ - public static RouteInfo makeHostRoute(InetAddress host, String iface) { + public static RouteInfo makeHostRoute(@NonNull InetAddress host, @Nullable String iface) { return makeHostRoute(host, null, iface); } /** * @hide */ - public static RouteInfo makeHostRoute(InetAddress host, InetAddress gateway, String iface) { + public static RouteInfo makeHostRoute(@Nullable InetAddress host, @Nullable InetAddress gateway, + @Nullable String iface) { if (host == null) return null; if (host instanceof Inet4Address) { @@ -290,6 +323,7 @@ public final class RouteInfo implements Parcelable { * * @return {@link IpPrefix} specifying the destination. This is never {@code null}. */ + @NonNull public IpPrefix getDestination() { return mDestination; } @@ -298,6 +332,7 @@ public final class RouteInfo implements Parcelable { * TODO: Convert callers to use IpPrefix and then remove. * @hide */ + @NonNull public LinkAddress getDestinationLinkAddress() { return new LinkAddress(mDestination.getAddress(), mDestination.getPrefixLength()); } @@ -308,6 +343,7 @@ public final class RouteInfo implements Parcelable { * @return {@link InetAddress} specifying the gateway or next hop. This may be * {@code null} for a directly-connected route." */ + @Nullable public InetAddress getGateway() { return mGateway; } @@ -317,6 +353,7 @@ public final class RouteInfo implements Parcelable { * * @return The name of the interface used for this route. */ + @Nullable public String getInterface() { return mInterface; } @@ -330,6 +367,7 @@ public final class RouteInfo implements Parcelable { */ @TestApi @SystemApi + @RouteType public int getType() { return mType; } @@ -401,6 +439,7 @@ public final class RouteInfo implements Parcelable { * @hide */ @UnsupportedAppUsage + @Nullable public static RouteInfo selectBestRoute(Collection routes, InetAddress dest) { if ((routes == null) || (dest == null)) return null; diff --git a/services/net/java/android/net/StaticIpConfigurationParcelable.aidl b/core/java/android/net/StaticIpConfiguration.aidl similarity index 78% rename from services/net/java/android/net/StaticIpConfigurationParcelable.aidl rename to core/java/android/net/StaticIpConfiguration.aidl index 6fffb423edb514e407660ff6f2d6cc479c00b804..8aac701fe7e19f83c5d1a52a4938723cca95ac61 100644 --- a/services/net/java/android/net/StaticIpConfigurationParcelable.aidl +++ b/core/java/android/net/StaticIpConfiguration.aidl @@ -17,11 +17,4 @@ package android.net; -import android.net.LinkAddress; - -parcelable StaticIpConfigurationParcelable { - LinkAddress ipAddress; - String gateway; - String[] dnsServers; - String domains; -} +@JavaOnlyStableParcelable parcelable StaticIpConfiguration; \ No newline at end of file diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 9e0d95bab7e68590e618ca670bacb5f9052880ec..c3166e91fac71704862ed473106da3beb58cc32c 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -18,6 +18,7 @@ package android.net; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.content.Intent; import android.os.Environment; @@ -380,9 +381,10 @@ public abstract class Uri implements Parcelable, Comparable { * returned as {@code tel:xxx-xxx-xxxx} and {@code http://example.com/path/to/item/} is * returned as {@code http://example.com/...}. * @return the common forms PII redacted string of this URI + * @hide */ - @NonNull - public String toSafeString() { + @SystemApi + public @NonNull String toSafeString() { String scheme = getScheme(); String ssp = getSchemeSpecificPart(); if (scheme != null) { diff --git a/core/java/android/net/apf/ApfCapabilities.aidl b/core/java/android/net/apf/ApfCapabilities.aidl new file mode 100644 index 0000000000000000000000000000000000000000..7c4d4c2da4bcec9cfc242fb06ac991d6d5e94a82 --- /dev/null +++ b/core/java/android/net/apf/ApfCapabilities.aidl @@ -0,0 +1,20 @@ +/* +** +** Copyright (C) 2019 The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.net.apf; + +@JavaOnlyStableParcelable parcelable ApfCapabilities; \ No newline at end of file diff --git a/core/java/android/net/apf/ApfCapabilities.java b/core/java/android/net/apf/ApfCapabilities.java index 17a03c7c8933743ef68ce3b474032babba53ac77..4dd2ace59c624f9bdab57e528133822dbc18f2d9 100644 --- a/core/java/android/net/apf/ApfCapabilities.java +++ b/core/java/android/net/apf/ApfCapabilities.java @@ -19,14 +19,17 @@ package android.net.apf; import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.content.Context; +import android.content.res.Resources; import android.os.Parcel; import android.os.Parcelable; import com.android.internal.R; /** - * APF program support capabilities. + * APF program support capabilities. APF stands for Android Packet Filtering and it is a flexible + * way to drop unwanted network packets to save power. + * + * See documentation at hardware/google/apf/apf.h * * This class is immutable. * @hide @@ -104,10 +107,11 @@ public final class ApfCapabilities implements Parcelable { } /** - * Returns true if the APF interpreter advertises support for the data buffer access opcodes - * LDDW and STDW. + * Determines whether the APF interpreter advertises support for the data buffer access opcodes + * LDDW (LoaD Data Word) and STDW (STore Data Word). Full LDDW (LoaD Data Word) and + * STDW (STore Data Word) support is present from APFv4 on. * - * Full LDDW and STDW support is present from APFv4 on. + * @return {@code true} if the IWifiStaIface#readApfPacketFilterData is supported. */ public boolean hasDataAccess() { return apfVersionSupported >= 4; @@ -116,14 +120,14 @@ public final class ApfCapabilities implements Parcelable { /** * @return Whether the APF Filter in the device should filter out IEEE 802.3 Frames. */ - public static boolean getApfDrop8023Frames(@NonNull Context context) { - return context.getResources().getBoolean(R.bool.config_apfDrop802_3Frames); + public static boolean getApfDrop8023Frames() { + return Resources.getSystem().getBoolean(R.bool.config_apfDrop802_3Frames); } /** * @return An array of blacklisted EtherType, packets with EtherTypes within it will be dropped. */ - public static @NonNull int[] getApfEthTypeBlackList(@NonNull Context context) { - return context.getResources().getIntArray(R.array.config_apfEthTypeBlackList); + public static @NonNull int[] getApfEtherTypeBlackList() { + return Resources.getSystem().getIntArray(R.array.config_apfEthTypeBlackList); } } diff --git a/core/java/android/net/captiveportal/CaptivePortalProbeSpec.java b/core/java/android/net/captiveportal/CaptivePortalProbeSpec.java index 6c6a16c4534eab9cd4a7fdd3f3dc5a6c1710b571..b354607b885669070ad701827a804ae1f378900f 100644 --- a/core/java/android/net/captiveportal/CaptivePortalProbeSpec.java +++ b/core/java/android/net/captiveportal/CaptivePortalProbeSpec.java @@ -50,8 +50,7 @@ public abstract class CaptivePortalProbeSpec { private final String mEncodedSpec; private final URL mUrl; - CaptivePortalProbeSpec(@NonNull String encodedSpec, @NonNull URL url) - throws NullPointerException { + CaptivePortalProbeSpec(@NonNull String encodedSpec, @NonNull URL url) { mEncodedSpec = checkNotNull(encodedSpec); mUrl = checkNotNull(url); } diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 8970c625caa7e7efc9eac6aafebbb5f7bef22a36..1be6c4bce36a5ded86b4ae1678788d42795f85e1 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -373,7 +373,8 @@ public final class NfcAdapter { * A callback to be invoked when the system successfully delivers your {@link NdefMessage} * to another device. * @see #setOnNdefPushCompleteCallback - * @deprecated this feature is deprecated. + * @deprecated this feature is deprecated. File sharing can work using other technology like + * Bluetooth. */ @java.lang.Deprecated public interface OnNdefPushCompleteCallback { @@ -398,7 +399,8 @@ public final class NfcAdapter { * content currently visible to the user. Alternatively, you can call {@link * #setNdefPushMessage setNdefPushMessage()} if the {@link NdefMessage} always contains the * same data. - * @deprecated this feature is deprecated. + * @deprecated this feature is deprecated. File sharing can work using other technology like + * Bluetooth. */ @java.lang.Deprecated public interface CreateNdefMessageCallback { @@ -427,7 +429,8 @@ public final class NfcAdapter { /** - * @deprecated this feature is deprecated. + * @deprecated this feature is deprecated. File sharing can work using other technology like + * Bluetooth. */ @java.lang.Deprecated public interface CreateBeamUrisCallback { @@ -981,7 +984,8 @@ public final class NfcAdapter { * @param uris an array of Uri(s) to push over Android Beam * @param activity activity for which the Uri(s) will be pushed * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - * @deprecated this feature is deprecated. + * @deprecated this feature is deprecated. File sharing can work using other technology like + * Bluetooth. */ @java.lang.Deprecated public void setBeamPushUris(Uri[] uris, Activity activity) { @@ -1068,7 +1072,8 @@ public final class NfcAdapter { * @param callback callback, or null to disable * @param activity activity for which the Uri(s) will be pushed * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - * @deprecated this feature is deprecated. + * @deprecated this feature is deprecated. File sharing can work using other technology like + * Bluetooth. */ @java.lang.Deprecated public void setBeamPushUrisCallback(CreateBeamUrisCallback callback, Activity activity) { @@ -1157,7 +1162,8 @@ public final class NfcAdapter { * to only register one at a time, and to do so in that activity's * {@link Activity#onCreate} * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - * @deprecated this feature is deprecated. + * @deprecated this feature is deprecated. File sharing can work using other technology like + * Bluetooth. */ @java.lang.Deprecated public void setNdefPushMessage(NdefMessage message, Activity activity, @@ -1275,7 +1281,8 @@ public final class NfcAdapter { * to only register one at a time, and to do so in that activity's * {@link Activity#onCreate} * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - * @deprecated this feature is deprecated. + * @deprecated this feature is deprecated. File sharing can work using other technology like + * Bluetooth. */ @java.lang.Deprecated public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity, @@ -1361,7 +1368,8 @@ public final class NfcAdapter { * to only register one at a time, and to do so in that activity's * {@link Activity#onCreate} * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - * @deprecated this feature is deprecated. + * @deprecated this feature is deprecated. File sharing can work using other technology like + * Bluetooth. */ @java.lang.Deprecated public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback, @@ -1577,7 +1585,8 @@ public final class NfcAdapter { * @param activity the current foreground Activity that has registered data to share * @return whether the Beam animation was successfully invoked * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - * @deprecated this feature is deprecated. + * @deprecated this feature is deprecated. File sharing can work using other technology like + * Bluetooth. */ @java.lang.Deprecated public boolean invokeBeam(Activity activity) { @@ -1822,7 +1831,8 @@ public final class NfcAdapter { * @see android.provider.Settings#ACTION_NFCSHARING_SETTINGS * @return true if NDEF Push feature is enabled * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. - * @deprecated this feature is deprecated. + * @deprecated this feature is deprecated. File sharing can work using other technology like + * Bluetooth. */ @java.lang.Deprecated diff --git a/core/java/android/os/BatterySaverPolicyConfig.java b/core/java/android/os/BatterySaverPolicyConfig.java index bda4e27bf54214ec366699473d7fcf0c77e6a9f6..3801cbd48cdd0a3ea6dac33f8d444b019762e7df 100644 --- a/core/java/android/os/BatterySaverPolicyConfig.java +++ b/core/java/android/os/BatterySaverPolicyConfig.java @@ -58,7 +58,8 @@ public final class BatterySaverPolicyConfig implements Parcelable { mAdvertiseIsEnabled = in.mAdvertiseIsEnabled; mDeferFullBackup = in.mDeferFullBackup; mDeferKeyValueBackup = in.mDeferKeyValueBackup; - mDeviceSpecificSettings = Collections.unmodifiableMap(in.mDeviceSpecificSettings); + mDeviceSpecificSettings = Collections.unmodifiableMap( + new ArrayMap<>(in.mDeviceSpecificSettings)); mDisableAnimation = in.mDisableAnimation; mDisableAod = in.mDisableAod; mDisableLaunchBoost = in.mDisableLaunchBoost; @@ -240,7 +241,10 @@ public final class BatterySaverPolicyConfig implements Parcelable { return mDisableOptionalSensors; } - /** Whether or not to disable sound trigger while in Battery Saver. */ + /** + * Whether or not to disable {@link android.hardware.soundtrigger.SoundTrigger} + * while in Battery Saver. + */ public boolean getDisableSoundTrigger() { return mDisableSoundTrigger; } @@ -260,7 +264,10 @@ public final class BatterySaverPolicyConfig implements Parcelable { return mEnableDataSaver; } - /** Whether or not to enable the network firewall while in Battery Saver. */ + /** + * Whether or not to enable network firewall rules to restrict background network use + * while in Battery Saver. + */ public boolean getEnableFirewall() { return mEnableFirewall; } @@ -280,7 +287,12 @@ public final class BatterySaverPolicyConfig implements Parcelable { return mForceAllAppsStandby; } - /** Whether or not to force background check while in Battery Saver. */ + /** + * Whether or not to force background check (disallow background services and manifest + * broadcast receivers) on all apps (not just apps targeting Android + * {@link Build.VERSION_CODES#O} and above) + * while in Battery Saver. + */ public boolean getForceBackgroundCheck() { return mForceBackgroundCheck; } @@ -400,7 +412,10 @@ public final class BatterySaverPolicyConfig implements Parcelable { return this; } - /** Set whether or not to disable sound trigger while in Battery Saver. */ + /** + * Set whether or not to disable {@link android.hardware.soundtrigger.SoundTrigger} + * while in Battery Saver. + */ @NonNull public Builder setDisableSoundTrigger(boolean disableSoundTrigger) { mDisableSoundTrigger = disableSoundTrigger; @@ -428,7 +443,10 @@ public final class BatterySaverPolicyConfig implements Parcelable { return this; } - /** Set whether or not to enable the network firewall while in Battery Saver. */ + /** + * Set whether or not to enable network firewall rules to restrict background network use + * while in Battery Saver. + */ @NonNull public Builder setEnableFirewall(boolean enableFirewall) { mEnableFirewall = enableFirewall; @@ -456,7 +474,12 @@ public final class BatterySaverPolicyConfig implements Parcelable { return this; } - /** Set whether or not to force background check while in Battery Saver. */ + /** + * Set whether or not to force background check (disallow background services and manifest + * broadcast receivers) on all apps (not just apps targeting Android + * {@link Build.VERSION_CODES#O} and above) + * while in Battery Saver. + */ @NonNull public Builder setForceBackgroundCheck(boolean forceBackgroundCheck) { mForceBackgroundCheck = forceBackgroundCheck; diff --git a/core/java/android/os/DropBoxManager.java b/core/java/android/os/DropBoxManager.java index b92e7135750334e4b635a7530a1444d11458284a..b7cccc66294a5b0760b01a19149215c30a4608c9 100644 --- a/core/java/android/os/DropBoxManager.java +++ b/core/java/android/os/DropBoxManager.java @@ -69,7 +69,8 @@ public class DropBoxManager { /** * Broadcast Action: This is broadcast when a new entry is added in the dropbox. * You must hold the {@link android.Manifest.permission#READ_LOGS} permission - * in order to receive this broadcast. + * in order to receive this broadcast. This broadcast can be rate limited for low priority + * entries * *

    This is a protected intent that can only be sent * by the system. diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index cceb6edc4c0a489c1123ce933b4ffd7b80bdcdee..f7e927e4886340f1ac0e3a700691b23c8e6249aa 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; +import android.app.AppGlobals; +import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.os.storage.StorageManager; @@ -1060,7 +1062,7 @@ public class Environment { * @throws IllegalArgumentException if the path is not a valid storage * device. */ - public static boolean isExternalStorageRemovable(File path) { + public static boolean isExternalStorageRemovable(@NonNull File path) { final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId()); if (volume != null) { return volume.isRemovable(); @@ -1103,7 +1105,7 @@ public class Environment { * @throws IllegalArgumentException if the path is not a valid storage * device. */ - public static boolean isExternalStorageEmulated(File path) { + public static boolean isExternalStorageEmulated(@NonNull File path) { final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId()); if (volume != null) { return volume.isEmulated(); @@ -1112,6 +1114,44 @@ public class Environment { } } + /** + * Returns whether the shared/external storage media at the given path is a + * sandboxed view that only contains files owned by the app. + *

    + * This value may be different from the value requested by + * {@code allowExternalStorageSandbox} in the app's manifest, since an app + * may inherit its sandboxed state based on when it was first installed. + *

    + * Sandboxed apps can continue to discover and read media belonging to other + * apps via {@link android.provider.MediaStore}. + */ + public static boolean isExternalStorageSandboxed() { + final File externalDir = sCurrentUser.getExternalDirs()[0]; + return isExternalStorageSandboxed(externalDir); + } + + /** + * Returns whether the shared/external storage media at the given path is a + * sandboxed view that only contains files owned by the app. + *

    + * This value may be different from the value requested by + * {@code allowExternalStorageSandbox} in the app's manifest, since an app + * may inherit its sandboxed state based on when it was first installed. + *

    + * Sandboxed apps can continue to discover and read media belonging to other + * apps via {@link android.provider.MediaStore}. + * + * @throws IllegalArgumentException if the path is not a valid storage + * device. + */ + public static boolean isExternalStorageSandboxed(@NonNull File path) { + final Context context = AppGlobals.getInitialApplication(); + final AppOpsManager appOps = context.getSystemService(AppOpsManager.class); + return appOps.noteOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, + context.getApplicationInfo().uid, + context.getPackageName()) != AppOpsManager.MODE_ALLOWED; + } + static File getDirectory(String variableName, String defaultPath) { String path = System.getenv(variableName); return path == null ? new File(defaultPath) : new File(path); diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 41691d763673831968a11676715d11e38fc37697..43ac5749fecb7925a39992675788ffe1cece6c20 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -103,6 +103,13 @@ public class GraphicsEnvironment { return (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) > 0; } + /** + * Check whether application is profileable + */ + private static boolean isProfileable(Context context) { + return context.getApplicationInfo().isProfileableByShell(); + } + /** * Store the layer paths available to the loader. */ @@ -153,11 +160,11 @@ public class GraphicsEnvironment { String layerPaths = ""; // Only enable additional debug functionality if the following conditions are met: - // 1. App is debuggable or device is rooted + // 1. App is debuggable, profileable, or device is rooted // 2. ENABLE_GPU_DEBUG_LAYERS is true // 3. Package name is equal to GPU_DEBUG_APP - if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1)) { + if (isDebuggable(context) || isProfileable(context) || (getCanLoadSystemLibraries() == 1)) { final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0); diff --git a/core/java/android/os/IIncidentManager.aidl b/core/java/android/os/IIncidentManager.aidl index b67b99fabb9e2e3d0ddef5ee8f3e69949d4df208..5e024b9e35d8b746cbae62952513d915cbe71309 100644 --- a/core/java/android/os/IIncidentManager.aidl +++ b/core/java/android/os/IIncidentManager.aidl @@ -17,32 +17,57 @@ package android.os; import android.os.IIncidentReportStatusListener; +import android.os.IncidentManager; import android.os.IncidentReportArgs; /** * Binder interface to report system health incidents. * {@hide} */ -oneway interface IIncidentManager { +interface IIncidentManager { /** * Takes a report with the given args, reporting status to the optional listener. * * When the report is completed, the system report listener will be notified. */ - void reportIncident(in IncidentReportArgs args); + oneway void reportIncident(in IncidentReportArgs args); /** * Takes a report with the given args, reporting status to the optional listener. * * When the report is completed, the system report listener will be notified. */ - void reportIncidentToStream(in IncidentReportArgs args, + oneway void reportIncidentToStream(in IncidentReportArgs args, @nullable IIncidentReportStatusListener listener, FileDescriptor stream); /** * Tell the incident daemon that the android system server is up and running. */ - void systemRunning(); + oneway void systemRunning(); + + /** + * List the incident reports for the given ComponentName. This is called + * via IncidentCompanion, which validates that the package name matches + * the caller. + */ + List getIncidentReportList(String pkg, String cls); + + /** + * Get the IncidentReport object. + */ + IncidentManager.IncidentReport getIncidentReport(String pkg, String cls, String id); + + /** + * Reduce the refcount on this receiver. This is called + * via IncidentCompanion, which validates that the package name matches + * the caller. + */ + void deleteIncidentReports(String pkg, String cls, String id); + + /** + * Delete all incident reports for this package. + */ + void deleteAllIncidentReports(String pkg); } diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 6536fc991b30caa148515faf34d2ec7f7923132f..03e8c154e3fcbb796d519d49b42a455a18255192 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -330,12 +330,6 @@ interface INetworkManagementService */ void removeIdleTimer(String iface); - /** - * Configure name servers, search paths, and resolver parameters for the given network. - */ - void setDnsConfigurationForNetwork(int netId, in String[] servers, in String[] domains, - in int[] params, String tlsHostname, in String[] tlsServers); - void setFirewallEnabled(boolean enabled); boolean isFirewallEnabled(); void setFirewallInterfaceRule(String iface, boolean allow); @@ -380,11 +374,6 @@ interface INetworkManagementService */ void createVirtualNetwork(int netId, boolean secure); - /** - * Remove a network. - */ - void removeNetwork(int netId); - /** * Add an interface to a network. */ diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl index 6d4c5a034b540efa580295dd998d2a563289d9b8..311c86d08211db6868cfe4d41e4b7b50bb6bc4a9 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/core/java/android/os/IStatsManager.aidl @@ -217,4 +217,9 @@ interface IStatsManager { */ oneway void sendBinaryPushStateChangedAtom(in String trainName, in long trainVersionCode, in int options, in int state, in long[] experimentId); + + /** + * Returns the most recently registered experiment IDs. + */ + long[] getRegisteredExperimentIds(); } diff --git a/core/java/android/os/IncidentManager.java b/core/java/android/os/IncidentManager.java index 0bdf6f10d88d9333f308a34a4da8e5c40d87fdd6..08afe31f05dd67a8a064e6c5a3be070ba49cde74 100644 --- a/core/java/android/os/IncidentManager.java +++ b/core/java/android/os/IncidentManager.java @@ -74,6 +74,13 @@ public class IncidentManager { */ public static final String URI_PARAM_ID = "id"; + /** + * Query parameter for the uris for the incident report id. + * + * @hide + */ + public static final String URI_PARAM_REPORT_ID = "r"; + /** * Query parameter for the uris for the pending report id. * @@ -96,6 +103,13 @@ public class IncidentManager { */ public static final String URI_PARAM_FLAGS = "flags"; + /** + * Query parameter for the uris for the pending report id. + * + * @hide + */ + public static final String URI_PARAM_RECEIVER_CLASS = "receiver"; + /** * Do the confirmation with a dialog instead of the default, which is a notification. * It is possible for the dialog to be downgraded to a notification in some cases. @@ -243,12 +257,12 @@ public class IncidentManager { @SystemApi @TestApi public static class IncidentReport implements Parcelable, Closeable { - private final long mTimestampMs; + private final long mTimestampNs; private final int mPrivacyPolicy; private ParcelFileDescriptor mFileDescriptor; public IncidentReport(Parcel in) { - mTimestampMs = in.readLong(); + mTimestampNs = in.readLong(); mPrivacyPolicy = in.readInt(); if (in.readInt() != 0) { mFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in); @@ -272,10 +286,10 @@ public class IncidentManager { /** * Get the time at which this incident report was taken, in wall clock time - * ({@link System#uptimeMillis System.uptimeMillis()} time base). + * ({@link System#currenttimeMillis System.currenttimeMillis()} time base). */ public long getTimestamp() { - return mTimestampMs; + return mTimestampNs / 1000000; } /** @@ -310,7 +324,7 @@ public class IncidentManager { * @inheritDoc */ public void writeToParcel(Parcel out, int flags) { - out.writeLong(mTimestampMs); + out.writeLong(mTimestampNs); out.writeInt(mPrivacyPolicy); if (mFileDescriptor != null) { out.writeInt(1); @@ -397,8 +411,8 @@ public class IncidentManager { public void requestAuthorization(int callingUid, String callingPackage, int flags, AuthListener listener) { try { - getCompanionServiceLocked().authorizeReport(callingUid, callingPackage, flags, - listener.mBinder); + getCompanionServiceLocked().authorizeReport(callingUid, callingPackage, null, null, + flags, listener.mBinder); } catch (RemoteException ex) { // System process going down throw new RuntimeException(ex); @@ -477,7 +491,19 @@ public class IncidentManager { android.Manifest.permission.PACKAGE_USAGE_STATS }) public @NonNull List getIncidentReportList(String receiverClass) { - throw new RuntimeException("implement me"); + List strings; + try { + strings = getCompanionServiceLocked().getIncidentReportList( + mContext.getPackageName(), receiverClass); + } catch (RemoteException ex) { + throw new RuntimeException("System server or incidentd going down", ex); + } + final int size = strings.size(); + ArrayList result = new ArrayList(size); + for (int i = 0; i < size; i++) { + result.add(Uri.parse(strings.get(i))); + } + return result; } /** @@ -493,20 +519,74 @@ public class IncidentManager { android.Manifest.permission.PACKAGE_USAGE_STATS }) public @Nullable IncidentReport getIncidentReport(Uri uri) { - throw new RuntimeException("implement me"); + final String pkg = uri.getQueryParameter(URI_PARAM_CALLING_PACKAGE); + if (pkg == null) { + throw new RuntimeException("Invalid URI: No " + + URI_PARAM_CALLING_PACKAGE + " parameter. " + uri); + } + + final String cls = uri.getQueryParameter(URI_PARAM_RECEIVER_CLASS); + if (cls == null) { + throw new RuntimeException("Invalid URI: No " + + URI_PARAM_RECEIVER_CLASS + " parameter. " + uri); + } + + final String id = uri.getQueryParameter(URI_PARAM_REPORT_ID); + if (cls == null) { + // If there's no report id, it's a bug report, so we can't return the incident + // report. + return null; + } + + try { + return getCompanionServiceLocked().getIncidentReport(pkg, cls, id); + } catch (RemoteException ex) { + throw new RuntimeException("System server or incidentd going down", ex); + } } /** * Delete the incident report with the given URI id. * - * @param uri Identifier of the incident report. + * @param uri Identifier of the incident report. Pass null to delete all + * incident reports owned by this application. */ @RequiresPermission(allOf = { android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS }) public void deleteIncidentReports(Uri uri) { - throw new RuntimeException("implement me"); + if (uri == null) { + try { + getCompanionServiceLocked().deleteAllIncidentReports(mContext.getPackageName()); + } catch (RemoteException ex) { + throw new RuntimeException("System server or incidentd going down", ex); + } + } else { + final String pkg = uri.getQueryParameter(URI_PARAM_CALLING_PACKAGE); + if (pkg == null) { + throw new RuntimeException("Invalid URI: No " + + URI_PARAM_CALLING_PACKAGE + " parameter. " + uri); + } + + final String cls = uri.getQueryParameter(URI_PARAM_RECEIVER_CLASS); + if (cls == null) { + throw new RuntimeException("Invalid URI: No " + + URI_PARAM_RECEIVER_CLASS + " parameter. " + uri); + } + + final String id = uri.getQueryParameter(URI_PARAM_REPORT_ID); + if (cls == null) { + throw new RuntimeException("Invalid URI: No " + + URI_PARAM_REPORT_ID + " parameter. " + uri); + } + + try { + getCompanionServiceLocked().deleteIncidentReports(pkg, cls, id); + } catch (RemoteException ex) { + throw new RuntimeException("System server or incidentd going down", ex); + } + } } private void reportIncidentInternal(IncidentReportArgs args) { diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 64e2f890ee479a9a5fa0ff11b751c6ec11fa521a..7d61bf6f3986e139b57266743227fddc2965519d 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -773,8 +773,10 @@ public final class PowerManager { */ public static final int LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF = 4; - static final int MIN_LOCATION_MODE = LOCATION_MODE_NO_CHANGE; - static final int MAX_LOCATION_MODE = LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF; + /** @hide */ + public static final int MIN_LOCATION_MODE = LOCATION_MODE_NO_CHANGE; + /** @hide */ + public static final int MAX_LOCATION_MODE = LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF; /** * @hide diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java index 33a6ee888d0a72dadd349106b48cd9e7374bd6c0..fa2c480aaf41bc0299c2c92ab6de3fe3ab432470 100644 --- a/core/java/android/os/image/DynamicSystemClient.java +++ b/core/java/android/os/image/DynamicSystemClient.java @@ -19,18 +19,21 @@ import android.annotation.BytesLong; import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Messenger; +import android.os.ParcelableException; import android.os.RemoteException; import android.util.Slog; @@ -43,20 +46,21 @@ import java.util.concurrent.Executor; *

    This class contains methods and constants used to start a {@code DynamicSystem} installation, * and a listener for status updates.

    * - *

    {@code DynamicSystem} allows user to run certified system images in a non destructive manner - * without needing to prior OEM unlock. While running in {@code DynamicSystem}, persitent storage - * for factory reset protection (FRP) remains unchanged. The new system is installed in a - * temporarily allocated partition. After the installation is completed, the device will be running - * in the new system on next reboot. Then, when the user reboots the device again, it will leave - * {@code DynamicSystem} and go back into the original system. Since the userdata for - * {@code DynamicSystem} is also newly created during the installation, running in - * {@code DynamicSystem} doesn't change user's app data.

    + *

    {@code DynamicSystem} allows users to run certified system images in a non destructive manner + * without needing to prior OEM unlock. It creates a temporary system partition to install the new + * system image, and a temporary data partition for the newly installed system to run with.

    + * + * After the installation is completed, the device will be running in the new system on next the + * reboot. Then, when the user reboots the device again, it will leave {@code DynamicSystem} and go + * back to the original system. While running in {@code DynamicSystem}, persitent storage for + * factory reset protection (FRP) remains unchanged. Since the user is running the new system with + * a temporarily created data partition, their original user data are kept unchanged.

    * *

    With {@link #setOnStatusChangedListener}, API users can register an - * {@link #OnStatusChangedListener} and get status updates and cause when the installation is + * {@link #OnStatusChangedListener} to get status updates and their causes when the installation is * started, stopped, or cancelled. It also sends progress updates during the installation. With - * {@link #start}, API users can start an installation with the {@link Uri} to a gzipped system - * image. The {@link Uri} can be a web URL or a content Uri to a local path.

    + * {@link #start}, API users can start an installation with the {@link Uri} to a unsparsed and + * gzipped system image. The {@link Uri} can be a web URL or a content Uri to a local path.

    * * @hide */ @@ -100,9 +104,10 @@ public class DynamicSystemClient { * @param status status code, also defined in {@code DynamicSystemClient}. * @param cause cause code, also defined in {@code DynamicSystemClient}. * @param progress number of bytes installed. + * @param detail additional detail about the error if available, otherwise null. */ void onStatusChanged(@InstallationStatus int status, @StatusChangedCause int cause, - @BytesLong long progress); + @BytesLong long progress, @Nullable Throwable detail); } /* @@ -177,6 +182,12 @@ public class DynamicSystemClient { */ public static final String KEY_INSTALLED_SIZE = "KEY_INSTALLED_SIZE"; + /** + * Message key, used when the service is sending exception detail to the client. + * @hide + */ + public static final String KEY_EXCEPTION_DETAIL = "KEY_EXCEPTION_DETAIL"; + /* * Intent Actions */ @@ -197,12 +208,6 @@ public class DynamicSystemClient { /* * Intent Keys */ - /** - * Intent key: URL to system image. - * @hide - */ - public static final String KEY_SYSTEM_URL = "KEY_SYSTEM_URL"; - /** * Intent key: Size of system image, in bytes. * @hide @@ -248,7 +253,7 @@ public class DynamicSystemClient { } catch (RemoteException e) { Slog.e(TAG, "Unable to get status from installation service"); mExecutor.execute(() -> { - mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0); + mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e); }); } } @@ -308,7 +313,7 @@ public class DynamicSystemClient { * allows it to send status updates to {@link #OnStatusChangedListener}. It is recommanded * to bind before calling {@link #start} and get status updates. */ - @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM) + @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void bind() { Intent intent = new Intent(); intent.setClassName("com.android.dynsystem", @@ -323,7 +328,7 @@ public class DynamicSystemClient { * Unbind from {@code DynamicSystem} installation service. Unbinding from the installation * service stops it from sending following status updates. */ - @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM) + @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void unbind() { if (!mBound) { return; @@ -356,8 +361,8 @@ public class DynamicSystemClient { * @param systemUrl A network URL or a file URL to system image. * @param systemSize size of system image. */ - @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM) - public void start(@NonNull String systemUrl, @BytesLong long systemSize) { + @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) + public void start(@NonNull Uri systemUrl, @BytesLong long systemSize) { start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE); } @@ -373,17 +378,17 @@ public class DynamicSystemClient { * @param systemSize size of system image. * @param userdataSize bytes reserved for userdata. */ - @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM) - public void start(@NonNull String systemUrl, @BytesLong long systemSize, + @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) + public void start(@NonNull Uri systemUrl, @BytesLong long systemSize, @BytesLong long userdataSize) { Intent intent = new Intent(); intent.setClassName("com.android.dynsystem", "com.android.dynsystem.VerificationActivity"); + intent.setData(systemUrl); intent.setAction(ACTION_START_INSTALL); - intent.putExtra(KEY_SYSTEM_URL, systemUrl); intent.putExtra(KEY_SYSTEM_SIZE, systemSize); intent.putExtra(KEY_USERDATA_SIZE, userdataSize); @@ -396,14 +401,19 @@ public class DynamicSystemClient { int status = msg.arg1; int cause = msg.arg2; // obj is non-null - long progress = ((Bundle) msg.obj).getLong(KEY_INSTALLED_SIZE); + Bundle bundle = (Bundle) msg.obj; + long progress = bundle.getLong(KEY_INSTALLED_SIZE); + ParcelableException t = (ParcelableException) bundle.getSerializable( + KEY_EXCEPTION_DETAIL); + + Throwable detail = t == null ? null : t.getCause(); if (mExecutor != null) { mExecutor.execute(() -> { - mListener.onStatusChanged(status, cause, progress); + mListener.onStatusChanged(status, cause, progress, detail); }); } else { - mListener.onStatusChanged(status, cause, progress); + mListener.onStatusChanged(status, cause, progress, detail); } break; default: diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 3e60d5119fb91191777972b6b0ea2a6c23d33133..c57bf914124897ea3c759fdb6d03af76e63b8287 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -16,7 +16,18 @@ package android.os.storage; +import static android.Manifest.permission.READ_EXTERNAL_STORAGE; +import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; +import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE; +import static android.app.AppOpsManager.OP_READ_MEDIA_AUDIO; +import static android.app.AppOpsManager.OP_READ_MEDIA_IMAGES; +import static android.app.AppOpsManager.OP_READ_MEDIA_VIDEO; +import static android.app.AppOpsManager.OP_WRITE_EXTERNAL_STORAGE; +import static android.app.AppOpsManager.OP_WRITE_MEDIA_AUDIO; +import static android.app.AppOpsManager.OP_WRITE_MEDIA_IMAGES; +import static android.app.AppOpsManager.OP_WRITE_MEDIA_VIDEO; import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.annotation.BytesLong; import android.annotation.IntDef; @@ -33,6 +44,7 @@ import android.annotation.WorkerThread; import android.app.Activity; import android.app.ActivityThread; import android.app.AppGlobals; +import android.app.AppOpsManager; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -120,6 +132,7 @@ import java.util.concurrent.atomic.AtomicInteger; @SystemService(Context.STORAGE_SERVICE) public class StorageManager { private static final String TAG = "StorageManager"; + private static final boolean LOCAL_LOGV = Log.isLoggable(TAG, Log.VERBOSE); /** {@hide} */ public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical"; @@ -139,8 +152,6 @@ public class StorageManager { public static final String PROP_ISOLATED_STORAGE = "persist.sys.isolated_storage"; /** {@hide} */ public static final String PROP_ISOLATED_STORAGE_SNAPSHOT = "sys.isolated_storage_snapshot"; - /** {@hide} */ - public static final String PROP_LEGACY_GREYLIST = "persist.sys.legacy_greylist"; /** {@hide} */ public static final String PROP_FORCE_AUDIO = "persist.fw.force_audio"; @@ -238,8 +249,6 @@ public class StorageManager { public static final int DEBUG_ISOLATED_STORAGE_FORCE_ON = 1 << 6; /** {@hide} */ public static final int DEBUG_ISOLATED_STORAGE_FORCE_OFF = 1 << 7; - /** {@hide} */ - public static final int DEBUG_LEGACY_GREYLIST = 1 << 8; /** {@hide} */ public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE; @@ -290,6 +299,7 @@ public class StorageManager { private final ContentResolver mResolver; private final IStorageManager mStorageManager; + private final AppOpsManager mAppOps; private final Looper mLooper; private final AtomicInteger mNextNonce = new AtomicInteger(0); @@ -500,6 +510,7 @@ public class StorageManager { mResolver = context.getContentResolver(); mLooper = looper; mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getServiceOrThrow("mount")); + mAppOps = mContext.getSystemService(AppOpsManager.class); } /** @@ -1635,6 +1646,105 @@ public class StorageManager { } } + /** + * Check that given app holds both permission and appop. + * @hide + */ + public static boolean checkPermissionAndAppOp(Context context, boolean enforce, + int pid, int uid, String packageName, String permission, int op) { + if (context.checkPermission(permission, pid, uid) != PERMISSION_GRANTED) { + if (enforce) { + throw new SecurityException( + "Permission " + permission + " denied for package " + packageName); + } else { + return false; + } + } + + AppOpsManager appOps = context.getSystemService(AppOpsManager.class); + final int mode = appOps.noteOpNoThrow(op, uid, packageName); + switch (mode) { + case AppOpsManager.MODE_ALLOWED: + return true; + case AppOpsManager.MODE_DEFAULT: + case AppOpsManager.MODE_IGNORED: + case AppOpsManager.MODE_ERRORED: + if (enforce) { + throw new SecurityException("Op " + AppOpsManager.opToName(op) + " " + + AppOpsManager.modeToName(mode) + " for package " + packageName); + } else { + return false; + } + default: + throw new IllegalStateException( + AppOpsManager.opToName(op) + " has unknown mode " + + AppOpsManager.modeToName(mode)); + } + } + + private boolean checkPermissionAndAppOp(boolean enforce, + int pid, int uid, String packageName, String permission, int op) { + return checkPermissionAndAppOp(mContext, enforce, pid, uid, packageName, permission, op); + } + + // Callers must hold both the old and new permissions, so that we can + // handle obscure cases like when an app targets Q but was installed on + // a device that was originally running on P before being upgraded to Q. + + /** {@hide} */ + public boolean checkPermissionReadAudio(boolean enforce, + int pid, int uid, String packageName) { + if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, + READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) return false; + mAppOps.noteOpNoThrow(OP_READ_MEDIA_AUDIO, uid, packageName); + return true; + } + + /** {@hide} */ + public boolean checkPermissionWriteAudio(boolean enforce, + int pid, int uid, String packageName) { + if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, + WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) return false; + mAppOps.noteOpNoThrow(OP_WRITE_MEDIA_AUDIO, uid, packageName); + return true; + } + + /** {@hide} */ + public boolean checkPermissionReadVideo(boolean enforce, + int pid, int uid, String packageName) { + if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, + READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) return false; + mAppOps.noteOpNoThrow(OP_READ_MEDIA_VIDEO, uid, packageName); + return true; + } + + /** {@hide} */ + public boolean checkPermissionWriteVideo(boolean enforce, + int pid, int uid, String packageName) { + if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, + WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) return false; + mAppOps.noteOpNoThrow(OP_WRITE_MEDIA_VIDEO, uid, packageName); + return true; + } + + /** {@hide} */ + public boolean checkPermissionReadImages(boolean enforce, + int pid, int uid, String packageName) { + if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, + READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE)) return false; + mAppOps.noteOpNoThrow(OP_READ_MEDIA_IMAGES, uid, packageName); + return true; + } + + /** {@hide} */ + public boolean checkPermissionWriteImages(boolean enforce, + int pid, int uid, String packageName) { + if (!checkPermissionAndAppOp(enforce, pid, uid, packageName, + WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE)) return false; + mAppOps.noteOpNoThrow(OP_WRITE_MEDIA_IMAGES, uid, packageName); + return true; + } + /** {@hide} */ @VisibleForTesting public @NonNull ParcelFileDescriptor openProxyFileDescriptor( diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java index 61511aa509b188e00a552fe2b1b74a0ab05364be..55fae3014666f16ebc7a246b67fbdce17147570c 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -472,7 +472,7 @@ public final class PermissionControllerManager { @NonNull UserHandle user) { super(context, SERVICE_INTERFACE, componentName, user.getIdentifier(), service -> Log.e(TAG, "RemoteService " + service + " died"), - context.getMainThreadHandler(), false, false, 1); + context.getMainThreadHandler(), 0, false, 1); } /** diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java index 01514646637e80b4e1c4f7eb14d8191c03b4d59d..9085fa2f0f5e40000d67c917566be7738b34d99c 100644 --- a/core/java/android/preference/SeekBarVolumizer.java +++ b/core/java/android/preference/SeekBarVolumizer.java @@ -111,7 +111,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba @UnsupportedAppUsage public SeekBarVolumizer(Context context, int streamType, Uri defaultUri, Callback callback) { - this(context, streamType, defaultUri, callback, false /* playSample */); + this(context, streamType, defaultUri, callback, true /* playSample */); } public SeekBarVolumizer( diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 728d77ef941ea941cae96856df72d83541cc8e56..5631282b3bd3bb7fa37fbf83b1bca8e73b70edef 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -299,27 +299,6 @@ public final class DeviceConfig { "device_identifier_access_restrictions_disabled"; } - /** - * Telephony related properties definitions. - * - * @hide - */ - public interface Telephony { - String NAMESPACE = "telephony"; - /** - * Ringer ramping time in milliseconds. - */ - String RAMPING_RINGER_DURATION = "ramping_ringer_duration"; - /** - * Whether to apply ramping ringer on incoming phone calls. - */ - String RAMPING_RINGER_ENABLED = "ramping_ringer_enabled"; - /** - * Vibration time in milliseconds before ramping ringer starts. - */ - String RAMPING_RINGER_VIBRATION_DURATION = "ramping_ringer_vibration_duration"; - } - private static final Object sLock = new Object(); @GuardedBy("sLock") private static ArrayMap> sSingleListeners = diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 7feeeee0f790f7d22a05d532b24fdad1e839cefd..bda6ed19d3c132cbba499d707af770271f219149 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -31,6 +31,7 @@ import android.annotation.UnsupportedAppUsage; import android.app.Activity; import android.app.AppGlobals; import android.content.ClipData; +import android.content.ContentInterface; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.ContentUris; @@ -884,9 +885,7 @@ public final class MediaStore { * access this path. Instead of trying to open this path * directly, apps should use * {@link ContentResolver#openFileDescriptor(Uri, String)} - * to gain access. This value will always be {@code NULL} - * for apps targeting - * {@link android.os.Build.VERSION_CODES#Q} or higher. + * to gain access. */ @Deprecated @Column(Cursor.FIELD_TYPE_STRING) @@ -1714,11 +1713,9 @@ public final class MediaStore { url = cr.insert(EXTERNAL_CONTENT_URI, values); if (source != null) { - OutputStream imageOut = cr.openOutputStream(url); - try { - source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut); - } finally { - imageOut.close(); + try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream( + cr.openFile(url, "w", null))) { + source.compress(Bitmap.CompressFormat.JPEG, 50, out); } long id = ContentUris.parseId(url); @@ -1959,9 +1956,7 @@ public final class MediaStore { * access this path. Instead of trying to open this path * directly, apps should use * {@link ContentResolver#loadThumbnail} - * to gain access. This value will always be - * {@code NULL} for apps targeting - * {@link android.os.Build.VERSION_CODES#Q} or higher. + * to gain access. */ @Deprecated @Column(Cursor.FIELD_TYPE_STRING) @@ -2442,9 +2437,7 @@ public final class MediaStore { * access this path. Instead of trying to open this path * directly, apps should use * {@link ContentResolver#openFileDescriptor(Uri, String)} - * to gain access. This value will always be - * {@code NULL} for apps targeting - * {@link android.os.Build.VERSION_CODES#Q} or higher. + * to gain access. */ @Deprecated @Column(Cursor.FIELD_TYPE_STRING) @@ -2734,9 +2727,7 @@ public final class MediaStore { * access this path. Instead of trying to open this path * directly, apps should use * {@link ContentResolver#loadThumbnail} - * to gain access. This value will always be - * {@code NULL} for apps targeting - * {@link android.os.Build.VERSION_CODES#Q} or higher. + * to gain access. */ @Deprecated @Column(Cursor.FIELD_TYPE_STRING) @@ -2823,9 +2814,7 @@ public final class MediaStore { * access this path. Instead of trying to open this path * directly, apps should use * {@link ContentResolver#loadThumbnail} - * to gain access. This value will always be - * {@code NULL} for apps targeting - * {@link android.os.Build.VERSION_CODES#Q} or higher. + * to gain access. */ @Deprecated @Column(Cursor.FIELD_TYPE_STRING) @@ -3193,9 +3182,7 @@ public final class MediaStore { * access this path. Instead of trying to open this path * directly, apps should use * {@link ContentResolver#openFileDescriptor(Uri, String)} - * to gain access. This value will always be - * {@code NULL} for apps targeting - * {@link android.os.Build.VERSION_CODES#Q} or higher. + * to gain access. */ @Deprecated @Column(Cursor.FIELD_TYPE_STRING) diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 702dc74f774803f84afda8ec131f90a686025950..f7142cbd56b8c2816302f412ca55e80ce352dda6 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1500,6 +1500,22 @@ public final class Settings { public static final String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS"; + /** + * Activity Action: Show More default apps settings. + *

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

    + * Input: Nothing. + *

    + * Output: Nothing. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + @SystemApi + public static final String ACTION_MANAGE_MORE_DEFAULT_APPS_SETTINGS = + "android.settings.MANAGE_MORE_DEFAULT_APPS_SETTINGS"; + /** * Activity Action: Show notification settings. * @@ -1541,6 +1557,18 @@ public final class Settings { public static final String ACTION_CHANNEL_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_NOTIFICATION_SETTINGS"; + /** + * Activity Action: Show notification bubble settings for a single app. + * See {@link NotificationManager#areBubblesAllowed()}. + *

    + * Input: {@link #EXTRA_APP_PACKAGE}, the package to display. + *

    + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS + = "android.settings.APP_NOTIFICATION_BUBBLE_SETTINGS"; + /** * Activity Extra: The package owner of the notification channel settings to display. *

    @@ -7738,15 +7766,6 @@ public final class Settings { public static final String CALL_SCREENING_DEFAULT_COMPONENT = "call_screening_default_component"; - /** - * Specifies the component name currently configured to be the default application to - * perform the user-defined call redirection service with Telecom. - * @hide - */ - @UnsupportedAppUsage - public static final String CALL_REDIRECTION_DEFAULT_APPLICATION = - "call_redirection_default_application"; - /** * Specifies the package name currently configured to be the emergency assistance application * @@ -11508,15 +11527,6 @@ public final class Settings { public static final String BACKGROUND_ACTIVITY_STARTS_ENABLED = "background_activity_starts_enabled"; - /** - * The packages temporarily whitelisted to be able so start activities from background. - * The list of packages is {@code ":"} colon delimited. - * - * @hide - */ - public static final String BACKGROUND_ACTIVITY_STARTS_PACKAGE_NAMES_WHITELIST = - "background_activity_starts_package_names_whitelist"; - /** * @hide * @see com.android.server.appbinding.AppBindingConstants @@ -12394,6 +12404,19 @@ public final class Settings { */ public static final String EMERGENCY_AFFORDANCE_NEEDED = "emergency_affordance_needed"; + /** + * Whether to enable automatic system server heap dumps. This only works on userdebug or + * eng builds, not on user builds. This is set by the user and overrides the config value. + * 1 means enable, 0 means disable. + * + * @hide + */ + public static final String ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS = + "enable_automatic_system_server_heap_dumps"; + + private static final Validator ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_VALIDATOR = + new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1"}); + /** * See RIL_PreferredNetworkType in ril.h * @hide @@ -13463,24 +13486,6 @@ public final class Settings { public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions"; - /** - * Sampling rate for hidden API access event logs with libmetricslogger, as an integer in - * the range 0 to 0x10000 inclusive. - * - * @hide - */ - public static final String HIDDEN_API_ACCESS_LOG_SAMPLING_RATE = - "hidden_api_access_log_sampling_rate"; - - /** - * Sampling rate for hidden API access event logging with statslog, as an integer in the - * range 0 to 0x10000 inclusive. - * - * @hide - */ - public static final String HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE = - "hidden_api_access_statslog_sampling_rate"; - /** * Hidden API enforcement policy for apps. * @@ -13565,6 +13570,7 @@ public final class Settings { EMERGENCY_TONE, CALL_AUTO_RETRY, DOCK_AUDIO_MEDIA_ENABLED, + ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS, ENCODED_SURROUND_OUTPUT, ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS, LOW_POWER_MODE_TRIGGER_LEVEL, @@ -13607,6 +13613,8 @@ public final class Settings { VALIDATORS.put(EMERGENCY_TONE, EMERGENCY_TONE_VALIDATOR); VALIDATORS.put(CALL_AUTO_RETRY, CALL_AUTO_RETRY_VALIDATOR); VALIDATORS.put(DOCK_AUDIO_MEDIA_ENABLED, DOCK_AUDIO_MEDIA_ENABLED_VALIDATOR); + VALIDATORS.put(ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS, + ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_VALIDATOR); VALIDATORS.put(ENCODED_SURROUND_OUTPUT, ENCODED_SURROUND_OUTPUT_VALIDATOR); VALIDATORS.put(ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS, ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS_VALIDATOR); @@ -14716,7 +14724,6 @@ public final class Settings { * * @hide */ - // TODO(b/117663715): require a new write permission restricted to a single source @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG) static void resetToDefaults(@NonNull ContentResolver resolver, @ResetMode int resetMode, @Nullable String prefix) { diff --git a/core/java/android/service/carrier/CarrierIdentifier.java b/core/java/android/service/carrier/CarrierIdentifier.java index 6629233ef5907a54ea09be325e19ff3b64471b17..af5bf7475f95109f9940efce0e139962e500e99e 100644 --- a/core/java/android/service/carrier/CarrierIdentifier.java +++ b/core/java/android/service/carrier/CarrierIdentifier.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.Rlog; import android.telephony.TelephonyManager; import com.android.internal.telephony.uicc.IccUtils; @@ -223,7 +224,7 @@ public class CarrierIdentifier implements Parcelable { + "mcc=" + mMcc + ",mnc=" + mMnc + ",spn=" + mSpn - + ",imsi=" + mImsi + + ",imsi=" + Rlog.pii(false, mImsi) + ",gid1=" + mGid1 + ",gid2=" + mGid2 + ",carrierid=" + mCarrierId diff --git a/core/java/android/service/carrier/CarrierService.java b/core/java/android/service/carrier/CarrierService.java index c351d891bc6189c34d912eb1da9821f300274685..aeb186b0068634b29a3d56660359a0c01bf9bf38 100644 --- a/core/java/android/service/carrier/CarrierService.java +++ b/core/java/android/service/carrier/CarrierService.java @@ -114,9 +114,7 @@ public abstract class CarrierService extends Service { * this UX, so a carrier app must be sure to call with active set to false * sometime after calling with it set to true. *

    - * Requires Permission: - * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} - * or the calling app has carrier privileges. + * Requires Permission: calling app has carrier privileges. * * @param active Whether the carrier network change is or shortly will be * active. Set this value to true to begin showing diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index fb07abaa34a13a6b5f7d3c17f70713843872cb8a..dc57a159191312d04b349500080f3cea3cf9d0ff 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -17,6 +17,8 @@ package android.service.contentcapture; import static android.view.contentcapture.ContentCaptureHelper.sDebug; import static android.view.contentcapture.ContentCaptureHelper.sVerbose; +import static android.view.contentcapture.ContentCaptureHelper.toList; +import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; @@ -36,9 +38,10 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.service.autofill.AutofillService; -import android.util.ArrayMap; import android.util.Log; import android.util.Slog; +import android.util.SparseIntArray; +import android.view.contentcapture.ContentCaptureCondition; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureEvent; import android.view.contentcapture.ContentCaptureManager; @@ -52,7 +55,6 @@ import com.android.internal.os.IResultReceiver; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -116,7 +118,7 @@ public abstract class ContentCaptureService extends Service { } @Override - public void onSessionStarted(ContentCaptureContext context, String sessionId, int uid, + public void onSessionStarted(ContentCaptureContext context, int sessionId, int uid, IResultReceiver clientReceiver, int initialState) { mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnCreateSession, ContentCaptureService.this, context, sessionId, uid, clientReceiver, @@ -124,14 +126,14 @@ public abstract class ContentCaptureService extends Service { } @Override - public void onActivitySnapshot(String sessionId, SnapshotData snapshotData) { + public void onActivitySnapshot(int sessionId, SnapshotData snapshotData) { mHandler.sendMessage( obtainMessage(ContentCaptureService::handleOnActivitySnapshot, ContentCaptureService.this, sessionId, snapshotData)); } @Override - public void onSessionFinished(String sessionId) { + public void onSessionFinished(int sessionId) { mHandler.sendMessage(obtainMessage(ContentCaptureService::handleFinishSession, ContentCaptureService.this, sessionId)); } @@ -170,7 +172,7 @@ public abstract class ContentCaptureService extends Service { *

    This map is populated when an session is started, which is called by the system server * and can be trusted. Then subsequent calls made by the app are verified against this map. */ - private final ArrayMap mSessionUids = new ArrayMap<>(); + private final SparseIntArray mSessionUids = new SparseIntArray(); @CallSuper @Override @@ -216,8 +218,40 @@ public abstract class ContentCaptureService extends Service { } } - private ArrayList toList(@Nullable Set set) { - return set == null ? null : new ArrayList(set); + /** + * Explicitly sets the conditions for which content capture should be available by an app. + * + *

    Typically used to restrict content capture to a few websites on browser apps. Example: + * + * + * ArraySet conditions = new ArraySet<>(1); + * conditions.add(new ContentCaptureCondition(new LocusId("^https://.*\\.example\\.com$"), + * ContentCaptureCondition.FLAG_IS_REGEX)); + * service.setContentCaptureConditions("com.example.browser_app", conditions); + * + * + * + *

    NOTE:

    this method doesn't automatically disable content capture for the given + * conditions; it's up to the {@code packageName} implementation to call + * {@link ContentCaptureManager#getContentCaptureConditions()} and disable it accordingly. + * + * @param packageName name of the packages where the restrictions are set. + * @param conditions list of conditions, or {@code null} to reset the conditions for the + * package. + */ + public final void setContentCaptureConditions(@NonNull String packageName, + @Nullable Set conditions) { + final IContentCaptureServiceCallback callback = mCallback; + if (callback == null) { + Log.w(TAG, "setContentCaptureConditions(): no server callback"); + return; + } + + try { + callback.setContentCaptureConditions(packageName, toList(conditions)); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } /** @@ -351,7 +385,7 @@ public abstract class ContentCaptureService extends Service { // so we don't need to create a temporary InteractionSessionId for each event. private void handleOnCreateSession(@NonNull ContentCaptureContext context, - @NonNull String sessionId, int uid, IResultReceiver clientReceiver, int initialState) { + int sessionId, int uid, IResultReceiver clientReceiver, int initialState) { mSessionUids.put(sessionId, uid); onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId)); @@ -376,27 +410,27 @@ public abstract class ContentCaptureService extends Service { // Most events belong to the same session, so we can keep a reference to the last one // to avoid creating too many ContentCaptureSessionId objects - String lastSessionId = null; + int lastSessionId = NO_SESSION_ID; ContentCaptureSessionId sessionId = null; final List events = parceledEvents.getList(); for (int i = 0; i < events.size(); i++) { final ContentCaptureEvent event = events.get(i); if (!handleIsRightCallerFor(event, uid)) continue; - String sessionIdString = event.getSessionId(); - if (!sessionIdString.equals(lastSessionId)) { - sessionId = new ContentCaptureSessionId(sessionIdString); - lastSessionId = sessionIdString; + int sessionIdInt = event.getSessionId(); + if (sessionIdInt != lastSessionId) { + sessionId = new ContentCaptureSessionId(sessionIdInt); + lastSessionId = sessionIdInt; } switch (event.getType()) { case ContentCaptureEvent.TYPE_SESSION_STARTED: final ContentCaptureContext clientContext = event.getContentCaptureContext(); clientContext.setParentSessionId(event.getParentSessionId()); - mSessionUids.put(sessionIdString, uid); + mSessionUids.put(sessionIdInt, uid); onCreateContentCaptureSession(clientContext, sessionId); break; case ContentCaptureEvent.TYPE_SESSION_FINISHED: - mSessionUids.remove(sessionIdString); + mSessionUids.delete(sessionIdInt); onDestroyContentCaptureSession(sessionId); break; default: @@ -405,13 +439,12 @@ public abstract class ContentCaptureService extends Service { } } - private void handleOnActivitySnapshot(@NonNull String sessionId, - @NonNull SnapshotData snapshotData) { + private void handleOnActivitySnapshot(int sessionId, @NonNull SnapshotData snapshotData) { onActivitySnapshot(new ContentCaptureSessionId(sessionId), snapshotData); } - private void handleFinishSession(@NonNull String sessionId) { - mSessionUids.remove(sessionId); + private void handleFinishSession(int sessionId) { + mSessionUids.delete(sessionId); onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId)); } @@ -427,7 +460,7 @@ public abstract class ContentCaptureService extends Service { * Checks if the given {@code uid} owns the session associated with the event. */ private boolean handleIsRightCallerFor(@NonNull ContentCaptureEvent event, int uid) { - final String sessionId; + final int sessionId; switch (event.getType()) { case ContentCaptureEvent.TYPE_SESSION_STARTED: case ContentCaptureEvent.TYPE_SESSION_FINISHED: @@ -436,8 +469,7 @@ public abstract class ContentCaptureService extends Service { default: sessionId = event.getSessionId(); } - final Integer rightUid = mSessionUids.get(sessionId); - if (rightUid == null) { + if (mSessionUids.indexOfKey(sessionId) < 0) { if (sVerbose) { Log.v(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId + ": " + mSessionUids); @@ -445,6 +477,7 @@ public abstract class ContentCaptureService extends Service { // Just ignore, as the session could have been finished already return false; } + final int rightUid = mSessionUids.get(sessionId); if (rightUid != uid) { Log.e(TAG, "invalid call from UID " + uid + ": session " + sessionId + " belongs to " + rightUid); diff --git a/core/java/android/service/contentcapture/IContentCaptureService.aidl b/core/java/android/service/contentcapture/IContentCaptureService.aidl index 6be7a80653e9ece07cb254ef72afb1a4f4f7c1e1..03e1b7857837ea46a243206db607615dceab3132 100644 --- a/core/java/android/service/contentcapture/IContentCaptureService.aidl +++ b/core/java/android/service/contentcapture/IContentCaptureService.aidl @@ -35,10 +35,10 @@ import java.util.List; oneway interface IContentCaptureService { void onConnected(IBinder callback, boolean verbose, boolean debug); void onDisconnected(); - void onSessionStarted(in ContentCaptureContext context, String sessionId, int uid, + void onSessionStarted(in ContentCaptureContext context, int sessionId, int uid, in IResultReceiver clientReceiver, int initialState); - void onSessionFinished(String sessionId); - void onActivitySnapshot(String sessionId, in SnapshotData snapshotData); + void onSessionFinished(int sessionId); + void onActivitySnapshot(int sessionId, in SnapshotData snapshotData); void onUserDataRemovalRequest(in UserDataRemovalRequest request); void onActivityEvent(in ActivityEvent event); } diff --git a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl index 8bc8defede8059d446d366e43b6537a9515b4641..0550ad3ea20c0d0871bf749418cb03a722481bf4 100644 --- a/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl +++ b/core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl @@ -17,6 +17,7 @@ package android.service.contentcapture; import android.content.ComponentName; +import android.view.contentcapture.ContentCaptureCondition; import java.util.List; @@ -27,5 +28,6 @@ import java.util.List; */ oneway interface IContentCaptureServiceCallback { void setContentCaptureWhitelist(in List packages, in List activities); + void setContentCaptureConditions(String packageName, in List conditions); void disableSelf(); } diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java index c35423fb09c61be3411e23994fa9773622afcd6f..d32bdad4d0a92c5ab07c37cee1b9b833f250db81 100644 --- a/core/java/android/service/quicksettings/TileService.java +++ b/core/java/android/service/quicksettings/TileService.java @@ -87,6 +87,10 @@ public class TileService extends Service { * This intent may also define a {@link Intent#EXTRA_COMPONENT_NAME} value * to indicate the {@link ComponentName} that caused the preferences to be * opened. + *

    + * To ensure that the activity can only be launched through quick settings + * UI provided by this service, apps can protect it with the + * BIND_QUICK_SETTINGS_TILE permission. */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String ACTION_QS_TILE_PREFERENCES diff --git a/core/java/android/service/wallpaper/IWallpaperService.aidl b/core/java/android/service/wallpaper/IWallpaperService.aidl index 99a81f503a952e180aa8b007a5f0f2617cf6ae25..56e2486dd6267c2fd7d5f9593ac1db3a07b1b4fc 100644 --- a/core/java/android/service/wallpaper/IWallpaperService.aidl +++ b/core/java/android/service/wallpaper/IWallpaperService.aidl @@ -26,4 +26,5 @@ oneway interface IWallpaperService { void attach(IWallpaperConnection connection, IBinder windowToken, int windowType, boolean isPreview, int reqWidth, int reqHeight, in Rect padding, int displayId); + void detach(); } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index e1762dffeef59449d9d2d268775ffe8fca2263c7..d645e3f746d78c430542f210f48de6b47ecd8343 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -72,6 +72,7 @@ import com.android.internal.view.BaseSurfaceHolder; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; /** @@ -1309,6 +1310,7 @@ public abstract class WallpaperService extends Service { final int mDisplayId; final DisplayManager mDisplayManager; final Display mDisplay; + private final AtomicBoolean mDetached = new AtomicBoolean(); Engine mEngine; @@ -1399,8 +1401,23 @@ public abstract class WallpaperService extends Service { mCaller.sendMessage(msg); } + public void detach() { + mDetached.set(true); + } + + private void doDetachEngine() { + mActiveEngines.remove(mEngine); + mEngine.detach(); + } + @Override public void executeMessage(Message message) { + if (mDetached.get()) { + if (mActiveEngines.contains(mEngine)) { + doDetachEngine(); + } + return; + } switch (message.what) { case DO_ATTACH: { try { @@ -1416,8 +1433,7 @@ public abstract class WallpaperService extends Service { return; } case DO_DETACH: { - mActiveEngines.remove(mEngine); - mEngine.detach(); + doDetachEngine(); return; } case DO_SET_DESIRED_SIZE: { @@ -1497,6 +1513,7 @@ public abstract class WallpaperService extends Service { */ class IWallpaperServiceWrapper extends IWallpaperService.Stub { private final WallpaperService mTarget; + private IWallpaperEngineWrapper mEngineWrapper; public IWallpaperServiceWrapper(WallpaperService context) { mTarget = context; @@ -1506,9 +1523,14 @@ public abstract class WallpaperService extends Service { public void attach(IWallpaperConnection conn, IBinder windowToken, int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, int displayId) { - new IWallpaperEngineWrapper(mTarget, conn, windowToken, + mEngineWrapper = new IWallpaperEngineWrapper(mTarget, conn, windowToken, windowType, isPreview, reqWidth, reqHeight, padding, displayId); } + + @Override + public void detach() { + mEngineWrapper.detach(); + } } @Override diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java index a7b280b7b992d4b8d6887b1662806c635d111756..2fab40472bd5120f9b56f3c3cf51f3823afe7beb 100644 --- a/core/java/android/speech/tts/TtsEngines.java +++ b/core/java/android/speech/tts/TtsEngines.java @@ -15,8 +15,10 @@ */ package android.speech.tts; -import org.xmlpull.v1.XmlPullParserException; +import static android.provider.Settings.Secure.getString; +import android.annotation.NonNull; +import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -27,10 +29,6 @@ import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; - -import static android.provider.Settings.Secure.getString; - -import android.annotation.UnsupportedAppUsage; import android.provider.Settings; import android.speech.tts.TextToSpeech.Engine; import android.speech.tts.TextToSpeech.EngineInfo; @@ -39,6 +37,8 @@ import android.util.AttributeSet; import android.util.Log; import android.util.Xml; +import org.xmlpull.v1.XmlPullParserException; + import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -522,7 +522,8 @@ public class TtsEngines { * read back, will evaluate to {@link Locale#getDefault()}. */ @UnsupportedAppUsage - public synchronized void updateLocalePrefForEngine(String engineName, Locale newLocale) { + public synchronized void updateLocalePrefForEngine( + @NonNull String engineName, Locale newLocale) { final String prefList = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_LOCALE); if (DBG) { diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index 077d12de4d59587c614281c3258dd98bb2a30fe2..d7baa1086b2f1e5983f76bffa8477663d4fef1f7 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -92,10 +92,15 @@ public final class Formatter { * @return formatted string with the number */ public static String formatFileSize(@Nullable Context context, long sizeBytes) { + return formatFileSize(context, sizeBytes, FLAG_SI_UNITS); + } + + /** @hide */ + public static String formatFileSize(@Nullable Context context, long sizeBytes, int flags) { if (context == null) { return ""; } - final BytesResult res = formatBytes(context.getResources(), sizeBytes, FLAG_SI_UNITS); + final BytesResult res = formatBytes(context.getResources(), sizeBytes, flags); return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix, res.value, res.units)); } diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java index 436cb4ff7072c69b2063534960ce1b3597a2cfc8..e2af6f5ed102e074823a8f605646217ac36daa67 100644 --- a/core/java/android/util/ArrayMap.java +++ b/core/java/android/util/ArrayMap.java @@ -16,12 +16,12 @@ package android.util; -import libcore.util.EmptyArray; - import android.annotation.UnsupportedAppUsage; import com.android.internal.util.ArrayUtils; +import libcore.util.EmptyArray; + import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Map; @@ -453,6 +453,10 @@ public final class ArrayMap implements Map { * @return Returns the key stored at the given index. */ public K keyAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } return (K)mArray[index << 1]; } @@ -462,6 +466,10 @@ public final class ArrayMap implements Map { * @return Returns the value stored at the given index. */ public V valueAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } return (V)mArray[(index << 1) + 1]; } @@ -472,6 +480,10 @@ public final class ArrayMap implements Map { * @return Returns the previous value at the given index. */ public V setValueAt(int index, V value) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } index = (index << 1) + 1; V old = (V)mArray[index]; mArray[index] = value; @@ -665,6 +677,11 @@ public final class ArrayMap implements Map { * @return Returns the value that was stored at this index. */ public V removeAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } + final Object old = mArray[(index << 1) + 1]; final int osize = mSize; final int nsize; diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java index cf49803a72251458e95475199fd63f615c85b33e..e4de7045721b9a35f803881f96b3d6ef124bd83b 100644 --- a/core/java/android/util/LongSparseArray.java +++ b/core/java/android/util/LongSparseArray.java @@ -21,9 +21,6 @@ import com.android.internal.util.GrowingArrayUtils; import libcore.util.EmptyArray; -import java.util.Arrays; -import java.util.Objects; - /** * SparseArray mapping longs to Objects. Unlike a normal array of Objects, * there can be gaps in the indices. It is intended to be more memory efficient @@ -147,6 +144,10 @@ public class LongSparseArray implements Cloneable { * Removes the mapping at the specified index. */ public void removeAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } if (mValues[index] != DELETED) { mValues[index] = DELETED; mGarbage = true; @@ -236,6 +237,10 @@ public class LongSparseArray implements Cloneable { * key.

    */ public long keyAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } if (mGarbage) { gc(); } @@ -256,6 +261,10 @@ public class LongSparseArray implements Cloneable { */ @SuppressWarnings("unchecked") public E valueAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } if (mGarbage) { gc(); } @@ -269,6 +278,10 @@ public class LongSparseArray implements Cloneable { * LongSparseArray stores. */ public void setValueAt(int index, E value) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } if (mGarbage) { gc(); } diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java index 8dcdb402624658781a211f7c5e78b1484664c463..f167f009a942e2a8ba5c1b461bf186ef9855780b 100644 --- a/core/java/android/util/LongSparseLongArray.java +++ b/core/java/android/util/LongSparseLongArray.java @@ -16,14 +16,13 @@ package android.util; +import android.annotation.UnsupportedAppUsage; + import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; -import android.annotation.UnsupportedAppUsage; import libcore.util.EmptyArray; -import java.util.Arrays; - /** * Map of {@code long} to {@code long}. Unlike a normal array of longs, there * can be gaps in the indices. It is intended to be more memory efficient than using a @@ -173,6 +172,10 @@ public class LongSparseLongArray implements Cloneable { * key.

    */ public long keyAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } return mKeys[index]; } @@ -188,6 +191,10 @@ public class LongSparseLongArray implements Cloneable { * associated with the largest key.

    */ public long valueAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } return mValues[index]; } diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java index 89ea2d35fc2f4abdd68d5bb53d502b03d21cf3b7..67dfb02a0b9545aa6c0fd44376d9470fc96e9632 100644 --- a/core/java/android/util/SparseArray.java +++ b/core/java/android/util/SparseArray.java @@ -16,10 +16,11 @@ package android.util; +import android.annotation.UnsupportedAppUsage; + import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; -import android.annotation.UnsupportedAppUsage; import libcore.util.EmptyArray; /** @@ -171,6 +172,10 @@ public class SparseArray implements Cloneable { * the behavior is undefined.

    */ public void removeAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } if (mValues[index] != DELETED) { mValues[index] = DELETED; mGarbage = true; @@ -279,6 +284,10 @@ public class SparseArray implements Cloneable { * the behavior is undefined.

    */ public int keyAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } if (mGarbage) { gc(); } @@ -302,6 +311,10 @@ public class SparseArray implements Cloneable { */ @SuppressWarnings("unchecked") public E valueAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } if (mGarbage) { gc(); } @@ -317,6 +330,10 @@ public class SparseArray implements Cloneable { *

    For indices outside of the range 0...size()-1, the behavior is undefined.

    */ public void setValueAt(int index, E value) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } if (mGarbage) { gc(); } diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java index d4c40954bdd10c6db0c826861af72ac847e1bd89..03fa1c996027c49ba0bf89a0c09cc676fe79f05f 100644 --- a/core/java/android/util/SparseBooleanArray.java +++ b/core/java/android/util/SparseBooleanArray.java @@ -16,10 +16,11 @@ package android.util; +import android.annotation.UnsupportedAppUsage; + import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; -import android.annotation.UnsupportedAppUsage; import libcore.util.EmptyArray; /** @@ -167,6 +168,10 @@ public class SparseBooleanArray implements Cloneable { * key.

    */ public int keyAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } return mKeys[index]; } @@ -182,6 +187,10 @@ public class SparseBooleanArray implements Cloneable { * associated with the largest key.

    */ public boolean valueAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } return mValues[index]; } @@ -189,11 +198,19 @@ public class SparseBooleanArray implements Cloneable { * Directly set the value at a particular index. */ public void setValueAt(int index, boolean value) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } mValues[index] = value; } /** @hide */ public void setKeyAt(int index, int key) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } mKeys[index] = key; } diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java index 9e6bad1d9ae0b4aa86d5e8d20f74c91c924e6e56..c68dc4edcfb7cf559b4f7e4eab9e6157ad895c0e 100644 --- a/core/java/android/util/SparseIntArray.java +++ b/core/java/android/util/SparseIntArray.java @@ -16,14 +16,15 @@ package android.util; +import android.annotation.UnsupportedAppUsage; + import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; -import java.util.Arrays; - -import android.annotation.UnsupportedAppUsage; import libcore.util.EmptyArray; +import java.util.Arrays; + /** * SparseIntArrays map integers to integers. Unlike a normal array of integers, * there can be gaps in the indices. It is intended to be more memory efficient @@ -171,6 +172,10 @@ public class SparseIntArray implements Cloneable { * key.

    */ public int keyAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } return mKeys[index]; } @@ -186,6 +191,10 @@ public class SparseIntArray implements Cloneable { * associated with the largest key.

    */ public int valueAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } return mValues[index]; } @@ -193,6 +202,10 @@ public class SparseIntArray implements Cloneable { * Directly set the value at a particular index. */ public void setValueAt(int index, int value) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } mValues[index] = value; } diff --git a/core/java/android/util/SparseLongArray.java b/core/java/android/util/SparseLongArray.java index 81db2b7ff715375491804663928eccbab88516f4..37a92024f374b1fc8c6dee2cc9ab5c32a7175dfb 100644 --- a/core/java/android/util/SparseLongArray.java +++ b/core/java/android/util/SparseLongArray.java @@ -182,6 +182,10 @@ public class SparseLongArray implements Cloneable { * key.

    */ public int keyAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } return mKeys[index]; } @@ -197,6 +201,10 @@ public class SparseLongArray implements Cloneable { * associated with the largest key.

    */ public long valueAt(int index) { + if (index >= mSize) { + // The array might be slightly bigger than mSize, in which case, indexing won't fail. + throw new ArrayIndexOutOfBoundsException(index); + } return mValues[index]; } diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index 610f7edef451d1ac2eb09840de2fef47ee507c26..715181f28076fea529189ed272737503a476dec3 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -512,8 +512,8 @@ public final class DisplayCutout { * @hide */ public DisplayCutout inset(int insetLeft, int insetTop, int insetRight, int insetBottom) { - if (isBoundsEmpty() - || insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0) { + if (insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0 + || isBoundsEmpty()) { return this; } @@ -534,6 +534,12 @@ public final class DisplayCutout { safeInsets.right = atLeastZero(safeInsets.right - insetRight); } + // If we are not cutting off part of the cutout by insetting it on bottom/right, and we also + // don't move it around, we can avoid the allocation and copy of the instance. + if (insetLeft == 0 && insetTop == 0 && mSafeInsets.equals(safeInsets)) { + return this; + } + Rect[] bounds = mBounds.getRects(); for (int i = 0; i < bounds.length; ++i) { if (!bounds[i].equals(ZERO_RECT)) { diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index c794a69d36808ff22868629b83706e0c4b89ffac..8fbbcf4b88c6a0028ec11990d573875303ca10d3 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -16,11 +16,20 @@ package android.view; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION; + import android.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Build; import android.os.Handler; import android.os.Message; +import android.os.SystemClock; +import android.util.StatsLog; /** * Detects various gestures and events using the supplied {@link MotionEvent}s. @@ -251,8 +260,12 @@ public class GestureDetector { private boolean mAlwaysInTapRegion; private boolean mAlwaysInBiggerTapRegion; private boolean mIgnoreNextUpEvent; + // Whether a classification has been recorded by statsd for the current event stream. Reset on + // ACTION_DOWN. + private boolean mHasRecordedClassification; private MotionEvent mCurrentDownEvent; + private MotionEvent mCurrentMotionEvent; private MotionEvent mPreviousUpEvent; /** @@ -297,6 +310,7 @@ public class GestureDetector { break; case LONG_PRESS: + recordGestureClassification(msg.arg1); dispatchLongPress(); break; @@ -304,6 +318,8 @@ public class GestureDetector { // If the user's finger is still down, do not count it as a tap if (mDoubleTapListener != null) { if (!mStillDown) { + recordGestureClassification( + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP); mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent); } else { mDeferConfirmSingleTap = true; @@ -501,6 +517,11 @@ public class GestureDetector { final int action = ev.getAction(); + if (mCurrentMotionEvent != null) { + mCurrentMotionEvent.recycle(); + } + mCurrentMotionEvent = MotionEvent.obtain(ev); + if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } @@ -569,6 +590,8 @@ public class GestureDetector { && isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) { // This is a second tap mIsDoubleTapping = true; + recordGestureClassification( + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP); // 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 @@ -590,11 +613,17 @@ public class GestureDetector { mStillDown = true; mInLongPress = false; mDeferConfirmSingleTap = false; + mHasRecordedClassification = false; if (mIsLongpressEnabled) { mHandler.removeMessages(LONG_PRESS); - mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime() - + ViewConfiguration.getLongPressTimeout()); + mHandler.sendMessageAtTime( + mHandler.obtainMessage( + LONG_PRESS, + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS, + 0 /* arg2 */), + mCurrentDownEvent.getDownTime() + + ViewConfiguration.getLongPressTimeout()); } mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT); @@ -613,6 +642,8 @@ public class GestureDetector { final float scrollY = mLastFocusY - focusY; if (mIsDoubleTapping) { // Give the move events of the double-tap + recordGestureClassification( + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP); handled |= mDoubleTapListener.onDoubleTapEvent(ev); } else if (mAlwaysInTapRegion) { final int deltaX = (int) (focusX - mDownFocusX); @@ -635,8 +666,12 @@ public class GestureDetector { // reschedule long press with a modified timeout. mHandler.removeMessages(LONG_PRESS); final long longPressTimeout = ViewConfiguration.getLongPressTimeout(); - mHandler.sendEmptyMessageAtTime(LONG_PRESS, ev.getDownTime() - + (long) (longPressTimeout * multiplier)); + mHandler.sendMessageAtTime( + mHandler.obtainMessage( + LONG_PRESS, + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS, + 0 /* arg2 */), + ev.getDownTime() + (long) (longPressTimeout * multiplier)); } // Inhibit default scroll. If a gesture is ambiguous, we prevent scroll // until the gesture is resolved. @@ -646,6 +681,8 @@ public class GestureDetector { } if (distance > slopSquare) { + recordGestureClassification( + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL); handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); mLastFocusX = focusX; mLastFocusY = focusY; @@ -659,6 +696,7 @@ public class GestureDetector { mAlwaysInBiggerTapRegion = false; } } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) { + recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL); handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); mLastFocusX = focusX; mLastFocusY = focusY; @@ -667,7 +705,11 @@ public class GestureDetector { motionClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS; if (deepPress && hasPendingLongPress) { mHandler.removeMessages(LONG_PRESS); - mHandler.sendEmptyMessage(LONG_PRESS); + mHandler.sendMessage( + mHandler.obtainMessage( + LONG_PRESS, + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS, + 0 /* arg2 */)); } break; @@ -676,11 +718,15 @@ public class GestureDetector { MotionEvent currentUpEvent = MotionEvent.obtain(ev); if (mIsDoubleTapping) { // Finally, give the up event of the double-tap + recordGestureClassification( + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP); handled |= mDoubleTapListener.onDoubleTapEvent(ev); } else if (mInLongPress) { mHandler.removeMessages(TAP); mInLongPress = false; } else if (mAlwaysInTapRegion && !mIgnoreNextUpEvent) { + recordGestureClassification( + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP); handled = mListener.onSingleTapUp(ev); if (mDeferConfirmSingleTap && mDoubleTapListener != null) { mDoubleTapListener.onSingleTapConfirmed(ev); @@ -821,4 +867,26 @@ public class GestureDetector { mInLongPress = true; mListener.onLongPress(mCurrentDownEvent); } + + private void recordGestureClassification(int classification) { + if (mHasRecordedClassification + || classification + == TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION) { + // Only record the first classification for an event stream. + return; + } + if (mCurrentDownEvent == null || mCurrentMotionEvent == null) { + // If the complete event stream wasn't seen, don't record anything. + mHasRecordedClassification = true; + return; + } + StatsLog.write( + StatsLog.TOUCH_GESTURE_CLASSIFIED, + getClass().getName(), + classification, + (int) (SystemClock.uptimeMillis() - mCurrentMotionEvent.getDownTime()), + (float) Math.hypot(mCurrentMotionEvent.getRawX() - mCurrentDownEvent.getRawX(), + mCurrentMotionEvent.getRawY() - mCurrentDownEvent.getRawY())); + mHasRecordedClassification = true; + } } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 87efb3fbf6c0263c59d21088a47116bac002da07..d317df05cc6e09649cdb0631d0b7279b76d0f1be 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -255,12 +255,11 @@ interface IWindowSession { void updatePointerIcon(IWindow window); /** - * Update a tap exclude region with a rectangular area identified by provided id in the window. - * Touches on this region will not switch focus to this window. Passing an empty rect will - * remove the area from the exclude region of this window. + * Update a tap exclude region identified by provided id in the window. Touches on this region + * will neither be dispatched to this window nor change the focus to this window. Passing an + * invalid region will remove the area from the exclude region of this window. */ - void updateTapExcludeRegion(IWindow window, int regionId, int left, int top, int width, - int height); + void updateTapExcludeRegion(IWindow window, int regionId, in Region region); /** * Called when the client has changed the local insets state, and now the server should reflect diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 13b0cc038fcea313132b7fb39514fa5101842fcd..b76f2a1753463bde12c395f35eb9d4c4e6a70c94 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -17,7 +17,10 @@ package android.view; import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; +import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE; +import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES; import static android.view.WindowInsets.Type.SIZE; +import static android.view.WindowInsets.Type.SYSTEM_GESTURES; import static android.view.WindowInsets.Type.indexOf; import android.annotation.IntDef; @@ -55,6 +58,12 @@ public class InsetsState implements Parcelable { TYPE_SIDE_BAR_1, TYPE_SIDE_BAR_2, TYPE_SIDE_BAR_3, + TYPE_TOP_GESTURES, + TYPE_BOTTOM_GESTURES, + TYPE_LEFT_GESTURES, + TYPE_RIGHT_GESTURES, + TYPE_TOP_TAPPABLE_ELEMENT, + TYPE_BOTTOM_TAPPABLE_ELEMENT, TYPE_IME }) public @interface InternalInsetType {} @@ -73,8 +82,16 @@ public class InsetsState implements Parcelable { public static final int TYPE_SIDE_BAR_2 = 2; public static final int TYPE_SIDE_BAR_3 = 3; + public static final int TYPE_TOP_GESTURES = 4; + public static final int TYPE_BOTTOM_GESTURES = 5; + public static final int TYPE_LEFT_GESTURES = 6; + public static final int TYPE_RIGHT_GESTURES = 7; + public static final int TYPE_TOP_TAPPABLE_ELEMENT = 8; + public static final int TYPE_BOTTOM_TAPPABLE_ELEMENT = 9; + /** Input method window. */ - public static final int TYPE_IME = 4; + public static final int TYPE_IME = 10; + static final int LAST_TYPE = TYPE_IME; // Derived types @@ -137,17 +154,6 @@ public class InsetsState implements Parcelable { && legacyContentInsets != null && legacyStableInsets != null) { WindowInsets.assignCompatInsets(typeInsetsMap, legacyContentInsets); WindowInsets.assignCompatInsets(typeMaxInsetsMap, legacyStableInsets); - - // TODO: set system gesture insets based on actual system gesture area. - typeInsetsMap[Type.indexOf(Type.systemGestures())] = Insets.of(legacyContentInsets); - typeInsetsMap[Type.indexOf(Type.mandatorySystemGestures())] = - Insets.of(legacyContentInsets); - typeInsetsMap[Type.indexOf(Type.tappableElement())] = Insets.of(legacyContentInsets); - - typeMaxInsetsMap[Type.indexOf(Type.systemGestures())] = Insets.of(legacyStableInsets); - typeMaxInsetsMap[Type.indexOf(Type.mandatorySystemGestures())] = - Insets.of(legacyStableInsets); - typeMaxInsetsMap[Type.indexOf(Type.tappableElement())] = Insets.of(legacyStableInsets); } for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { InsetsSource source = mSources.get(type); @@ -159,7 +165,9 @@ public class InsetsState implements Parcelable { && (type == TYPE_TOP_BAR || type == TYPE_NAVIGATION_BAR); boolean skipIme = source.getType() == TYPE_IME && (legacySoftInputMode & LayoutParams.SOFT_INPUT_ADJUST_RESIZE) == 0; - if (skipSystemBars || skipIme) { + boolean skipLegacyTypes = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE + && (toPublicType(type) & Type.compatSystemInsets()) != 0; + if (skipSystemBars || skipIme || skipLegacyTypes) { typeVisibilityMap[indexOf(toPublicType(type))] = source.isVisible(); continue; } @@ -183,7 +191,25 @@ public class InsetsState implements Parcelable { @Nullable boolean[] typeVisibilityMap) { Insets insets = source.calculateInsets(relativeFrame, ignoreVisibility); - int index = indexOf(toPublicType(source.getType())); + int type = toPublicType(source.getType()); + processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap, + insets, type); + + if (type == MANDATORY_SYSTEM_GESTURES) { + // Mandatory system gestures are also system gestures. + // TODO: find a way to express this more generally. One option would be to define + // Type.systemGestureInsets() as NORMAL | MANDATORY, but then we lose the + // ability to set systemGestureInsets() independently from + // mandatorySystemGestureInsets() in the Builder. + processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap, + insets, SYSTEM_GESTURES); + } + } + + private void processSourceAsPublicType(InsetsSource source, Insets[] typeInsetsMap, + @InsetSide @Nullable SparseIntArray typeSideMap, + @Nullable boolean[] typeVisibilityMap, Insets insets, int type) { + int index = indexOf(type); Insets existing = typeInsetsMap[index]; if (existing == null) { typeInsetsMap[index] = insets; @@ -300,6 +326,15 @@ public class InsetsState implements Parcelable { return Type.SIDE_BARS; case TYPE_IME: return Type.IME; + case TYPE_TOP_GESTURES: + case TYPE_BOTTOM_GESTURES: + return Type.MANDATORY_SYSTEM_GESTURES; + case TYPE_LEFT_GESTURES: + case TYPE_RIGHT_GESTURES: + return Type.SYSTEM_GESTURES; + case TYPE_TOP_TAPPABLE_ELEMENT: + case TYPE_BOTTOM_TAPPABLE_ELEMENT: + return Type.TAPPABLE_ELEMENT; default: throw new IllegalArgumentException("Unknown type: " + type); } @@ -336,10 +371,20 @@ public class InsetsState implements Parcelable { return "TYPE_SIDE_BAR_2"; case TYPE_SIDE_BAR_3: return "TYPE_SIDE_BAR_3"; - case TYPE_IME: - return "TYPE_IME"; + case TYPE_TOP_GESTURES: + return "TYPE_TOP_GESTURES"; + case TYPE_BOTTOM_GESTURES: + return "TYPE_BOTTOM_GESTURES"; + case TYPE_LEFT_GESTURES: + return "TYPE_LEFT_GESTURES"; + case TYPE_RIGHT_GESTURES: + return "TYPE_RIGHT_GESTURES"; + case TYPE_TOP_TAPPABLE_ELEMENT: + return "TYPE_TOP_TAPPABLE_ELEMENT"; + case TYPE_BOTTOM_TAPPABLE_ELEMENT: + return "TYPE_BOTTOM_TAPPABLE_ELEMENT"; default: - return "TYPE_UNKNOWN"; + return "TYPE_UNKNOWN_" + type; } } diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java index a3cd0ba4b8236d1e6b207d1ae24dd19e977467ad..8b3b10f48a4a0a43b2730530e3e7a13812ae1e31 100644 --- a/core/java/android/view/MenuInflater.java +++ b/core/java/android/view/MenuInflater.java @@ -23,7 +23,7 @@ import android.content.ContextWrapper; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; -import android.graphics.PorterDuff; +import android.graphics.BlendMode; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; @@ -308,7 +308,7 @@ public class MenuInflater { private CharSequence itemTitleCondensed; private int itemIconResId; private ColorStateList itemIconTintList = null; - private PorterDuff.Mode itemIconTintMode = null; + private BlendMode mItemIconBlendMode = null; private char itemAlphabeticShortcut; private int itemAlphabeticModifiers; private char itemNumericShortcut; @@ -401,12 +401,12 @@ public class MenuInflater { itemTitleCondensed = a.getText(com.android.internal.R.styleable.MenuItem_titleCondensed); itemIconResId = a.getResourceId(com.android.internal.R.styleable.MenuItem_icon, 0); if (a.hasValue(com.android.internal.R.styleable.MenuItem_iconTintMode)) { - itemIconTintMode = Drawable.parseTintMode(a.getInt( + mItemIconBlendMode = Drawable.parseBlendMode(a.getInt( com.android.internal.R.styleable.MenuItem_iconTintMode, -1), - itemIconTintMode); + mItemIconBlendMode); } else { // Reset to null so that it's not carried over to the next item - itemIconTintMode = null; + mItemIconBlendMode = null; } if (a.hasValue(com.android.internal.R.styleable.MenuItem_iconTint)) { itemIconTintList = a.getColorStateList( @@ -487,8 +487,8 @@ public class MenuInflater { item.setShowAsAction(itemShowAsAction); } - if (itemIconTintMode != null) { - item.setIconTintMode(itemIconTintMode); + if (mItemIconBlendMode != null) { + item.setIconTintMode(mItemIconBlendMode); } if (itemIconTintList != null) { diff --git a/core/java/android/view/MenuItem.java b/core/java/android/view/MenuItem.java index ad160cbf41eb4b44fff9201d8afdc8d2d58c22fd..37853103ce02fa65d6bdfe6617f0b9aa25e5f940 100644 --- a/core/java/android/view/MenuItem.java +++ b/core/java/android/view/MenuItem.java @@ -18,11 +18,13 @@ package android.view; import android.annotation.DrawableRes; import android.annotation.LayoutRes; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; import android.app.Activity; import android.content.Intent; import android.content.res.ColorStateList; +import android.graphics.BlendMode; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.view.ContextMenu.ContextMenuInfo; @@ -268,8 +270,33 @@ public interface MenuItem { * @attr ref android.R.styleable#MenuItem_iconTintMode * @see #setIconTintList(ColorStateList) * @see Drawable#setTintMode(PorterDuff.Mode) + * @see Drawable#setTintMode(BlendMode) + * + * @deprecated use {@link #setIconTintMode(BlendMode)} instead */ - public default MenuItem setIconTintMode(@Nullable PorterDuff.Mode tintMode) { return this; } + @Deprecated + default @NonNull MenuItem setIconTintMode(@Nullable PorterDuff.Mode tintMode) { + return this; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setIconTintList(ColorStateList)} to this item's icon. The default mode is + * {@link BlendMode#SRC_IN}. + * + * @param blendMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * @attr ref android.R.styleable#MenuItem_iconTintMode + * @see #setIconTintList(ColorStateList) + */ + default @NonNull MenuItem setIconTintMode(@Nullable BlendMode blendMode) { + PorterDuff.Mode mode = BlendMode.blendModeToPorterDuffMode(blendMode); + if (mode != null) { + return setIconTintMode(mode); + } else { + return this; + } + } /** * Returns the blending mode used to apply the tint to this item's icon, if specified. @@ -277,9 +304,31 @@ public interface MenuItem { * @return the blending mode used to apply the tint to this item's icon * @attr ref android.R.styleable#MenuItem_iconTintMode * @see #setIconTintMode(PorterDuff.Mode) + * @see #setIconTintMode(BlendMode) + * + * @deprecated Use {@link #getIconTintBlendMode()} instead */ + @Deprecated @Nullable public default PorterDuff.Mode getIconTintMode() { return null; } + + /** + * Returns the blending mode used to apply the tint to this item's icon, if specified. + * + * @return the blending mode used to apply the tint to this item's icon + * @attr ref android.R.styleable#MenuItem_iconTintMode + * @see #setIconTintMode(BlendMode) + * + */ + @Nullable + default BlendMode getIconTintBlendMode() { + PorterDuff.Mode mode = getIconTintMode(); + if (mode != null) { + return BlendMode.fromValue(mode.nativeInt); + } else { + return null; + } + } /** * Change the Intent associated with this item. By default there is no diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2357db46771a3669661cd7dca57b9aadcea78833..65fe87fa8ca01cfb842fe6cb00c3869f3304a9c2 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -17,6 +17,10 @@ package android.view; import static android.content.res.Resources.ID_NULL; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP; +import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION; import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED; @@ -50,6 +54,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; +import android.graphics.BlendMode; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Insets; @@ -85,7 +90,6 @@ import android.os.Trace; import android.sysprop.DisplayProperties; import android.text.InputType; import android.text.TextUtils; -import android.util.ArrayMap; import android.util.AttributeSet; import android.util.FloatProperty; import android.util.LayoutDirection; @@ -96,6 +100,7 @@ import android.util.Property; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.StateSet; +import android.util.StatsLog; import android.util.SuperNotCalledException; import android.util.TypedValue; import android.view.AccessibilityIterators.CharacterTextSegmentIterator; @@ -4062,11 +4067,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, }, formatToHexString = true) /* @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769414) public int mPrivateFlags; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768943) int mPrivateFlags2; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 129147060) int mPrivateFlags3; private int mPrivateFlags4; @@ -4506,7 +4511,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, static class TintInfo { ColorStateList mTintList; - PorterDuff.Mode mTintMode; + BlendMode mBlendMode; boolean mHasTintMode; boolean mHasTintList; } @@ -5687,7 +5692,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mBackgroundTint == null) { mBackgroundTint = new TintInfo(); } - mBackgroundTint.mTintMode = Drawable.parseTintMode(a.getInt( + mBackgroundTint.mBlendMode = Drawable.parseBlendMode(a.getInt( R.styleable.View_backgroundTintMode, -1), null); mBackgroundTint.mHasTintMode = true; break; @@ -5707,7 +5712,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, break; case R.styleable.View_foregroundTintMode: if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) { - setForegroundTintMode(Drawable.parseTintMode(a.getInt(attr, -1), null)); + setForegroundTintMode(Drawable.parseBlendMode(a.getInt(attr, -1), null)); } break; case R.styleable.View_foregroundTint: @@ -7955,6 +7960,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * View is not a pane. * * {@see AccessibilityNodeInfo#setPaneTitle(CharSequence)} + * + * @attr ref android.R.styleable#View_accessibilityPaneTitle */ public void setAccessibilityPaneTitle(@Nullable CharSequence accessibilityPaneTitle) { if (!TextUtils.equals(accessibilityPaneTitle, mAccessibilityPaneTitle)) { @@ -7970,6 +7977,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The current pane title. * * {@see #setAccessibilityPaneTitle}. + * + * @attr ref android.R.styleable#View_accessibilityPaneTitle */ @InspectableProperty @Nullable @@ -8524,11 +8533,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Populates a {@link ViewStructure} for Content Capture. + * Populates a {@link ViewStructure} for content capture. * - *

    This method is called after a view is that is eligible for Content Capture + *

    This method is called after a view is that is eligible for content capture * (for example, if it {@link #isImportantForAutofill()}, an intelligence service is enabled for - * the user, and the activity rendering the view is enabled for Content Capture) is laid out and + * the user, and the activity rendering the view is enabled for content capture) is laid out and * is visible. * *

    The populated structure is then passed to the service through @@ -8547,6 +8556,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@code childStructure.getAutofillId()} or * {@link ContentCaptureSession#newAutofillId(AutofillId, long)}. * + *

    When the virtual view hierarchy represents a web page, you should also: + * + *

      + *
    • Call {@link ContentCaptureManager#getContentCaptureConditions()} to infer content + * capture events should be generate for that URL. + *
    • Create a new {@link ContentCaptureSession} child for every HTML element that + * renders a new URL (like an {@code IFRAME}) and use that session to notify events from + * that subtree. + *
    + * *

    Note: the following methods of the {@code structure} will be ignored: *

      *
    • {@link ViewStructure#setChildCount(int)} @@ -9263,11 +9282,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Hints the Android System whether this view is considered important for Content Capture, based + * Hints the Android System whether this view is considered important for content capture, based * on the value explicitly set by {@link #setImportantForContentCapture(int)} and heuristics * when it's {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO}. * - * @return whether the view is considered important for autofill. + *

      See {@link ContentCaptureManager} for more info about content capture. + * + * @return whether the view is considered important for content capture. * * @see #setImportantForContentCapture(int) * @see #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO @@ -9466,7 +9487,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Sets the (optional) {@link ContentCaptureSession} associated with this view. * *

      This method should be called when you need to associate a {@link ContentCaptureContext} to - * the Content Capture events associated with this view or its view hierarchy (if it's a + * the content capture events associated with this view or its view hierarchy (if it's a * {@link ViewGroup}). * *

      For example, if your activity is associated with a web domain, first you would need to @@ -9497,7 +9518,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Gets the session used to notify Content Capture events. + * Gets the session used to notify content capture events. * * @return session explicitly set by {@link #setContentCaptureSession(ContentCaptureSession)}, * inherited by ancestors, default session or {@code null} if content capture is disabled for @@ -9718,7 +9739,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Dispatches the initial Content Capture events for a view structure. + * Dispatches the initial content capture events for a view structure. * * @hide */ @@ -12100,6 +12121,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #setScreenReaderFocusable(boolean) * * @return Whether the view should be treated as a focusable unit by screen reader. + * + * @attr ref android.R.styleable#View_screenReaderFocusable */ @InspectableProperty public boolean isScreenReaderFocusable() { @@ -12118,6 +12141,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @param screenReaderFocusable Whether the view should be treated as a unit by screen reader * accessibility tools. + * + * @attr ref android.R.styleable#View_screenReaderFocusable */ public void setScreenReaderFocusable(boolean screenReaderFocusable) { updatePflags3AndNotifyA11yIfChanged(PFLAG3_SCREEN_READER_FOCUSABLE, screenReaderFocusable); @@ -13898,6 +13923,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, .getAccessibilityFocusedHost() == this); } + /** + * Returns whether this view can receive pointer events. + * + * @return {@code true} if this view can receive pointer events. + * @hide + */ + protected boolean canReceivePointerEvents() { + return (mViewFlags & VISIBILITY_MASK) == VISIBLE || getAnimation() != null; + } + /** * Filter the touch event to apply security policies. * @@ -14541,7 +14576,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (clickable) { setPressed(true, x, y); } - checkForLongClick(ViewConfiguration.getLongPressTimeout(), x, y); + checkForLongClick( + ViewConfiguration.getLongPressTimeout(), + x, + y, + // This is not a touch gesture -- do not classify it as one. + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION); return true; } } @@ -15282,7 +15322,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mHasPerformedLongPress = false; if (!clickable) { - checkForLongClick(ViewConfiguration.getLongPressTimeout(), x, y); + checkForLongClick( + ViewConfiguration.getLongPressTimeout(), + x, + y, + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS); break; } @@ -15306,7 +15350,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { // Not inside a scrolling container, so show the feedback right away setPressed(true, x, y); - checkForLongClick(ViewConfiguration.getLongPressTimeout(), x, y); + checkForLongClick( + ViewConfiguration.getLongPressTimeout(), + x, + y, + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS); } break; @@ -15343,7 +15391,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * ambiguousMultiplier); // Subtract the time already spent delay -= event.getEventTime() - event.getDownTime(); - checkForLongClick(delay, x, y); + checkForLongClick( + delay, + x, + y, + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS); } touchSlop *= ambiguousMultiplier; } @@ -15365,7 +15417,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (deepPress && hasPendingLongPressCallback()) { // process the long click action immediately removeLongPressCallback(); - checkForLongClick(0 /* send immediately */, x, y); + checkForLongClick( + 0 /* send immediately */, + x, + y, + TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS); } break; @@ -17938,7 +17994,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final int scrollX = mScrollX; final int scrollY = mScrollY; invalidateInternal(dirty.left - scrollX, dirty.top - scrollY, - dirty.right - scrollX, dirty.bottom - scrollY, true, false); + dirty.right - scrollX, dirty.bottom - scrollY, true); } /** @@ -17964,7 +18020,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public void invalidate(int l, int t, int r, int b) { final int scrollX = mScrollX; final int scrollY = mScrollY; - invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true, false); + invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true); } /** @@ -17994,11 +18050,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @UnsupportedAppUsage public void invalidate(boolean invalidateCache) { - invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true); + invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache); } - void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, - boolean fullInvalidate) { + void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache) { if (mGhostView != null) { mGhostView.invalidate(true); return; @@ -18015,11 +18070,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED - || (fullInvalidate && isOpaque() != mLastIsOpaque)) { - if (fullInvalidate) { - mLastIsOpaque = isOpaque(); - mPrivateFlags &= ~PFLAG_DRAWN; - } + || isOpaque() != mLastIsOpaque) { + mLastIsOpaque = isOpaque(); + mPrivateFlags &= ~PFLAG_DRAWN; mPrivateFlags |= PFLAG_DIRTY; @@ -22564,12 +22617,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @Override public void invalidateDrawable(@NonNull Drawable drawable) { if (verifyDrawable(drawable)) { - final Rect dirty = drawable.getDirtyBounds(); - final int scrollX = mScrollX; - final int scrollY = mScrollY; - - invalidate(dirty.left + scrollX, dirty.top + scrollY, - dirty.right + scrollX, dirty.bottom + scrollY); + invalidate(); rebuildOutline(); } } @@ -23266,7 +23314,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Applies a tint to the background drawable. Does not modify the current tint - * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. + * mode, which is {@link BlendMode#SRC_IN} by default. *

      * Subsequent calls to {@link #setBackground(Drawable)} will automatically * mutate the drawable and apply the specified tint and tint mode using @@ -23311,12 +23359,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_backgroundTintMode * @see #getBackgroundTintMode() * @see Drawable#setTintMode(PorterDuff.Mode) + * + * @deprecated use @setBackgroundTintMode(BlendMode) instead */ + @Deprecated public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { + BlendMode mode = null; + if (tintMode != null) { + mode = BlendMode.fromValue(tintMode.nativeInt); + } + + setBackgroundTintMode(mode); + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setBackgroundTintList(ColorStateList)}} to the background + * drawable. The default mode is {@link BlendMode#SRC_IN}. + * + * @param blendMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * @attr ref android.R.styleable#View_backgroundTintMode + * @see #getBackgroundTintMode() + * @see Drawable#setTintMode(BlendMode) + */ + public void setBackgroundTintMode(@Nullable BlendMode blendMode) { if (mBackgroundTint == null) { mBackgroundTint = new TintInfo(); } - mBackgroundTint.mTintMode = tintMode; + + mBackgroundTint.mBlendMode = blendMode; mBackgroundTint.mHasTintMode = true; applyBackgroundTint(); @@ -23329,12 +23401,34 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return the blending mode used to apply the tint to the background * drawable * @attr ref android.R.styleable#View_backgroundTintMode - * @see #setBackgroundTintMode(PorterDuff.Mode) + * @see #setBackgroundTintMode(BlendMode) + * + * @deprecated use #getBackgroundBlendMode() instead */ @Nullable @InspectableProperty + @Deprecated public PorterDuff.Mode getBackgroundTintMode() { - return mBackgroundTint != null ? mBackgroundTint.mTintMode : null; + PorterDuff.Mode porterDuffMode; + if (mBackgroundTint != null && mBackgroundTint.mBlendMode != null) { + porterDuffMode = BlendMode.blendModeToPorterDuffMode(mBackgroundTint.mBlendMode); + } else { + porterDuffMode = null; + } + return porterDuffMode; + } + + /** + * Return the blending mode used to apply the tint to the background + * drawable, if specified. + * + * @return the blending mode used to apply the tint to the background + * drawable, null if no blend has previously been configured + * @attr ref android.R.styleable#View_backgroundTintMode + * @see #setBackgroundTintMode(BlendMode) + */ + public @Nullable BlendMode getBackgroundBlendMode() { + return mBackgroundTint != null ? mBackgroundTint.mBlendMode : null; } private void applyBackgroundTint() { @@ -23348,7 +23442,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } if (tintInfo.mHasTintMode) { - mBackground.setTintMode(tintInfo.mTintMode); + mBackground.setTintMode(tintInfo.mBlendMode); } // The drawable (or one of its children) may not have been @@ -23532,15 +23626,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_foregroundTintMode * @see #getForegroundTintMode() * @see Drawable#setTintMode(PorterDuff.Mode) + * + * @deprecated use #setForegroundTintMode(BlendMode) */ + @Deprecated public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) { + BlendMode mode = null; + if (tintMode != null) { + mode = BlendMode.fromValue(tintMode.nativeInt); + } + setForegroundTintMode(mode); + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setForegroundTintList(ColorStateList)}} to the background + * drawable. The default mode is {@link BlendMode#SRC_IN}. + * + * @param blendMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * @attr ref android.R.styleable#View_foregroundTintMode + * @see #getForegroundTintMode() + * @see Drawable#setTintMode(BlendMode) + */ + public void setForegroundTintMode(@Nullable BlendMode blendMode) { if (mForegroundInfo == null) { mForegroundInfo = new ForegroundInfo(); } if (mForegroundInfo.mTintInfo == null) { mForegroundInfo.mTintInfo = new TintInfo(); } - mForegroundInfo.mTintInfo.mTintMode = tintMode; + mForegroundInfo.mTintInfo.mBlendMode = blendMode; mForegroundInfo.mTintInfo.mHasTintMode = true; applyForegroundTint(); @@ -23554,12 +23670,35 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * drawable * @attr ref android.R.styleable#View_foregroundTintMode * @see #setForegroundTintMode(PorterDuff.Mode) + * + * @deprecated use #getForegroundBlendMode() instead */ @InspectableProperty @Nullable + @Deprecated public PorterDuff.Mode getForegroundTintMode() { + BlendMode blendMode = mForegroundInfo != null && mForegroundInfo.mTintInfo != null + ? mForegroundInfo.mTintInfo.mBlendMode : null; + if (blendMode != null) { + return BlendMode.blendModeToPorterDuffMode(blendMode); + } else { + return null; + } + } + + /** + * Return the blending mode used to apply the tint to the foreground + * drawable, if specified. + * + * @return the blending mode used to apply the tint to the foreground + * drawable + * @attr ref android.R.styleable#View_foregroundTintMode + * @see #setForegroundTintMode(BlendMode) + * + */ + public @Nullable BlendMode getForegroundBlendMode() { return mForegroundInfo != null && mForegroundInfo.mTintInfo != null - ? mForegroundInfo.mTintInfo.mTintMode : null; + ? mForegroundInfo.mTintInfo.mBlendMode : null; } private void applyForegroundTint() { @@ -23574,7 +23713,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } if (tintInfo.mHasTintMode) { - mForegroundInfo.mDrawable.setTintMode(tintInfo.mTintMode); + mForegroundInfo.mDrawable.setTintMode(tintInfo.mBlendMode); } // The drawable (or one of its children) may not have been @@ -26030,7 +26169,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - private void checkForLongClick(long delay, float x, float y) { + private void checkForLongClick(long delay, float x, float y, int classification) { if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE || (mViewFlags & TOOLTIP) == TOOLTIP) { mHasPerformedLongPress = false; @@ -26040,6 +26179,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPendingCheckForLongPress.setAnchor(x, y); mPendingCheckForLongPress.rememberWindowAttachCount(); mPendingCheckForLongPress.rememberPressedState(); + mPendingCheckForLongPress.setClassification(classification); postDelayed(mPendingCheckForLongPress, delay); } } @@ -27597,11 +27737,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private float mX; private float mY; private boolean mOriginalPressedState; + /** + * The classification of the long click being checked: one of the + * StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__* constants. + */ + private int mClassification; @Override public void run() { if ((mOriginalPressedState == isPressed()) && (mParent != null) && mOriginalWindowAttachCount == mWindowAttachCount) { + recordGestureClassification(mClassification); if (performLongClick(mX, mY)) { mHasPerformedLongPress = true; } @@ -27620,6 +27766,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public void rememberPressedState() { mOriginalPressedState = isPressed(); } + + public void setClassification(int classification) { + mClassification = classification; + } } private final class CheckForTap implements Runnable { @@ -27632,17 +27782,28 @@ public class View implements Drawable.Callback, KeyEvent.Callback, setPressed(true, x, y); final long delay = ViewConfiguration.getLongPressTimeout() - ViewConfiguration.getTapTimeout(); - checkForLongClick(delay, x, y); + checkForLongClick(delay, x, y, TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS); } } private final class PerformClick implements Runnable { @Override public void run() { + recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP); performClickInternal(); } } + /** Records a classification for the current event stream. */ + private void recordGestureClassification(int classification) { + if (classification == TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION) { + return; + } + // To avoid negatively impacting View performance, the latency and displacement metrics + // are omitted. + StatsLog.write(StatsLog.TOUCH_GESTURE_CLASSIFIED, getClass().getName(), classification); + } + /** * This method returns a ViewPropertyAnimator object, which can be used to animate * specific properties on this View. @@ -28486,8 +28647,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * hierarchy is traversed: value is either the view itself for appearead events, or its * autofill id for disappeared. */ - // TODO(b/121197119): use SparseArray once session id becomes integer - ArrayMap> mContentCaptureEvents; + SparseArray> mContentCaptureEvents; /** * Cached reference to the {@link ContentCaptureManager}. @@ -28517,9 +28677,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @NonNull View view, boolean appeared) { if (mContentCaptureEvents == null) { // Most of the time there will be just one session, so intial capacity is 1 - mContentCaptureEvents = new ArrayMap<>(1); + mContentCaptureEvents = new SparseArray<>(1); } - String sessionId = session.getId(); + int sessionId = session.getId(); // TODO: life would be much easier if we provided a MultiMap implementation somwhere... ArrayList events = mContentCaptureEvents.get(sessionId); if (events == null) { diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 4851476e3d70849e87c76e4b120c039290aa5a7c..937bd1b34e61f044fc9d48f2f6456ad2baef4f88 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2011,7 +2011,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); - if (!canViewReceivePointerEvents(child) + if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { continue; } @@ -2094,7 +2094,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView( preorderedList, children, childIndex); - if (!canViewReceivePointerEvents(child) + if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { continue; } @@ -2314,7 +2314,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager getAndVerifyPreorderedIndex(childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); - if (!canViewReceivePointerEvents(child) + if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { continue; } @@ -2500,7 +2500,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); - if (!canViewReceivePointerEvents(child) + if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { continue; } @@ -2680,7 +2680,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager i = childrenCount - 1; } - if (!canViewReceivePointerEvents(child) + if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); continue; @@ -2970,15 +2970,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } - /** - * Returns true if a child view can receive pointer events. - * @hide - */ - private static boolean canViewReceivePointerEvents(@NonNull View child) { - return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE - || child.getAnimation() != null; - } - private float[] getTempPoint() { if (mTempPoint == null) { mTempPoint = new float[2]; @@ -7199,6 +7190,46 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + /** + * @hide + */ + @Override + public void subtractObscuredTouchableRegion(Region touchableRegion, View view) { + final int childrenCount = mChildrenCount; + final ArrayList preorderedList = buildTouchDispatchChildList(); + final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); + final View[] children = mChildren; + for (int i = childrenCount - 1; i >= 0; i--) { + final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); + final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); + if (child == view) { + // We've reached the target view. + break; + } + if (!child.canReceivePointerEvents()) { + // This child cannot be touched. Skip it. + continue; + } + applyOpToRegionByBounds(touchableRegion, child, Region.Op.DIFFERENCE); + } + + // The touchable region should not exceed the bounds of its container. + applyOpToRegionByBounds(touchableRegion, this, Region.Op.INTERSECT); + + final ViewParent parent = getParent(); + if (parent != null) { + parent.subtractObscuredTouchableRegion(touchableRegion, this); + } + } + + private static void applyOpToRegionByBounds(Region region, View view, Region.Op op) { + final int[] locationInWindow = new int[2]; + view.getLocationInWindow(locationInWindow); + final int x = locationInWindow[0]; + final int y = locationInWindow[1]; + region.op(x, y, x + view.getWidth(), y + view.getHeight(), op); + } + @Override public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { insets = super.dispatchApplyWindowInsets(insets); diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java index 572e69b1a78c4aab6cb20650875199cbe40b4a1b..feba7bb7f195a37a8e23bdd31f008d5a7714e2a6 100644 --- a/core/java/android/view/ViewParent.java +++ b/core/java/android/view/ViewParent.java @@ -18,6 +18,7 @@ package android.view; import android.annotation.NonNull; import android.graphics.Rect; +import android.graphics.Region; import android.os.Bundle; import android.view.accessibility.AccessibilityEvent; @@ -660,4 +661,17 @@ public interface ViewParent { * @return true if the action was consumed by this ViewParent */ public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle arguments); + + /** + * Given a touchable region of a child, this method reduces region by the bounds of all views on + * top of the child for which {@link View#canReceivePointerEvents} returns {@code true}. This + * applies recursively for all views in the view hierarchy on top of this one. + * + * @param touchableRegion The touchable region we want to modify. + * @param view A child view of this ViewGroup which indicates the z-order of the touchable + * region. + * @hide + */ + default void subtractObscuredTouchableRegion(Region touchableRegion, View view) { + } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 2880e7f135456689b458d14012852220b99c746e..f3b7ad5e557c5f021429d4879a7d8ca340d139ac 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1455,6 +1455,8 @@ public final class ViewRootImpl implements ViewParent, @Override public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) { + // TODO: Re-enable after camera is fixed or consider targetSdk checking this + // checkThread(); if ((descendant.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) { mIsAnimating = true; } @@ -1915,16 +1917,10 @@ public final class ViewRootImpl implements ViewParent, } contentInsets = ensureInsetsNonNegative(contentInsets, "content"); stableInsets = ensureInsetsNonNegative(stableInsets, "stable"); - if (sNewInsetsMode != NEW_INSETS_MODE_NONE) { - mLastWindowInsets = mInsetsController.calculateInsets( - mContext.getResources().getConfiguration().isScreenRound(), - mAttachInfo.mAlwaysConsumeSystemBars, displayCutout, - contentInsets, stableInsets, mWindowAttributes.softInputMode); - } else { - mLastWindowInsets = new WindowInsets(contentInsets, stableInsets, - mContext.getResources().getConfiguration().isScreenRound(), - mAttachInfo.mAlwaysConsumeSystemBars, displayCutout); - } + mLastWindowInsets = mInsetsController.calculateInsets( + mContext.getResources().getConfiguration().isScreenRound(), + mAttachInfo.mAlwaysConsumeSystemBars, displayCutout, + contentInsets, stableInsets, mWindowAttributes.softInputMode); } return mLastWindowInsets; } @@ -1985,7 +1981,6 @@ public final class ViewRootImpl implements ViewParent, mIsInTraversal = true; mWillDrawSoon = true; boolean windowSizeMayChange = false; - boolean newSurface = false; boolean surfaceChanged = false; WindowManager.LayoutParams lp = mWindowAttributes; @@ -2386,13 +2381,7 @@ public final class ViewRootImpl implements ViewParent, if (!hadSurface) { if (mSurface.isValid()) { // If we are creating a new surface, then we need to - // completely redraw it. Also, when we get to the - // point of drawing it we will hold off and schedule - // a new traversal instead. This is so we can tell the - // window manager about all of the windows being displayed - // before actually drawing them, so it can display then - // all at once. - newSurface = true; + // completely redraw it. mFullRedrawNeeded = true; mPreviousTransparentRegion.setEmpty(); @@ -2777,7 +2766,7 @@ public final class ViewRootImpl implements ViewParent, boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible; - if (!cancelDraw && !newSurface) { + if (!cancelDraw) { if (mPendingTransitions != null && mPendingTransitions.size() > 0) { for (int i = 0; i < mPendingTransitions.size(); ++i) { mPendingTransitions.get(i).startChangingAnimations(); @@ -2811,8 +2800,7 @@ public final class ViewRootImpl implements ViewParent, MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager .getMainContentCaptureSession(); for (int i = 0; i < mAttachInfo.mContentCaptureEvents.size(); i++) { - String sessionId = mAttachInfo.mContentCaptureEvents - .keyAt(i); + int sessionId = mAttachInfo.mContentCaptureEvents.keyAt(i); mainSession.notifyViewTreeEvent(sessionId, /* started= */ true); ArrayList events = mAttachInfo.mContentCaptureEvents .valueAt(i); @@ -2827,8 +2815,8 @@ public final class ViewRootImpl implements ViewParent, Log.w(mTag, "no content capture session on view: " + view); continue for_each_event; } - String actualId = session.getId().toString(); - if (!actualId.equals(sessionId)) { + int actualId = session.getId(); + if (actualId != sessionId) { Log.w(mTag, "content capture session mismatch for view (" + view + "): was " + sessionId + " before, it's " + actualId + " now"); continue for_each_event; @@ -3986,7 +3974,7 @@ public final class ViewRootImpl implements ViewParent, void systemGestureExclusionChanged() { final List rectsForWindowManager = mGestureExclusionTracker.computeChangedRects(); - if (rectsForWindowManager != null) { + if (rectsForWindowManager != null && mView != null) { try { mWindowSession.reportSystemGestureExclusionChanged(mWindow, rectsForWindowManager); } catch (RemoteException e) { diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index ffa769a424a999d2a580dd17a7e533ca6bbe8667..2d292ef7b25c9ebba1f8a4b2c6d8368ab7856cef 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -29,9 +29,6 @@ import static android.view.WindowInsets.Type.TOP_BAR; import static android.view.WindowInsets.Type.all; import static android.view.WindowInsets.Type.compatSystemInsets; import static android.view.WindowInsets.Type.indexOf; -import static android.view.WindowInsets.Type.mandatorySystemGestures; -import static android.view.WindowInsets.Type.systemGestures; -import static android.view.WindowInsets.Type.tappableElement; import android.annotation.IntDef; import android.annotation.IntRange; @@ -225,10 +222,6 @@ public final class WindowInsets { } Insets[] typeInsetMap = new Insets[SIZE]; assignCompatInsets(typeInsetMap, insets); - // TODO: set system gesture insets based on actual system gesture area. - typeInsetMap[indexOf(systemGestures())] = Insets.of(insets); - typeInsetMap[indexOf(mandatorySystemGestures())] = Insets.of(insets); - typeInsetMap[indexOf(tappableElement())] = Insets.of(insets); return typeInsetMap; } diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java index df8690d2eef82dd1ff0684900de526ee3cc8d68b..f9e601f36e0e9d6d5e7159bd13a559b6fb80c6ca 100644 --- a/core/java/android/view/accessibility/AccessibilityCache.java +++ b/core/java/android/view/accessibility/AccessibilityCache.java @@ -105,11 +105,7 @@ public class AccessibilityCache { Log.i(LOG_TAG, "Caching window: " + window.getId()); } final int windowId = window.getId(); - AccessibilityWindowInfo oldWindow = mWindowCache.get(windowId); - if (oldWindow != null) { - oldWindow.recycle(); - } - mWindowCache.put(windowId, AccessibilityWindowInfo.obtain(window)); + mWindowCache.put(windowId, new AccessibilityWindowInfo(window)); } } @@ -225,7 +221,7 @@ public class AccessibilityCache { if (info != null) { // Return a copy since the client calls to AccessibilityNodeInfo#recycle() // will wipe the data of the cached info. - info = AccessibilityNodeInfo.obtain(info); + info = new AccessibilityNodeInfo(info); } if (DEBUG) { Log.i(LOG_TAG, "get(" + accessibilityNodeId + ") = " + info); @@ -257,7 +253,7 @@ public class AccessibilityCache { List windows = new ArrayList<>(sortedWindowCount); for (int i = sortedWindowCount - 1; i >= 0; i--) { AccessibilityWindowInfo window = sortedWindows.valueAt(i); - windows.add(AccessibilityWindowInfo.obtain(window)); + windows.add(new AccessibilityWindowInfo(window)); sortedWindows.removeAt(i); } @@ -271,7 +267,7 @@ public class AccessibilityCache { synchronized (mLock) { AccessibilityWindowInfo window = mWindowCache.get(windowId); if (window != null) { - return AccessibilityWindowInfo.obtain(window); + return new AccessibilityWindowInfo(window); } return null; } @@ -326,14 +322,12 @@ public class AccessibilityCache { final long oldParentId = oldInfo.getParentNodeId(); if (info.getParentNodeId() != oldParentId) { clearSubTreeLocked(windowId, oldParentId); - } else { - oldInfo.recycle(); } } // Cache a copy since the client calls to AccessibilityNodeInfo#recycle() // will wipe the data of the cached info. - AccessibilityNodeInfo clone = AccessibilityNodeInfo.obtain(info); + AccessibilityNodeInfo clone = new AccessibilityNodeInfo(info); nodes.put(sourceId, clone); if (clone.isAccessibilityFocused()) { if (mAccessibilityFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID @@ -371,12 +365,7 @@ public class AccessibilityCache { } private void clearWindowCache() { - final int windowCount = mWindowCache.size(); - for (int i = windowCount - 1; i >= 0; i--) { - AccessibilityWindowInfo window = mWindowCache.valueAt(i); - window.recycle(); - mWindowCache.removeAt(i); - } + mWindowCache.clear(); mIsAllWindowsCached = false; } @@ -391,13 +380,6 @@ public class AccessibilityCache { if (nodes == null) { return; } - // Recycle the nodes before clearing the cache. - final int nodeCount = nodes.size(); - for (int i = nodeCount - 1; i >= 0; i--) { - AccessibilityNodeInfo info = nodes.valueAt(i); - nodes.removeAt(i); - info.recycle(); - } mNodeCache.remove(windowId); } @@ -440,11 +422,9 @@ public class AccessibilityCache { for (int i = 0; i < childCount; i++) { final long childNodeId = current.getChildId(i); if (clearSubTreeRecursiveLocked(nodes, childNodeId)) { - current.recycle(); return true; } } - current.recycle(); return false; } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index a8a787edde04b2a47d934fbc72b5dec65e1d8fcb..774a359e5d6c23adc2edd24789480cb11edb5d62 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -654,6 +654,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32; + // TODO(b/129300068): Remove sNumInstancesInUse. private static AtomicInteger sNumInstancesInUse; /** @@ -766,6 +767,11 @@ public class AccessibilityNodeInfo implements Parcelable { /* do nothing */ } + /** @hide */ + AccessibilityNodeInfo(AccessibilityNodeInfo info) { + init(info); + } + /** * Sets the source. *

      @@ -1676,17 +1682,29 @@ public class AccessibilityNodeInfo implements Parcelable { } /** - * Gets the node bounds in parent coordinates. + * Gets the node bounds in the viewParent's coordinates. + * {@link #getParent()} does not represent the source's viewParent. + * Instead it represents the result of {@link View#getParentForAccessibility()}, + * which returns the closest ancestor where {@link View#isImportantForAccessibility()} is true. + * So this method is not reliable. * * @param outBounds The output node bounds. + * @deprecated Use {@link #getBoundsInScreen(Rect)} instead. + * */ + @Deprecated public void getBoundsInParent(Rect outBounds) { outBounds.set(mBoundsInParent.left, mBoundsInParent.top, mBoundsInParent.right, mBoundsInParent.bottom); } /** - * Sets the node bounds in parent coordinates. + * Sets the node bounds in the viewParent's coordinates. + * {@link #getParent()} does not represent the source's viewParent. + * Instead it represents the result of {@link View#getParentForAccessibility()}, + * which returns the closest ancestor where {@link View#isImportantForAccessibility()} is true. + * So this method is not reliable. + * *

      * Note: Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. @@ -1696,7 +1714,9 @@ public class AccessibilityNodeInfo implements Parcelable { * @param bounds The node bounds. * * @throws IllegalStateException If called from an AccessibilityService. + * @deprecated Accessibility services should not care about these bounds. */ + @Deprecated public void setBoundsInParent(Rect bounds) { enforceNotSealed(); mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom); diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java index 4383f8a02d43c754cc6413f8ae28f44149e05e02..1e2321cf60d97608de6a8246751e4108c04f66d9 100644 --- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java +++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java @@ -96,6 +96,7 @@ public final class AccessibilityWindowInfo implements Parcelable { private static final int MAX_POOL_SIZE = 10; private static final SynchronizedPool sPool = new SynchronizedPool(MAX_POOL_SIZE); + // TODO(b/129300068): Remove sNumInstancesInUse. private static AtomicInteger sNumInstancesInUse; // Data. @@ -115,6 +116,11 @@ public final class AccessibilityWindowInfo implements Parcelable { /* do nothing - hide constructor */ } + /** @hide */ + AccessibilityWindowInfo(AccessibilityWindowInfo info) { + init(info); + } + /** * Gets the title of the window. * @@ -448,26 +454,7 @@ public final class AccessibilityWindowInfo implements Parcelable { */ public static AccessibilityWindowInfo obtain(AccessibilityWindowInfo info) { AccessibilityWindowInfo infoClone = obtain(); - - infoClone.mType = info.mType; - infoClone.mLayer = info.mLayer; - infoClone.mBooleanProperties = info.mBooleanProperties; - infoClone.mId = info.mId; - infoClone.mParentId = info.mParentId; - infoClone.mBoundsInScreen.set(info.mBoundsInScreen); - infoClone.mTitle = info.mTitle; - infoClone.mAnchorId = info.mAnchorId; - - if (info.mChildIds != null && info.mChildIds.size() > 0) { - if (infoClone.mChildIds == null) { - infoClone.mChildIds = info.mChildIds.clone(); - } else { - infoClone.mChildIds.addAll(info.mChildIds); - } - } - - infoClone.mConnectionId = info.mConnectionId; - + infoClone.init(info); return infoClone; } @@ -529,6 +516,32 @@ public final class AccessibilityWindowInfo implements Parcelable { parcel.writeInt(mConnectionId); } + /** + * Initializes this instance from another one. + * + * @param other The other instance. + */ + private void init(AccessibilityWindowInfo other) { + mType = other.mType; + mLayer = other.mLayer; + mBooleanProperties = other.mBooleanProperties; + mId = other.mId; + mParentId = other.mParentId; + mBoundsInScreen.set(other.mBoundsInScreen); + mTitle = other.mTitle; + mAnchorId = other.mAnchorId; + + if (other.mChildIds != null && other.mChildIds.size() > 0) { + if (mChildIds == null) { + mChildIds = other.mChildIds.clone(); + } else { + mChildIds.addAll(other.mChildIds); + } + } + + mConnectionId = other.mConnectionId; + } + private void initFromParcel(Parcel parcel) { mType = parcel.readInt(); mLayer = parcel.readInt(); diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 77a0c4c5f4eef2231d5cd41c85ea30719de8d711..6503a800acb708b855c0490dcb4878152f4a4518 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -2178,6 +2178,18 @@ public final class AutofillManager { boolean saveOnAllViewsInvisible, boolean saveOnFinish, @Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) { synchronized (mLock) { + if (sVerbose) { + Log.v(TAG, "setTrackedViews(): sessionId=" + sessionId + + ", trackedIds=" + Arrays.toString(trackedIds) + + ", saveOnAllViewsInvisible=" + saveOnAllViewsInvisible + + ", saveOnFinish=" + saveOnFinish + + ", fillableIds=" + Arrays.toString(fillableIds) + + ", saveTrigerId=" + saveTriggerId + + ", mFillableIds=" + mFillableIds + + ", mEnabled=" + mEnabled + + ", mSessionId=" + mSessionId); + + } if (mEnabled && mSessionId == sessionId) { if (saveOnAllViewsInvisible) { mTrackedViews = new TrackedViews(trackedIds); @@ -2192,10 +2204,6 @@ public final class AutofillManager { for (AutofillId id : fillableIds) { mFillableIds.add(id); } - if (sVerbose) { - Log.v(TAG, "setTrackedViews(): fillableIds=" + Arrays.toString(fillableIds) - + ", mFillableIds" + mFillableIds); - } } if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) { diff --git a/core/java/android/view/autofill/AutofillManagerInternal.java b/core/java/android/view/autofill/AutofillManagerInternal.java index d5862bd2f9428eadcf25ec862857e7fda51e53b5..3de1a03d98e5390474cc107a4a128c7df92c23ed 100644 --- a/core/java/android/view/autofill/AutofillManagerInternal.java +++ b/core/java/android/view/autofill/AutofillManagerInternal.java @@ -33,7 +33,10 @@ public abstract class AutofillManagerInternal { public abstract void onBackKeyPressed(); /** - * Gets autofill options for a package + * Gets autofill options for a package. + * + *

      NOTE: this method is called by the {@code ActivityManager} service and hence cannot + * hold the main service lock. * * @param packageName The package for which to query. * @param versionCode The package version code. diff --git a/media/java/android/media/session/ControllerCallbackLink.aidl b/core/java/android/view/contentcapture/ContentCaptureCondition.aidl similarity index 73% rename from media/java/android/media/session/ControllerCallbackLink.aidl rename to core/java/android/view/contentcapture/ContentCaptureCondition.aidl index 8ee8c7d0014839b9f2108ea86503c0c8ae52ae33..99f8894408b33f814bafeafffc70ed0d8d90a242 100644 --- a/media/java/android/media/session/ControllerCallbackLink.aidl +++ b/core/java/android/view/contentcapture/ContentCaptureCondition.aidl @@ -1,11 +1,11 @@ -/* - * Copyright 2019 The Android Open Source Project +/** + * Copyright (c) 2019, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.media.session; -parcelable ControllerCallbackLink; +package android.view.contentcapture; + +parcelable ContentCaptureCondition; diff --git a/core/java/android/view/contentcapture/ContentCaptureCondition.java b/core/java/android/view/contentcapture/ContentCaptureCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..cf171d7385249f18b9fab8b4b26a41f43a55a242 --- /dev/null +++ b/core/java/android/view/contentcapture/ContentCaptureCondition.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.view.contentcapture; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.content.LocusId; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.DebugUtils; + +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Defines a condition for when content capture should be allowed. + * + *

      See {@link ContentCaptureManager#getContentCaptureConditions()} for more. + */ +public final class ContentCaptureCondition implements Parcelable { + + /** + * When set, package should use the {@link LocusId#getId()} as a regular expression. + */ + public static final int FLAG_IS_REGEX = 0x2; + + /** @hide */ + @IntDef(prefix = { "FLAG" }, flag = true, value = { + FLAG_IS_REGEX + }) + @Retention(RetentionPolicy.SOURCE) + @interface Flags {} + + private final @NonNull LocusId mLocusId; + private final @Flags int mFlags; + + /** + * Default constructor. + * + * @param locusId id of the condition, as defined by + * {@link ContentCaptureContext#getLocusId()}. + * @param flags either {@link ContentCaptureCondition#FLAG_IS_REGEX} or {@code 0}. + */ + public ContentCaptureCondition(@NonNull LocusId locusId, @Flags int flags) { + this.mLocusId = Preconditions.checkNotNull(locusId); + this.mFlags = flags; + } + + /** + * Gets the {@code LocusId} per se. + */ + @NonNull + public LocusId getLocusId() { + return mLocusId; + } + + /** + * Gets the flags associates with this condition. + * + * @return either {@link ContentCaptureCondition#FLAG_IS_REGEX} or {@code 0}. + */ + public @Flags int getFlags() { + return mFlags; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + mFlags; + result = prime * result + ((mLocusId == null) ? 0 : mLocusId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + final ContentCaptureCondition other = (ContentCaptureCondition) obj; + if (mFlags != other.mFlags) return false; + if (mLocusId == null) { + if (other.mLocusId != null) return false; + } else { + if (!mLocusId.equals(other.mLocusId)) return false; + } + return true; + } + + @Override + public String toString() { + final StringBuilder string = new StringBuilder(mLocusId.toString()); // LocusID is PII safe + if (mFlags != 0) { + string + .append(" (") + .append(DebugUtils.flagsToString(ContentCaptureCondition.class, "FLAG_", mFlags)) + .append(')'); + } + return string.toString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel parcel, int flags) { + parcel.writeParcelable(mLocusId, flags); + parcel.writeInt(mFlags); + } + + public static final @NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + @Override + public ContentCaptureCondition createFromParcel(@NonNull Parcel parcel) { + return new ContentCaptureCondition(parcel.readParcelable(null), + parcel.readInt()); + } + + @Override + public ContentCaptureCondition[] newArray(int size) { + return new ContentCaptureCondition[size]; + } + }; +} diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java index 5a27e94aa49566870bf3789dc5b627df6560b84c..94e548fa0eebc84068ae164aaef482a040f72d01 100644 --- a/core/java/android/view/contentcapture/ContentCaptureContext.java +++ b/core/java/android/view/contentcapture/ContentCaptureContext.java @@ -15,6 +15,8 @@ */ package android.view.contentcapture; +import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -35,9 +37,9 @@ import com.android.internal.util.Preconditions; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; - /** - * Context associated with a {@link ContentCaptureSession}. + * Context associated with a {@link ContentCaptureSession} - see {@link ContentCaptureManager} for + * more info. */ public final class ContentCaptureContext implements Parcelable { @@ -50,8 +52,7 @@ public final class ContentCaptureContext implements Parcelable { /** * Flag used to indicate that the app explicitly disabled content capture for the activity - * (using - * {@link android.view.contentcapture.ContentCaptureManager#setContentCaptureEnabled(boolean)}), + * (using {@link ContentCaptureManager#setContentCaptureEnabled(boolean)}), * in which case the service will just receive activity-level events. * * @hide @@ -107,7 +108,7 @@ public final class ContentCaptureContext implements Parcelable { private final int mDisplayId; // Fields below are set by the service upon "delivery" and are not marshalled in the parcel - private @Nullable String mParentSessionId; + private int mParentSessionId = NO_SESSION_ID; /** @hide */ public ContentCaptureContext(@Nullable ContentCaptureContext clientContext, @@ -198,11 +199,12 @@ public final class ContentCaptureContext implements Parcelable { @SystemApi @TestApi public @Nullable ContentCaptureSessionId getParentSessionId() { - return mParentSessionId == null ? null : new ContentCaptureSessionId(mParentSessionId); + return mParentSessionId == NO_SESSION_ID ? null + : new ContentCaptureSessionId(mParentSessionId); } /** @hide */ - public void setParentSessionId(@NonNull String parentSessionId) { + public void setParentSessionId(int parentSessionId) { mParentSessionId = parentSessionId; } @@ -260,9 +262,10 @@ public final class ContentCaptureContext implements Parcelable { *

    • A unique identifier of the application state (for example, a conversation between * 2 users in a chat app). * + *

      See {@link ContentCaptureManager} for more info about the content capture context. + * * @param id id associated with this context. */ - // TODO(b/123577059): make sure this is well documented and understandable public Builder(@NonNull LocusId id) { mId = Preconditions.checkNotNull(id); } @@ -316,7 +319,7 @@ public final class ContentCaptureContext implements Parcelable { } pw.print(", taskId="); pw.print(mTaskId); pw.print(", displayId="); pw.print(mDisplayId); - if (mParentSessionId != null) { + if (mParentSessionId != NO_SESSION_ID) { pw.print(", parentId="); pw.print(mParentSessionId); } if (mFlags > 0) { @@ -348,7 +351,7 @@ public final class ContentCaptureContext implements Parcelable { builder.append(", hasExtras"); } } - if (mParentSessionId != null) { + if (mParentSessionId != NO_SESSION_ID) { builder.append(", parentId=").append(mParentSessionId); } return builder.append(']').toString(); diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java index 8188e0592b9db892d688108e759e536cb43fe697..bd386292f95dc586eaf73787c8c5257eaf68a087 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -16,6 +16,7 @@ package android.view.contentcapture; import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString; +import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID; import android.annotation.IntDef; import android.annotation.NonNull; @@ -126,25 +127,25 @@ public final class ContentCaptureEvent implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface EventType{} - private final @NonNull String mSessionId; + private final int mSessionId; private final int mType; private final long mEventTime; private @Nullable AutofillId mId; private @Nullable ArrayList mIds; private @Nullable ViewNode mNode; private @Nullable CharSequence mText; - private @Nullable String mParentSessionId; + private int mParentSessionId = NO_SESSION_ID; private @Nullable ContentCaptureContext mClientContext; /** @hide */ - public ContentCaptureEvent(@NonNull String sessionId, int type, long eventTime) { + public ContentCaptureEvent(int sessionId, int type, long eventTime) { mSessionId = sessionId; mType = type; mEventTime = eventTime; } /** @hide */ - public ContentCaptureEvent(@NonNull String sessionId, int type) { + public ContentCaptureEvent(int sessionId, int type) { this(sessionId, type, System.currentTimeMillis()); } @@ -185,7 +186,7 @@ public final class ContentCaptureEvent implements Parcelable { * * @hide */ - public ContentCaptureEvent setParentSessionId(@NonNull String parentSessionId) { + public ContentCaptureEvent setParentSessionId(int parentSessionId) { mParentSessionId = parentSessionId; return this; } @@ -202,7 +203,7 @@ public final class ContentCaptureEvent implements Parcelable { /** @hide */ @NonNull - public String getSessionId() { + public int getSessionId() { return mSessionId; } @@ -212,7 +213,7 @@ public final class ContentCaptureEvent implements Parcelable { * @hide */ @Nullable - public String getParentSessionId() { + public int getParentSessionId() { return mParentSessionId; } @@ -357,10 +358,10 @@ public final class ContentCaptureEvent implements Parcelable { if (mNode != null) { pw.print(", mNode.id="); pw.print(mNode.getAutofillId()); } - if (mSessionId != null) { + if (mSessionId != NO_SESSION_ID) { pw.print(", sessionId="); pw.print(mSessionId); } - if (mParentSessionId != null) { + if (mParentSessionId != NO_SESSION_ID) { pw.print(", parentSessionId="); pw.print(mParentSessionId); } if (mText != null) { @@ -377,7 +378,7 @@ public final class ContentCaptureEvent implements Parcelable { final StringBuilder string = new StringBuilder("ContentCaptureEvent[type=") .append(getTypeAsString(mType)); string.append(", session=").append(mSessionId); - if (mType == TYPE_SESSION_STARTED && mParentSessionId != null) { + if (mType == TYPE_SESSION_STARTED && mParentSessionId != NO_SESSION_ID) { string.append(", parent=").append(mParentSessionId); } if (mId != null) { @@ -409,7 +410,7 @@ public final class ContentCaptureEvent implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { - parcel.writeString(mSessionId); + parcel.writeInt(mSessionId); parcel.writeInt(mType); parcel.writeLong(mEventTime); parcel.writeParcelable(mId, flags); @@ -417,7 +418,7 @@ public final class ContentCaptureEvent implements Parcelable { ViewNode.writeToParcel(parcel, mNode, flags); parcel.writeCharSequence(mText); if (mType == TYPE_SESSION_STARTED || mType == TYPE_SESSION_FINISHED) { - parcel.writeString(mParentSessionId); + parcel.writeInt(mParentSessionId); } if (mType == TYPE_SESSION_STARTED || mType == TYPE_CONTEXT_UPDATED) { parcel.writeParcelable(mClientContext, flags); @@ -430,7 +431,7 @@ public final class ContentCaptureEvent implements Parcelable { @Override @NonNull public ContentCaptureEvent createFromParcel(Parcel parcel) { - final String sessionId = parcel.readString(); + final int sessionId = parcel.readInt(); final int type = parcel.readInt(); final long eventTime = parcel.readLong(); final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, type, eventTime); @@ -448,7 +449,7 @@ public final class ContentCaptureEvent implements Parcelable { } event.setText(parcel.readCharSequence()); if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) { - event.setParentSessionId(parcel.readString()); + event.setParentSessionId(parcel.readInt()); } if (type == TYPE_SESSION_STARTED || type == TYPE_CONTEXT_UPDATED) { event.setClientContext(parcel.readParcelable(null)); diff --git a/core/java/android/view/contentcapture/ContentCaptureHelper.java b/core/java/android/view/contentcapture/ContentCaptureHelper.java index 6bc38290745775f3e0897f941b8c62a768471e1c..c7ca2209d3874094572b4d45d68841315a9b9783 100644 --- a/core/java/android/view/contentcapture/ContentCaptureHelper.java +++ b/core/java/android/view/contentcapture/ContentCaptureHelper.java @@ -23,9 +23,14 @@ import static android.view.contentcapture.ContentCaptureManager.LOGGING_LEVEL_VE import android.annotation.Nullable; import android.os.Build; import android.provider.DeviceConfig; +import android.util.ArraySet; import android.util.Log; import android.view.contentcapture.ContentCaptureManager.LoggingLevel; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + /** * Helper class for this package and server's. * @@ -101,6 +106,22 @@ public final class ContentCaptureHelper { } } + /** + * Converts a set to a list. + */ + @Nullable + public static ArrayList toList(@Nullable Set set) { + return set == null ? null : new ArrayList(set); + } + + /** + * Converts a list to a set. + */ + @Nullable + public static ArraySet toSet(@Nullable List list) { + return list == null ? null : new ArraySet(list); + } + private ContentCaptureHelper() { throw new UnsupportedOperationException("contains only static methods"); } diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 9e546a80dfd346194fa3b076334587f7fda5d9fc..253935680cb8ab3dc99c9592eeacbca37675d13e 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -17,6 +17,7 @@ package android.view.contentcapture; import static android.view.contentcapture.ContentCaptureHelper.sDebug; import static android.view.contentcapture.ContentCaptureHelper.sVerbose; +import static android.view.contentcapture.ContentCaptureHelper.toSet; import android.annotation.IntDef; import android.annotation.NonNull; @@ -28,12 +29,15 @@ import android.annotation.UiThread; import android.content.ComponentName; import android.content.ContentCaptureOptions; import android.content.Context; +import android.graphics.Canvas; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; +import android.view.View; +import android.view.ViewStructure; import android.view.contentcapture.ContentCaptureSession.FlushReason; import com.android.internal.annotations.GuardedBy; @@ -43,9 +47,152 @@ import com.android.internal.util.SyncResultReceiver; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Set; /** - * TODO(b/123577059): add javadocs / mention it can be null + *

      The {@link ContentCaptureManager} provides additional ways for for apps to + * integrate with the content capture subsystem. + * + *

      Content capture provides real-time, continuous capture of application activity, display and + * events to an intelligence service that is provided by the Android system. The intelligence + * service then uses that info to mediate and speed user journey through different apps. For + * example, when the user receives a restaurant address in a chat app and switchs to a map app + * to search for that restaurant, the intelligence service could offer an autofill dialog to + * let the user automatically select its address. + * + *

      Content capture was designed with two major concerns in mind: privacy and performance. + * + *

        + *
      • Privacy: the intelligence service is a trusted component provided that is provided + * by the device manufacturer and that cannot be changed by the user (although the user can + * globaly disable content capture using the Android Settings app). This service can only use the + * data for in-device machine learning, which is enforced both by process isolation and + * CDD requirements. + *
      • Performance: content capture is highly optimized to minimize its impact in the app + * jankiness and overall device system health. For example, its only enabled on apps (or even + * specific activities from an app) that were explicitly whitelisted by the intelligence service, + * and it buffers the events so they are sent in a batch to the service (see + * {@link #isContentCaptureEnabled()} for other cases when its disabled). + *
      + * + *

      In fact, before using this manager, the app developer should check if it's available. Example: + * + * ContentCaptureManager mgr = context.getSystemService(ContentCaptureManager.class); + * if (mgr != null && mgr.isContentCaptureEnabled()) { + * // ... + * } + * + * + *

      App developers usually don't need to explicitly interact with content capture, except when the + * app: + * + *

        + *
      • Can define a contextual {@link android.content.LocusId} to identify unique state (such as a + * conversation between 2 chat users). + *
      • Can have multiple view hierarchies with different contextual meaning (for example, a + * browser app with multiple tabs, each representing a different URL). + *
      • Contains custom views (that extend View directly and are not provided by the standard + * Android SDK. + *
      • Contains views that provide their own virtual hierarchy (like a web browser that render the + * HTML elements using a Canvas). + *
      + * + *

      The main integration point with content capture is the {@link ContentCaptureSession}. A "main" + * session is automatically created by the Android System when content capture is enabled for the + * activity and its used by the standard Android views to notify the content capture service of + * events such as views being added, views been removed, and text changed by user input. The session + * could have a {@link ContentCaptureContext} to provide more contextual info about it, such as + * the locus associated with the view hierarchy (see {@link android.content.LocusId} for more info + * about locus). By default, the main session doesn't have a {@code ContentCaptureContext}, but you + * can change it after its created. Example: + * + *

      
      + * protected void onCreate(Bundle savedInstanceState) {
      + *   // Initialize view structure
      + *   ContentCaptureSession session = rootView.getContentCaptureSession();
      + *   if (session != null) {
      + *     session.setContentCaptureContext(ContentCaptureContext.forLocusId("chat_UserA_UserB"));
      + *   }
      + * }
      + * 
      + * + *

      If your activity contains view hierarchies with a different contextual meaning, you should + * created child sessions for each view hierarchy root. For example, if your activity is a browser, + * you could use the main session for the main URL being rendered, then child sessions for each + * {@code IFRAME}: + * + *

      
      + * ContentCaptureSession mMainSession;
      + *
      + * protected void onCreate(Bundle savedInstanceState) {
      + *    // Initialize view structure...
      + *    mMainSession = rootView.getContentCaptureSession();
      + *    if (mMainSession != null) {
      + *      mMainSession.setContentCaptureContext(
      + *          ContentCaptureContext.forLocusId("https://example.com"));
      + *    }
      + * }
      + *
      + * private void loadIFrame(View iframeRootView, String url) {
      + *   if (mMainSession != null) {
      + *      ContentCaptureSession iFrameSession = mMainSession.newChild(
      + *          ContentCaptureContext.forLocusId(url));
      + *      }
      + *      iframeRootView.setContentCaptureSession(iFrameSession);
      + *   }
      + *   // Load iframe...
      + * }
      + * 
      + * + *

      If your activity has custom views (i.e., views that extend {@link View} directly and provide + * just one logical view, not a virtual tree hiearchy) and it provides content that's relevant for + * content capture (as of {@link android.os.Build.VERSION_CODES#Q Android Q}, the only relevant + * content is text), then your view implementation should: + * + *

        + *
      • Set it as important for content capture. + *
      • Fill {@link ViewStructure} used for content capture. + *
      • Notify the {@link ContentCaptureSession} when the text is changed by user input. + *
      + * + *

      Here's an example of the relevant methods for an {@code EditText}-like view: + * + *

      
      + * public class MyEditText extends View {
      + *
      + * public MyEditText(...) {
      + *   if (getImportantForContentCapture() == IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) {
      + *     setImportantForContentCapture(IMPORTANT_FOR_CONTENT_CAPTURE_YES);
      + *   }
      + * }
      + *
      + * public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
      + *   super.onProvideContentCaptureStructure(structure, flags);
      + *
      + *   structure.setText(getText(), getSelectionStart(), getSelectionEnd());
      + *   structure.setHint(getHint());
      + *   structure.setInputType(getInputType());
      + *   // set other properties like setTextIdEntry(), setTextLines(), setTextStyle(),
      + *   // setMinTextEms(), setMaxTextEms(), setMaxTextLength()
      + * }
      + *
      + * private void onTextChanged() {
      + *   if (isLaidOut() && isImportantForContentCapture() && isTextEditable()) {
      + *     ContentCaptureManager mgr = mContext.getSystemService(ContentCaptureManager.class);
      + *     if (cm != null && cm.isContentCaptureEnabled()) {
      + *        ContentCaptureSession session = getContentCaptureSession();
      + *        if (session != null) {
      + *          session.notifyViewTextChanged(getAutofillId(), getText());
      + *        }
      + *   }
      + * }
      + * 
      + * + *

      If your view provides its own virtual hierarchy (for example, if it's a browser that draws + * the HTML using {@link Canvas} or native libraries in a different render process), then the view + * is also responsible to notify the session when the virtual elements appear and disappear - see + * {@link View#onProvideContentCaptureStructure(ViewStructure, int)} for more info. */ @SystemService(Context.CONTENT_CAPTURE_MANAGER_SERVICE) public final class ContentCaptureManager { @@ -68,7 +215,7 @@ public final class ContentCaptureManager { /** * DeviceConfig property used by {@code com.android.server.SystemServer} on start to decide - * whether the Content Capture service should be created or not + * whether the content capture service should be created or not * *

      By default it should *NOT* be set (or set to {@code "default"}, so the decision is based * on whether the OEM provides an implementation for the service), but it can be overridden to: @@ -339,12 +486,12 @@ public final class ContentCaptureManager { *

      There are many reasons it could be disabled, such as: *

        *
      • App itself disabled content capture through {@link #setContentCaptureEnabled(boolean)}. - *
      • Service disabled content capture for this specific activity. - *
      • Service disabled content capture for all activities of this package. - *
      • Service disabled content capture globally. - *
      • User disabled content capture globally (through Settings). - *
      • OEM disabled content capture globally. - *
      • Transient errors. + *
      • Intelligence service did not whitelist content capture for this activity's package. + *
      • Intelligence service did not whitelist content capture for this specific activity. + *
      • Intelligence service disabled content capture globally. + *
      • User disabled content capture globally through the Android Settings app. + *
      • Device manufacturer (OEM) disabled content capture globally. + *
      • Transient errors, such as intelligence service package being updated. *
      */ public boolean isContentCaptureEnabled() { @@ -361,6 +508,31 @@ public final class ContentCaptureManager { return true; } + /** + * Gets the list of conditions for when content capture should be allowed. + * + *

      This method is typically used by web browsers so they don't generate unnecessary content + * capture events for websites the content capture service is not interested on. + * + * @return list of conditions, or {@code null} if the service didn't set any restriction + * (in which case content capture events should always be generated). If the list is empty, + * then it should not generate any event at all. + */ + @Nullable + public Set getContentCaptureConditions() { + // NOTE: we could cache the conditions on ContentCaptureOptions, but then it would be stick + // to the lifetime of the app. OTOH, by dynamically calling the server every time, we allow + // the service to fine tune how long-lived apps (like browsers) are whitelisted. + if (!isContentCaptureEnabled() && !mOptions.lite) return null; + + final SyncResultReceiver resultReceiver = syncRun( + (r) -> mService.getContentCaptureConditions(mContext.getPackageName(), r)); + + final ArrayList result = resultReceiver + .getParcelableListResult(); + return toSet(result); + } + /** * Called by apps to explicitly enable or disable content capture. * @@ -378,12 +550,12 @@ public final class ContentCaptureManager { } /** - * Gets whether Content Capture is enabled for the given user. + * Gets whether content capture is enabled for the given user. * - *

      This method is typically used by the Content Capture Service settings page, so it can + *

      This method is typically used by the content capture service settings page, so it can * provide a toggle to enable / disable it. * - * @throws SecurityException if caller is not the app that owns the Content Capture service + * @throws SecurityException if caller is not the app that owns the content capture service * associated with the user. * * @hide @@ -391,21 +563,14 @@ public final class ContentCaptureManager { @SystemApi @TestApi public boolean isContentCaptureFeatureEnabled() { - final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); - final int resultCode; - try { - mService.isContentCaptureFeatureEnabled(resultReceiver); - resultCode = resultReceiver.getIntResult(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + final SyncResultReceiver resultReceiver = syncRun( + (r) -> mService.isContentCaptureFeatureEnabled(r)); + final int resultCode = resultReceiver.getIntResult(); switch (resultCode) { case RESULT_CODE_TRUE: return true; case RESULT_CODE_FALSE: return false; - case RESULT_CODE_SECURITY_EXCEPTION: - throw new SecurityException("caller is not user's ContentCapture service"); default: Log.wtf(TAG, "received invalid result: " + resultCode); return false; @@ -413,7 +578,7 @@ public final class ContentCaptureManager { } /** - * Called by the app to request the Content Capture service to remove user-data associated with + * Called by the app to request the content capture service to remove user-data associated with * some context. * * @param request object specifying what user data should be removed. @@ -428,6 +593,26 @@ public final class ContentCaptureManager { } } + /** + * Runs a sync method in the service, properly handling exceptions. + * + * @throws SecurityException if caller is not allowed to execute the method. + */ + @NonNull + private SyncResultReceiver syncRun(@NonNull MyRunnable r) { + final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); + try { + r.run(resultReceiver); + final int resultCode = resultReceiver.getIntResult(); + if (resultCode == RESULT_CODE_SECURITY_EXCEPTION) { + throw new SecurityException(resultReceiver.getStringResult()); + } + return resultReceiver; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** @hide */ public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.println("ContentCaptureManager"); @@ -451,4 +636,8 @@ public final class ContentCaptureManager { } } } + + private interface MyRunnable { + void run(@NonNull SyncResultReceiver receiver) throws RemoteException; + } } diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index ed1ca2a48850207e9221b818d90ef9d2268ed1e6..7761038f8af14f11bd921b9566dd0753814b5deb 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -38,7 +38,7 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.UUID; +import java.util.Random; /** * Session used to notify a system-provided Content Capture service about events associated with @@ -48,6 +48,11 @@ public abstract class ContentCaptureSession implements AutoCloseable { private static final String TAG = ContentCaptureSession.class.getSimpleName(); + private static final Random sIdGenerator = new Random(); + + /** @hide */ + public static final int NO_SESSION_ID = 0; + /** * Initial state, when there is no session. * @@ -186,7 +191,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { /** @hide */ @Nullable - protected final String mId; + protected final int mId; private int mState = UNKNOWN_STATE; @@ -210,13 +215,14 @@ public abstract class ContentCaptureSession implements AutoCloseable { /** @hide */ protected ContentCaptureSession() { - this(UUID.randomUUID().toString()); + this(getRandomSessionId()); } /** @hide */ @VisibleForTesting - public ContentCaptureSession(@NonNull String id) { - mId = Preconditions.checkNotNull(id); + public ContentCaptureSession(int id) { + Preconditions.checkArgument(id != NO_SESSION_ID); + mId = id; } // Used by ChildCOntentCaptureSession @@ -240,16 +246,9 @@ public abstract class ContentCaptureSession implements AutoCloseable { return mContentCaptureSessionId; } - /** @hide */ - @VisibleForTesting - public int getIdAsInt() { - // TODO(b/121197119): use sessionId instead of hashcode once it's changed to int - return mId.hashCode(); - } - /** @hide */ @NonNull - public String getId() { + public int getId() { return mId; } @@ -415,7 +414,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { // TODO(b/123036895): use a internalNotifyViewsDisappeared that optimizes how the event is // parcelized for (long id : virtualIds) { - internalNotifyViewDisappeared(new AutofillId(hostId, id, getIdAsInt())); + internalNotifyViewDisappeared(new AutofillId(hostId, id, mId)); } } @@ -464,7 +463,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { public @NonNull AutofillId newAutofillId(@NonNull AutofillId hostId, long virtualChildId) { Preconditions.checkNotNull(hostId); Preconditions.checkArgument(hostId.isNonVirtual(), "hostId cannot be virtual: %s", hostId); - return new AutofillId(hostId, virtualChildId, getIdAsInt()); + return new AutofillId(hostId, virtualChildId, mId); } /** @@ -480,7 +479,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { @NonNull public final ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId, long virtualId) { - return new ViewNode.ViewStructureImpl(parentId, virtualId, getIdAsInt()); + return new ViewNode.ViewStructureImpl(parentId, virtualId, mId); } boolean isContentCaptureEnabled() { @@ -511,7 +510,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { @Override public String toString() { - return mId; + return Integer.toString(mId); } /** @hide */ @@ -541,4 +540,12 @@ public abstract class ContentCaptureSession implements AutoCloseable { return "UNKOWN-" + reason; } } + + private static int getRandomSessionId() { + int id; + do { + id = sIdGenerator.nextInt(); + } while (id == NO_SESSION_ID); + return id; + } } diff --git a/core/java/android/view/contentcapture/ContentCaptureSessionId.java b/core/java/android/view/contentcapture/ContentCaptureSessionId.java index e7c350a12b928110912c855f6b42a3a91bec3cbf..2d350b27c4a32f50fc723693bf256da52f43fd44 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSessionId.java +++ b/core/java/android/view/contentcapture/ContentCaptureSessionId.java @@ -27,7 +27,7 @@ import java.io.PrintWriter; */ public final class ContentCaptureSessionId implements Parcelable { - private final @NonNull String mValue; + private final @NonNull int mValue; /** * Creates a new instance. @@ -36,14 +36,14 @@ public final class ContentCaptureSessionId implements Parcelable { * * @hide */ - public ContentCaptureSessionId(@NonNull String value) { + public ContentCaptureSessionId(@NonNull int value) { mValue = value; } /** * @hide */ - public String getValue() { + public int getValue() { return mValue; } @@ -51,7 +51,7 @@ public final class ContentCaptureSessionId implements Parcelable { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((mValue == null) ? 0 : mValue.hashCode()); + result = prime * result + mValue; return result; } @@ -61,11 +61,7 @@ public final class ContentCaptureSessionId implements Parcelable { if (obj == null) return false; if (getClass() != obj.getClass()) return false; final ContentCaptureSessionId other = (ContentCaptureSessionId) obj; - if (mValue == null) { - if (other.mValue != null) return false; - } else if (!mValue.equals(other.mValue)) { - return false; - } + if (mValue != other.mValue) return false; return true; } @@ -77,9 +73,10 @@ public final class ContentCaptureSessionId implements Parcelable { */ @Override public String toString() { - return mValue; + return Integer.toString(mValue); } + /** @hide */ // TODO(b/111276913): dump to proto as well public void dump(PrintWriter pw) { @@ -93,16 +90,16 @@ public final class ContentCaptureSessionId implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { - parcel.writeString(mValue); + parcel.writeInt(mValue); } - public static final @android.annotation.NonNull Parcelable.Creator CREATOR = + public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override @NonNull public ContentCaptureSessionId createFromParcel(Parcel parcel) { - return new ContentCaptureSessionId(parcel.readString()); + return new ContentCaptureSessionId(parcel.readInt()); } @Override diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl index 15fbaa2d7dabf8f06d35827035068ca0c68440f4..7335073c59e010e690c96cdba71b52d5881f70c4 100644 --- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl +++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl @@ -42,13 +42,13 @@ oneway interface IContentCaptureManager { * {@link IContentCaptureContext#flags}). */ void startSession(IBinder activityToken, in ComponentName componentName, - String sessionId, int flags, in IResultReceiver result); + int sessionId, int flags, in IResultReceiver result); /** * Marks the end of a session for the calling user identified by * the corresponding {@code startSession}'s {@code sessionId}. */ - void finishSession(String sessionId); + void finishSession(int sessionId); /** * Returns the content capture service's component name (if enabled and @@ -72,4 +72,9 @@ oneway interface IContentCaptureManager { * Returns a ComponentName with the name of custom service activity, if defined. */ void getServiceSettingsActivity(in IResultReceiver result); + + /** + * Returns a list with the ContentCaptureConditions for the package (or null if not defined). + */ + void getContentCaptureConditions(String packageName, in IResultReceiver result); } diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index 790b8f9dde460b9b984651ff4efd66b72e62f8d9..784cf9c325575c5ff93a3068cd8adec86b6b8c89 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -318,7 +318,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { if (!mEvents.isEmpty() && eventType == TYPE_VIEW_DISAPPEARED) { final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1); if (lastEvent.getType() == TYPE_VIEW_DISAPPEARED - && event.getSessionId().equals(lastEvent.getSessionId())) { + && event.getSessionId() == lastEvent.getSessionId()) { if (sVerbose) { Log.v(TAG, "Buffering TYPE_VIEW_DISAPPEARED events for session " + lastEvent.getSessionId()); @@ -581,37 +581,35 @@ public final class MainContentCaptureSession extends ContentCaptureSession { // TODO(b/122454205): refactor "notifyXXXX" methods below to a common "Buffer" object that is // shared between ActivityContentCaptureSession and ChildContentCaptureSession objects. Such // change should also get get rid of the "internalNotifyXXXX" methods above - void notifyChildSessionStarted(@NonNull String parentSessionId, - @NonNull String childSessionId, @NonNull ContentCaptureContext clientContext) { + void notifyChildSessionStarted(int parentSessionId, int childSessionId, + @NonNull ContentCaptureContext clientContext) { sendEvent(new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED) .setParentSessionId(parentSessionId).setClientContext(clientContext), FORCE_FLUSH); } - void notifyChildSessionFinished(@NonNull String parentSessionId, - @NonNull String childSessionId) { + void notifyChildSessionFinished(int parentSessionId, int childSessionId) { sendEvent(new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED) .setParentSessionId(parentSessionId), FORCE_FLUSH); } - void notifyViewAppeared(@NonNull String sessionId, @NonNull ViewStructureImpl node) { + void notifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) { sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED) .setViewNode(node.mNode)); } /** Public because is also used by ViewRootImpl */ - public void notifyViewDisappeared(@NonNull String sessionId, @NonNull AutofillId id) { + public void notifyViewDisappeared(int sessionId, @NonNull AutofillId id) { sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id)); } - void notifyViewTextChanged(@NonNull String sessionId, @NonNull AutofillId id, - @Nullable CharSequence text) { + void notifyViewTextChanged(int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) { sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED).setAutofillId(id) .setText(text)); } /** Public because is also used by ViewRootImpl */ - public void notifyViewTreeEvent(@NonNull String sessionId, boolean started) { + public void notifyViewTreeEvent(int sessionId, boolean started) { final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED; sendEvent(new ContentCaptureEvent(sessionId, type), FORCE_FLUSH); } @@ -622,8 +620,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { sendEvent(new ContentCaptureEvent(mId, type), FORCE_FLUSH); } - void notifyContextUpdated(@NonNull String sessionId, - @Nullable ContentCaptureContext context) { + void notifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) { sendEvent(new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED) .setClientContext(context)); } diff --git a/core/java/android/view/inspector/PropertyReader.java b/core/java/android/view/inspector/PropertyReader.java index b5020ce6495f4d02172cf1f1893b92249295aeeb..5be0e3f3ccf2e582f580799cd0cd0887a2e230c5 100644 --- a/core/java/android/view/inspector/PropertyReader.java +++ b/core/java/android/view/inspector/PropertyReader.java @@ -133,10 +133,11 @@ public interface PropertyReader { void readColor(int id, @ColorInt int value); /** - * Read a color packed into a {@link ColorLong} as a property. + * Read a color packed into a {@code ColorLong} as a property. * * @param id Identifier of the property from a {@link PropertyMapper} - * @param value Value of the property + * @param value Value of the property packed as a {@code ColorLong}. See the + * {@link Color} class for details of the packing. * @throws PropertyTypeMismatchException If the property ID is not mapped as a color */ void readColor(int id, @ColorLong long value); diff --git a/core/java/android/webkit/IWebViewUpdateService.aidl b/core/java/android/webkit/IWebViewUpdateService.aidl index 10cfea166abd5867e84f188aed404b5053bd87fa..6e3887fa738151a8fffbb3bc9994030e4f987ecf 100644 --- a/core/java/android/webkit/IWebViewUpdateService.aidl +++ b/core/java/android/webkit/IWebViewUpdateService.aidl @@ -77,11 +77,6 @@ interface IWebViewUpdateService { @UnsupportedAppUsage boolean isFallbackPackage(String packageName); - /** - * Enable or disable the WebView package fallback mechanism. - */ - void enableFallbackLogic(boolean enable); - /** * Used by Settings to determine whether multiprocess is enabled. */ diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 4413585535f27878fa411157366ca7bcda1cc75d..678a25223ef5f9204e72fe675cf4a7dd4436f9e2 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -321,45 +321,6 @@ public final class WebViewFactory { } } - /** - * If the ApplicationInfo provided is for a stub WebView, fix up the object to include the - * required values from the donor package. If the ApplicationInfo is for a full WebView, - * leave it alone. Throws MissingWebViewPackageException if the donor is missing. - */ - private static void fixupStubApplicationInfo(ApplicationInfo ai, PackageManager pm) - throws MissingWebViewPackageException { - String donorPackageName = null; - if (ai.metaData != null) { - donorPackageName = ai.metaData.getString("com.android.webview.WebViewDonorPackage"); - } - if (donorPackageName != null) { - PackageInfo donorPackage; - try { - donorPackage = pm.getPackageInfo( - donorPackageName, - PackageManager.GET_SHARED_LIBRARY_FILES - | PackageManager.MATCH_DEBUG_TRIAGED_MISSING - | PackageManager.MATCH_UNINSTALLED_PACKAGES - | PackageManager.MATCH_FACTORY_ONLY); - } catch (PackageManager.NameNotFoundException e) { - throw new MissingWebViewPackageException("Failed to find donor package: " + - donorPackageName); - } - ApplicationInfo donorInfo = donorPackage.applicationInfo; - - // Replace the stub's code locations with the donor's. - ai.sourceDir = donorInfo.sourceDir; - ai.splitSourceDirs = donorInfo.splitSourceDirs; - ai.nativeLibraryDir = donorInfo.nativeLibraryDir; - ai.secondaryNativeLibraryDir = donorInfo.secondaryNativeLibraryDir; - - // Copy the donor's primary and secondary ABIs, since the stub doesn't have native code - // and so they are unset. - ai.primaryCpuAbi = donorInfo.primaryCpuAbi; - ai.secondaryCpuAbi = donorInfo.secondaryCpuAbi; - } - } - @UnsupportedAppUsage private static Context getWebViewContextAndSetProvider() throws MissingWebViewPackageException { Application initialApplication = AppGlobals.getInitialApplication(); @@ -411,7 +372,6 @@ public final class WebViewFactory { verifyPackageInfo(response.packageInfo, newPackageInfo); ApplicationInfo ai = newPackageInfo.applicationInfo; - fixupStubApplicationInfo(ai, pm); Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "initialApplication.createApplicationContext"); @@ -494,18 +454,14 @@ public final class WebViewFactory { */ public static int onWebViewProviderChanged(PackageInfo packageInfo) { int startedRelroProcesses = 0; - ApplicationInfo originalAppInfo = new ApplicationInfo(packageInfo.applicationInfo); try { - fixupStubApplicationInfo(packageInfo.applicationInfo, - AppGlobals.getInitialApplication().getPackageManager()); - startedRelroProcesses = WebViewLibraryLoader.prepareNativeLibraries(packageInfo); } catch (Throwable t) { // Log and discard errors at this stage as we must not crash the system server. Log.e(LOGTAG, "error preparing webview native library", t); } - WebViewZygote.onWebViewProviderChanged(packageInfo, originalAppInfo); + WebViewZygote.onWebViewProviderChanged(packageInfo); return startedRelroProcesses; } diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java index 09aa066549cb9e2ed01a92a59921a9ff17d00260..62f54b943e110132080b722a241d5687a479cd1a 100644 --- a/core/java/android/webkit/WebViewZygote.java +++ b/core/java/android/webkit/WebViewZygote.java @@ -16,8 +16,6 @@ package android.webkit; -import android.app.LoadedApk; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.os.AsyncTask; import android.os.Build; @@ -29,10 +27,6 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; -import java.io.File; -import java.util.ArrayList; -import java.util.List; - /** @hide */ public class WebViewZygote { private static final String LOGTAG = "WebViewZygote"; @@ -55,13 +49,6 @@ public class WebViewZygote { @GuardedBy("sLock") private static PackageInfo sPackage; - /** - * Original ApplicationInfo for the selected WebView package before stub fixup. This is set from - * #onWebViewProviderChanged(). - */ - @GuardedBy("sLock") - private static ApplicationInfo sPackageOriginalAppInfo; - /** * Flag for whether multi-process WebView is enabled. If this is {@code false}, the zygote * will not be started. @@ -110,11 +97,9 @@ public class WebViewZygote { } } - public static void onWebViewProviderChanged(PackageInfo packageInfo, - ApplicationInfo originalAppInfo) { + static void onWebViewProviderChanged(PackageInfo packageInfo) { synchronized (sLock) { sPackage = packageInfo; - sPackageOriginalAppInfo = originalAppInfo; // If multi-process is not enabled, then do not start the zygote service. if (!sMultiprocessEnabled) { @@ -165,34 +150,7 @@ public class WebViewZygote { Process.FIRST_ISOLATED_UID, Integer.MAX_VALUE); // TODO(b/123615476) deal with user-id ranges properly ZygoteProcess.waitForConnectionToZygote(sZygote.getPrimarySocketAddress()); - - if (sPackageOriginalAppInfo.sourceDir.equals(sPackage.applicationInfo.sourceDir)) { - // No stub WebView is involved here, so we can preload the package the "clean" way - // using the ApplicationInfo. - sZygote.preloadApp(sPackage.applicationInfo, abi); - } else { - // Legacy path to support the stub WebView. - // Reuse the logic from LoadedApk to determine the correct paths and pass them to - // the zygote as strings. - final List zipPaths = new ArrayList<>(10); - final List libPaths = new ArrayList<>(10); - LoadedApk.makePaths(null, false, sPackage.applicationInfo, zipPaths, libPaths); - final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths); - final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) : - TextUtils.join(File.pathSeparator, zipPaths); - - String libFileName = WebViewFactory.getWebViewLibrary(sPackage.applicationInfo); - - // Use the original ApplicationInfo to determine what the original classpath would - // have been to use as a cache key. - LoadedApk.makePaths(null, false, sPackageOriginalAppInfo, zipPaths, null); - final String cacheKey = (zipPaths.size() == 1) ? zipPaths.get(0) : - TextUtils.join(File.pathSeparator, zipPaths); - - Log.d(LOGTAG, "Preloading package " + zip + " " + librarySearchPath); - sZygote.preloadPackageForAbi(zip, librarySearchPath, libFileName, cacheKey, - Build.SUPPORTED_ABIS[0]); - } + sZygote.preloadApp(sPackage.applicationInfo, abi); } catch (Exception e) { Log.e(LOGTAG, "Error connecting to webview zygote", e); stopZygoteLocked(); diff --git a/core/java/android/widget/TEST_MAPPING b/core/java/android/widget/TEST_MAPPING index ee378ff722182a35fc1d21b6dd95ac90993d317f..99a6cdcbe60cceae7d501ea3c3122c86e907c7da 100644 --- a/core/java/android/widget/TEST_MAPPING +++ b/core/java/android/widget/TEST_MAPPING @@ -1,12 +1,7 @@ { - "presubmit": [ + "imports": [ { - "name": "CtsWidgetTestCases", - "options": [ - { - "instrumentation-arg": "size:=small" - } - ] + "path": "cts/tests/tests/widget" } ] } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index a5a1a80cca88c708eb74f928a0fcfe2855499f55..a961783dab7c1606145d779771288f659c91322e 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -10081,7 +10081,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Return true iff there is a selection inside this text view. + * Return true iff there is a selection of nonzero length inside this text view. */ public boolean hasSelection() { final int selectionStart = getSelectionStart(); diff --git a/core/java/com/android/internal/app/AbstractResolverComparator.java b/core/java/com/android/internal/app/AbstractResolverComparator.java index 3576b6bb42368ef7de5274193af15c82546550a1..e091aac04c601f6eaca26982b3578d7361cfb660 100644 --- a/core/java/com/android/internal/app/AbstractResolverComparator.java +++ b/core/java/com/android/internal/app/AbstractResolverComparator.java @@ -1,7 +1,15 @@ package com.android.internal.app; +import android.app.usage.UsageStatsManager; import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.UserHandle; +import android.util.Log; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; +import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -10,20 +18,102 @@ import java.util.List; */ abstract class AbstractResolverComparator implements Comparator { + private static final int NUM_OF_TOP_ANNOTATIONS_TO_USE = 3; + protected AfterCompute mAfterCompute; + protected final PackageManager mPm; + protected final UsageStatsManager mUsm; + protected String[] mAnnotations; + protected String mContentType; + + // True if the current share is a link. + private final boolean mHttp; + // can be null if mHttp == false or current user has no default browser package + private final String mDefaultBrowserPackageName; + + AbstractResolverComparator(Context context, Intent intent) { + String scheme = intent.getScheme(); + mHttp = "http".equals(scheme) || "https".equals(scheme); + mContentType = intent.getType(); + getContentAnnotations(intent); + + mPm = context.getPackageManager(); + mUsm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); + mDefaultBrowserPackageName = mHttp + ? mPm.getDefaultBrowserPackageNameAsUser(UserHandle.myUserId()) + : null; + } + + // get annotations of content from intent. + private void getContentAnnotations(Intent intent) { + ArrayList annotations = intent.getStringArrayListExtra( + Intent.EXTRA_CONTENT_ANNOTATIONS); + if (annotations != null) { + int size = annotations.size(); + if (size > NUM_OF_TOP_ANNOTATIONS_TO_USE) { + size = NUM_OF_TOP_ANNOTATIONS_TO_USE; + } + mAnnotations = new String[size]; + for (int i = 0; i < size; i++) { + mAnnotations[i] = annotations.get(i); + } + } + } /** * Callback to be called when {@link #compute(List)} finishes. This signals to stop waiting. */ - public interface AfterCompute { + interface AfterCompute { - public void afterCompute(); + void afterCompute(); } - public void setCallBack(AfterCompute afterCompute) { + void setCallBack(AfterCompute afterCompute) { mAfterCompute = afterCompute; } + @Override + public final int compare(ResolvedComponentInfo lhsp, ResolvedComponentInfo rhsp) { + final ResolveInfo lhs = lhsp.getResolveInfoAt(0); + final ResolveInfo rhs = rhsp.getResolveInfoAt(0); + + // We want to put the one targeted to another user at the end of the dialog. + if (lhs.targetUserId != UserHandle.USER_CURRENT) { + return rhs.targetUserId != UserHandle.USER_CURRENT ? 0 : 1; + } + if (rhs.targetUserId != UserHandle.USER_CURRENT) { + return -1; + } + + if (mHttp) { + // Special case: we want filters that match URI paths/schemes to be + // ordered before others. This is for the case when opening URIs, + // to make native apps go above browsers - except for 1 even more special case + // which is the default browser, as we want that to go above them all. + if (isDefaultBrowser(lhs)) { + return -1; + } + + if (isDefaultBrowser(rhs)) { + return 1; + } + final boolean lhsSpecific = ResolverActivity.isSpecificUriMatch(lhs.match); + final boolean rhsSpecific = ResolverActivity.isSpecificUriMatch(rhs.match); + if (lhsSpecific != rhsSpecific) { + return lhsSpecific ? -1 : 1; + } + } + return compare(lhs, rhs); + } + + /** + * Delegated to when used as a {@link Comparator} if there is not a + * special case. The {@link ResolveInfo ResolveInfos} are the first {@link ResolveInfo} in + * {@link ResolvedComponentInfo#getResolveInfoAt(int)} from the parameters of {@link + * #compare(ResolvedComponentInfo, ResolvedComponentInfo)} + */ + abstract int compare(ResolveInfo lhs, ResolveInfo rhs); + /** * Computes features for each target. This will be called before calls to {@link * #getScore(ComponentName)} or {@link #compare(Object, Object)}, in order to prepare the @@ -31,19 +121,22 @@ abstract class AbstractResolverComparator implements Comparator targets); + abstract void compute(List targets); /** * Returns the score that was calculated for the corresponding {@link ResolvedComponentInfo} * when {@link #compute(List)} was called before this. */ - public abstract float getScore(ComponentName name); + abstract float getScore(ComponentName name); /** * Reports to UsageStats what was chosen. */ - // TODO(b/129014961) Move implemetation here and make final. - public abstract void updateChooserCounts(String packageName, int userId, String action); + final void updateChooserCounts(String packageName, int userId, String action) { + if (mUsm != null) { + mUsm.reportChooserSelection(packageName, userId, mContentType, mAnnotations, action); + } + } /** * Updates the model used to rank the componentNames. @@ -53,11 +146,25 @@ abstract class AbstractResolverComparator implements Comparator 8 viewgroup - if (mResolverDrawerLayout != null && isSendAction(target)) { - mResolverDrawerLayout.setOnScrollChangeListener(this::handleScroll); + if (mResolverDrawerLayout != null) { + mResolverDrawerLayout.addOnLayoutChangeListener(this::handleLayoutChange); + + // expand/shrink direct share 4 -> 8 viewgroup + if (isSendAction(target)) { + mResolverDrawerLayout.setOnScrollChangeListener(this::handleScroll); + } } if (DEBUG) { @@ -823,6 +841,7 @@ public class ChooserActivity extends ResolverActivity { mRefinementResultReceiver = null; } unbindRemainingServices(); + mChooserHandler.removeMessages(LIST_VIEW_UPDATE_MESSAGE); mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_RESULT); if (USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) { @@ -878,18 +897,9 @@ public class ChooserActivity extends ResolverActivity { mChooserListAdapter.addServiceResults(null, Lists.newArrayList(mCallerChooserTargets)); } mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter); - mChooserRowAdapter.registerDataSetObserver(new OffsetDataSetObserver(adapterView)); if (listView != null) { listView.setItemsCanFocus(true); - listView.addOnLayoutChangeListener( - (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { - if (mChooserRowAdapter.calculateMaxTargetsPerRow(right - left)) { - adapterView.setAdapter(mChooserRowAdapter); - } - }); } - - adapterView.setAdapter(mChooserRowAdapter); } @Override @@ -1597,7 +1607,7 @@ public class ChooserActivity extends ResolverActivity { if (info == null) return null; // Now fetch app icon and raster with no badging even in work profile - Bitmap appIcon = (new ActivityInfoPresentationGetter(info)).getIconBitmap(); + Bitmap appIcon = makePresentationGetter(info).getIconBitmap(); // Raster target drawable with appIcon as a badge SimpleIconFactory sif = SimpleIconFactory.obtain(ChooserActivity.this); @@ -1728,6 +1738,66 @@ public class ChooserActivity extends ResolverActivity { } } + /* + * Need to dynamically adjust how many icons can fit per row before we add them, + * which also means setting the correct offset to initially show the content + * preview area + 2 rows of targets + */ + private void handleLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, + int oldTop, int oldRight, int oldBottom) { + if (mChooserRowAdapter == null || mAdapterView == null) { + return; + } + + if (mChooserRowAdapter.calculateMaxTargetsPerRow(right - left) + || mAdapterView.getAdapter() == null) { + mAdapterView.setAdapter(mChooserRowAdapter); + + getMainThreadHandler().post(() -> { + if (mResolverDrawerLayout == null || mChooserRowAdapter == null) { + return; + } + + int offset = 0; + int rowsToShow = mChooserRowAdapter.getContentPreviewRowCount() + + mChooserRowAdapter.getServiceTargetRowCount() + + mChooserRowAdapter.getCallerTargetRowCount(); + + // then this is most likely not a SEND_* action, so check + // the app target count + if (rowsToShow == 0) { + rowsToShow = mChooserRowAdapter.getCount(); + } + + // still zero? then use a default height and leave, which + // can happen when there are no targets to show + if (rowsToShow == 0) { + offset = getResources().getDimensionPixelSize( + R.dimen.chooser_max_collapsed_height); + mResolverDrawerLayout.setCollapsibleHeightReserved(offset); + return; + } + + int lastHeight = 0; + rowsToShow = Math.max(3, rowsToShow); + for (int i = 0; i < Math.min(rowsToShow, mAdapterView.getChildCount()); i++) { + lastHeight = mAdapterView.getChildAt(i).getHeight(); + offset += lastHeight; + } + + if (lastHeight != 0 && isSendAction(getTargetIntent())) { + // make sure to leave room for direct share 4->8 expansion + int expansionArea = + (int) (mResolverDrawerLayout.getUncollapsibleHeight() + / DIRECT_SHARE_EXPANSION_RATE); + offset = Math.min(offset, bottom - top - lastHeight - expansionArea); + } + + mResolverDrawerLayout.setCollapsibleHeightReserved(offset); + }); + } + } + public class ChooserListAdapter extends ResolveListAdapter { public static final int TARGET_BAD = -1; public static final int TARGET_CALLER = 0; @@ -1808,12 +1878,30 @@ public class ChooserActivity extends ResolverActivity { ri.noResourceId = true; ri.icon = 0; } + ResolveInfoPresentationGetter getter = makePresentationGetter(ri); mCallerTargets.add(new DisplayResolveInfo(ii, ri, - ri.loadLabel(pm), null, ii)); + getter.getLabel(), getter.getSubLabel(), ii)); } } } + @Override + public void notifyDataSetChanged() { + if (!mListViewDataChanged) { + mChooserHandler.sendEmptyMessageDelayed(LIST_VIEW_UPDATE_MESSAGE, + LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS); + mListViewDataChanged = true; + } + } + + private void refreshListView() { + if (mListViewDataChanged) { + super.notifyDataSetChanged(); + } + mListViewDataChanged = false; + } + + private void createPlaceHolders() { mServiceTargets.clear(); for (int i = 0; i < MAX_SERVICE_TARGETS; i++) { @@ -1821,12 +1909,6 @@ public class ChooserActivity extends ResolverActivity { } } - @Override - public boolean showsExtendedInfo(TargetInfo info) { - // We have badges so we don't need this text shown. - return false; - } - @Override public View onCreateView(ViewGroup parent) { return mInflater.inflate( @@ -1841,7 +1923,7 @@ public class ChooserActivity extends ResolverActivity { } if (mServiceTargets != null) { - if (getDisplayInfoCount() == 0) { + if (getDisplayResolveInfoCount() == 0) { // b/109676071: When packages change, onListRebuilt() is called before // ResolverActivity.mDisplayList is re-populated; pruning now would cause the // list to disappear briefly, so instead we detect this case (the @@ -1854,12 +1936,14 @@ public class ChooserActivity extends ResolverActivity { if (DEBUG) { Log.d(TAG, "querying direct share targets from ShortcutManager"); } + queryDirectShareTargets(this); } if (USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS) { if (DEBUG) { Log.d(TAG, "List built querying services"); } + queryTargetServices(this); } } @@ -1955,7 +2039,7 @@ public class ChooserActivity extends ResolverActivity { offset += callerTargetCount; return filtered ? super.getItem(position - offset) - : getDisplayInfoAt(position - offset); + : getDisplayResolveInfo(position - offset); } public void addServiceResults(DisplayResolveInfo origTarget, List targets) { @@ -2244,8 +2328,10 @@ public class ChooserActivity extends ResolverActivity { final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); int columnCount = holder.getColumnCount(); + final boolean isDirectShare = holder instanceof DirectShareViewHolder; + for (int i = 0; i < columnCount; i++) { - final View v = mChooserListAdapter.createView(holder.getRow(i)); + final View v = mChooserListAdapter.createView(holder.getRowByIndex(i)); final int column = i; v.setOnClickListener(new OnClickListener() { @Override @@ -2264,27 +2350,31 @@ public class ChooserActivity extends ResolverActivity { }); ViewGroup row = holder.addView(i, v); + // Force Direct Share to be 2 lines and auto-wrap to second line via hoz scroll = + // false. TextView#setHorizontallyScrolling must be reset after #setLines. Must be + // done before measuring. + if (isDirectShare) { + final ViewHolder vh = (ViewHolder) v.getTag(); + vh.text.setLines(2); + vh.text.setHorizontallyScrolling(false); + vh.text2.setVisibility(View.GONE); + } + // Force height to be a given so we don't have visual disruption during scaling. - LayoutParams lp = v.getLayoutParams(); v.measure(spec, spec); - if (lp == null) { - lp = new LayoutParams(LayoutParams.MATCH_PARENT, v.getMeasuredHeight()); - row.setLayoutParams(lp); - } else { - lp.height = v.getMeasuredHeight(); - } + setViewHeight(v, v.getMeasuredHeight()); } final ViewGroup viewGroup = holder.getViewGroup(); - // Pre-measure so we can scale later. + // Pre-measure and fix height so we can scale later. holder.measure(); - LayoutParams lp = viewGroup.getLayoutParams(); - if (lp == null) { - lp = new LayoutParams(LayoutParams.MATCH_PARENT, holder.getMeasuredRowHeight()); - viewGroup.setLayoutParams(lp); - } else { - lp.height = holder.getMeasuredRowHeight(); + setViewHeight(viewGroup, holder.getMeasuredRowHeight()); + + if (isDirectShare) { + DirectShareViewHolder dsvh = (DirectShareViewHolder) holder; + setViewHeight(dsvh.getRow(0), holder.getMeasuredRowHeight()); + setViewHeight(dsvh.getRow(1), holder.getMeasuredRowHeight()); } viewGroup.setTag(holder); @@ -2292,6 +2382,16 @@ public class ChooserActivity extends ResolverActivity { return holder; } + private void setViewHeight(View view, int heightPx) { + LayoutParams lp = view.getLayoutParams(); + if (lp == null) { + lp = new LayoutParams(LayoutParams.MATCH_PARENT, heightPx); + view.setLayoutParams(lp); + } else { + lp.height = heightPx; + } + } + RowViewHolder createViewHolder(int viewType, ViewGroup parent) { if (viewType == VIEW_TYPE_DIRECT_SHARE) { ViewGroup parentGroup = (ViewGroup) mLayoutInflater.inflate( @@ -2329,7 +2429,7 @@ public class ChooserActivity extends ResolverActivity { if (startType != lastStartType || rowPosition == getContentPreviewRowCount()) { row.setBackground(mChooserRowLayer); - setVertPadding(row, mChooserRowServiceSpacing, 0); + setVertPadding(row, 0, 0); } else { row.setBackground(null); setVertPadding(row, 0, 0); @@ -2426,7 +2526,9 @@ public class ChooserActivity extends ResolverActivity { abstract ViewGroup getViewGroup(); - abstract ViewGroup getRow(int index); + abstract ViewGroup getRowByIndex(int index); + + abstract ViewGroup getRow(int rowNumber); abstract void setViewVisibility(int i, int visibility); @@ -2475,10 +2577,15 @@ public class ChooserActivity extends ResolverActivity { return mRow; } - public ViewGroup getRow(int index) { + public ViewGroup getRowByIndex(int index) { return mRow; } + public ViewGroup getRow(int rowNumber) { + if (rowNumber == 0) return mRow; + return null; + } + public ViewGroup addView(int index, View v) { mRow.addView(v); mCells[index] = v; @@ -2517,7 +2624,7 @@ public class ChooserActivity extends ResolverActivity { } public ViewGroup addView(int index, View v) { - ViewGroup row = getRow(index); + ViewGroup row = getRowByIndex(index); row.addView(v); mCells[index] = v; @@ -2532,16 +2639,19 @@ public class ChooserActivity extends ResolverActivity { return mParent; } - public ViewGroup getRow(int index) { + public ViewGroup getRowByIndex(int index) { return mRows.get(index / mCellCountPerRow); } + public ViewGroup getRow(int rowNumber) { + return mRows.get(rowNumber); + } + public void measure() { final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); getRow(0).measure(spec, spec); getRow(1).measure(spec, spec); - // uses ChooserActiivty state variables to track height mDirectShareMinHeight = getRow(0).getMeasuredHeight(); mDirectShareCurrHeight = mDirectShareCurrHeight > 0 ? mDirectShareCurrHeight : mDirectShareMinHeight; @@ -2574,18 +2684,18 @@ public class ChooserActivity extends ResolverActivity { } public void handleScroll(AbsListView view, int y, int oldy, int maxTargetsPerRow) { - // only expand if we have more than 4 targets, and delay that decision until - // they start to scroll if (mHideDirectShareExpansion) { return; } + // only expand if we have more than maxTargetsPerRow, and delay that decision + // until they start to scroll if (mChooserListAdapter.getSelectableServiceTargetCount() <= maxTargetsPerRow) { mHideDirectShareExpansion = true; return; } - int yDiff = (int) ((oldy - y) * 0.7f); + int yDiff = (int) ((oldy - y) * DIRECT_SHARE_EXPANSION_RATE); int prevHeight = mDirectShareCurrHeight; mDirectShareCurrHeight = Math.min(mDirectShareCurrHeight + yDiff, @@ -2593,27 +2703,31 @@ public class ChooserActivity extends ResolverActivity { mDirectShareCurrHeight = Math.max(mDirectShareCurrHeight, mDirectShareMinHeight); yDiff = mDirectShareCurrHeight - prevHeight; - if (view == null || view.getChildCount() == 0) { + if (view == null || view.getChildCount() == 0 || yDiff == 0) { return; } - int index = mChooserRowAdapter.getContentPreviewRowCount(); - - ViewGroup expansionGroup = (ViewGroup) view.getChildAt(index); - int widthSpec = MeasureSpec.makeMeasureSpec(expansionGroup.getWidth(), - MeasureSpec.EXACTLY); - int heightSpec = MeasureSpec.makeMeasureSpec(mDirectShareCurrHeight, - MeasureSpec.EXACTLY); - expansionGroup.measure(widthSpec, heightSpec); - expansionGroup.getLayoutParams().height = expansionGroup.getMeasuredHeight(); - expansionGroup.layout(expansionGroup.getLeft(), expansionGroup.getTop(), - expansionGroup.getRight(), - expansionGroup.getTop() + expansionGroup.getMeasuredHeight()); + // locate the item to expand, and offset the rows below that one + boolean foundExpansion = false; + for (int i = 0; i < view.getChildCount(); i++) { + View child = view.getChildAt(i); - // reposition list items - int items = view.getChildCount(); - for (int i = index + 1; i < items; i++) { - view.getChildAt(i).offsetTopAndBottom(yDiff); + if (foundExpansion) { + child.offsetTopAndBottom(yDiff); + } else { + if (child.getTag() != null && child.getTag() instanceof DirectShareViewHolder) { + int widthSpec = MeasureSpec.makeMeasureSpec(child.getWidth(), + MeasureSpec.EXACTLY); + int heightSpec = MeasureSpec.makeMeasureSpec(mDirectShareCurrHeight, + MeasureSpec.EXACTLY); + child.measure(widthSpec, heightSpec); + child.getLayoutParams().height = child.getMeasuredHeight(); + child.layout(child.getLeft(), child.getTop(), child.getRight(), + child.getTop() + child.getMeasuredHeight()); + + foundExpansion = true; + } + } } } } @@ -2771,47 +2885,6 @@ public class ChooserActivity extends ResolverActivity { } } - class OffsetDataSetObserver extends DataSetObserver { - private final AbsListView mListView; - private int mCachedViewType = -1; - private View mCachedView; - - public OffsetDataSetObserver(AbsListView listView) { - mListView = listView; - } - - @Override - public void onChanged() { - if (mResolverDrawerLayout == null) { - return; - } - - final int chooserTargetRows = mChooserRowAdapter.getServiceTargetRowCount(); - int offset = 0; - for (int i = 0; i < chooserTargetRows; i++) { - final int pos = mChooserRowAdapter.getContentPreviewRowCount() + i; - final int vt = mChooserRowAdapter.getItemViewType(pos); - if (vt != mCachedViewType) { - mCachedView = null; - } - final View v = mChooserRowAdapter.getView(pos, mCachedView, mListView); - int height = ((RowViewHolder) (v.getTag())).getMeasuredRowHeight(); - - offset += (int) (height); - - if (vt >= 0) { - mCachedViewType = vt; - mCachedView = v; - } else { - mCachedViewType = -1; - } - } - - mResolverDrawerLayout.setCollapsibleHeightReserved(offset); - } - } - - /** * Used internally to round image corners while obeying view padding. */ diff --git a/core/java/com/android/internal/app/DumpHeapActivity.java b/core/java/com/android/internal/app/DumpHeapActivity.java index 0ce501ec6daaae7adae4b8fddfe69c8a3c48ea6d..e04e870a8ec5a306739d9a14636cfc48fc5c1a4e 100644 --- a/core/java/com/android/internal/app/DumpHeapActivity.java +++ b/core/java/com/android/internal/app/DumpHeapActivity.java @@ -37,6 +37,10 @@ public class DumpHeapActivity extends Activity { public static final String KEY_PROCESS = "process"; /** The size limit the process reached */ public static final String KEY_SIZE = "size"; + /** Whether the user initiated the dump or not. */ + public static final String KEY_IS_USER_INITIATED = "is_user_initiated"; + /** Whether the process is a system process (eg: Android System) or not. */ + public static final String KEY_IS_SYSTEM_PROCESS = "is_system_process"; /** Optional name of package to directly launch */ public static final String KEY_DIRECT_LAUNCH = "direct_launch"; @@ -59,6 +63,8 @@ public class DumpHeapActivity extends Activity { mProcess = getIntent().getStringExtra(KEY_PROCESS); mSize = getIntent().getLongExtra(KEY_SIZE, 0); + final boolean isUserInitiated = getIntent().getBooleanExtra(KEY_IS_USER_INITIATED, false); + final boolean isSystemProcess = getIntent().getBooleanExtra(KEY_IS_SYSTEM_PROCESS, false); String directLaunch = getIntent().getStringExtra(KEY_DIRECT_LAUNCH); if (directLaunch != null) { @@ -81,11 +87,19 @@ public class DumpHeapActivity extends Activity { } } + final int messageId; + if (isUserInitiated) { + messageId = com.android.internal.R.string.dump_heap_ready_text; + } else if (isSystemProcess) { + messageId = com.android.internal.R.string.dump_heap_system_text; + } else { + messageId = com.android.internal.R.string.dump_heap_text; + } AlertDialog.Builder b = new AlertDialog.Builder(this, android.R.style.Theme_Material_Light_Dialog_Alert); b.setTitle(com.android.internal.R.string.dump_heap_title); - b.setMessage(getString(com.android.internal.R.string.dump_heap_text, - mProcess, DebugUtils.sizeValueToString(mSize, null))); + b.setMessage(getString( + messageId, mProcess, DebugUtils.sizeValueToString(mSize, null))); b.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 84a1bed36e553017ff6ff94de1147aa377cb9335..f47469a0e1b359d1fa90e63ccbf1989221997683 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -83,7 +83,6 @@ import com.android.internal.widget.ResolverDrawerLayout; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Objects; @@ -499,33 +498,40 @@ public class ResolverActivity extends Activity { /** - * Loads the icon for the provided ApplicationInfo. Defaults to using the application icon over - * any IntentFilter or Activity icon to increase user understanding, with an exception for - * applications that hold the right permission. Always attempts to use icon resources over - * PackageManager loading mechanisms so badging can be done by iconloader. + * Loads the icon and label for the provided ApplicationInfo. Defaults to using the application + * icon and label over any IntentFilter or Activity icon to increase user understanding, with an + * exception for applications that hold the right permission. Always attempts to use available + * resources over PackageManager loading mechanisms so badging can be done by iconloader. Uses + * Strings to strip creative formatting. */ - private abstract class TargetPresentationGetter { - @Nullable abstract Drawable getIconSubstitute(); - @Nullable abstract String getAppSubLabel(); + private abstract static class TargetPresentationGetter { + @Nullable abstract Drawable getIconSubstituteInternal(); + @Nullable abstract String getAppSubLabelInternal(); + private Context mCtx; + protected PackageManager mPm; private final ApplicationInfo mAi; + private final int mIconDpi; private final boolean mHasSubstitutePermission; - TargetPresentationGetter(ApplicationInfo ai) { + TargetPresentationGetter(Context ctx, int iconDpi, ApplicationInfo ai) { + mCtx = ctx; + mPm = ctx.getPackageManager(); mAi = ai; + mIconDpi = iconDpi; mHasSubstitutePermission = PackageManager.PERMISSION_GRANTED == mPm.checkPermission( android.Manifest.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON, mAi.packageName); } - Drawable getIcon() { - return new BitmapDrawable(getResources(), getIconBitmap()); + public Drawable getIcon() { + return new BitmapDrawable(mCtx.getResources(), getIconBitmap()); } - Bitmap getIconBitmap() { + public Bitmap getIconBitmap() { Drawable dr = null; if (mHasSubstitutePermission) { - dr = getIconSubstitute(); + dr = getIconSubstituteInternal(); } if (dr == null) { @@ -542,18 +548,18 @@ public class ResolverActivity extends Activity { dr = mAi.loadIcon(mPm); } - SimpleIconFactory sif = SimpleIconFactory.obtain(ResolverActivity.this); + SimpleIconFactory sif = SimpleIconFactory.obtain(mCtx); Bitmap icon = sif.createUserBadgedIconBitmap(dr, Process.myUserHandle()); sif.recycle(); return icon; } - String getLabel() { + public String getLabel() { String label = null; // Apps with the substitute permission will always show the sublabel as their label if (mHasSubstitutePermission) { - label = getAppSubLabel(); + label = getAppSubLabelInternal(); } if (label == null) { @@ -563,10 +569,14 @@ public class ResolverActivity extends Activity { return label; } - String getSubLabel() { + public String getSubLabel() { // Apps with the substitute permission will never have a sublabel if (mHasSubstitutePermission) return null; - return getAppSubLabel(); + return getAppSubLabelInternal(); + } + + protected String loadLabelFromResource(Resources res, int resId) { + return res.getString(resId); } @Nullable @@ -576,17 +586,19 @@ public class ResolverActivity extends Activity { } - protected class ResolveInfoPresentationGetter extends TargetPresentationGetter { - + /** + * Loads the icon and label for the provided ResolveInfo. + */ + @VisibleForTesting + public static class ResolveInfoPresentationGetter extends TargetPresentationGetter { private final ResolveInfo mRi; - - ResolveInfoPresentationGetter(ResolveInfo ri) { - super(ri.activityInfo.applicationInfo); + public ResolveInfoPresentationGetter(Context ctx, int iconDpi, ResolveInfo ri) { + super(ctx, iconDpi, ri.activityInfo.applicationInfo); mRi = ri; } @Override - Drawable getIconSubstitute() { + Drawable getIconSubstituteInternal() { Drawable dr = null; try { // Do not use ResolveInfo#getIconResource() as it defaults to the app @@ -603,20 +615,31 @@ public class ResolverActivity extends Activity { } @Override - String getAppSubLabel() { + String getAppSubLabelInternal() { + // Will default to app name if no intent filter or activity label set, make sure to + // check if subLabel matches label before final display return (String) mRi.loadLabel(mPm); } } - protected class ActivityInfoPresentationGetter extends TargetPresentationGetter { + ResolveInfoPresentationGetter makePresentationGetter(ResolveInfo ri) { + return new ResolveInfoPresentationGetter(this, mIconDpi, ri); + } + + /** + * Loads the icon and label for the provided ActivityInfo. + */ + @VisibleForTesting + public static class ActivityInfoPresentationGetter extends TargetPresentationGetter { private final ActivityInfo mActivityInfo; - protected ActivityInfoPresentationGetter(ActivityInfo activityInfo) { - super(activityInfo.applicationInfo); + public ActivityInfoPresentationGetter(Context ctx, int iconDpi, + ActivityInfo activityInfo) { + super(ctx, iconDpi, activityInfo.applicationInfo); mActivityInfo = activityInfo; } @Override - Drawable getIconSubstitute() { + Drawable getIconSubstituteInternal() { Drawable dr = null; try { // Do not use ActivityInfo#getIconResource() as it defaults to the app @@ -634,13 +657,19 @@ public class ResolverActivity extends Activity { } @Override - String getAppSubLabel() { + String getAppSubLabelInternal() { + // Will default to app name if no activity label set, make sure to check if subLabel + // matches label before final display return (String) mActivityInfo.loadLabel(mPm); } } + protected ActivityInfoPresentationGetter makePresentationGetter(ActivityInfo ai) { + return new ActivityInfoPresentationGetter(this, mIconDpi, ai); + } + Drawable loadIconForResolveInfo(ResolveInfo ri) { - return (new ResolveInfoPresentationGetter(ri)).getIcon(); + return makePresentationGetter(ri).getIcon(); } @Override @@ -1201,7 +1230,7 @@ public class ResolverActivity extends Activity { final ImageView iconView = findViewById(R.id.icon); final DisplayResolveInfo iconInfo = mAdapter.getFilteredItem(); if (iconView != null && iconInfo != null) { - new LoadIconIntoViewTask(iconInfo, iconView).execute(); + new LoadIconTask(iconInfo, iconView).execute(); } } @@ -1713,34 +1742,13 @@ public class ResolverActivity extends Activity { } } - // Check for applications with same name and use application name or - // package name if necessary - ResolvedComponentInfo rci0 = sortedComponents.get(0); - ResolveInfo r0 = rci0.getResolveInfoAt(0); - int start = 0; - CharSequence r0Label = r0.loadLabel(mPm); - mHasExtendedInfo = false; - for (int i = 1; i < N; i++) { - if (r0Label == null) { - r0Label = r0.activityInfo.packageName; - } - ResolvedComponentInfo rci = sortedComponents.get(i); - ResolveInfo ri = rci.getResolveInfoAt(0); - CharSequence riLabel = ri.loadLabel(mPm); - if (riLabel == null) { - riLabel = ri.activityInfo.packageName; - } - if (riLabel.equals(r0Label)) { - continue; + for (ResolvedComponentInfo rci : sortedComponents) { + final ResolveInfo ri = rci.getResolveInfoAt(0); + if (ri != null) { + ResolveInfoPresentationGetter pg = makePresentationGetter(ri); + addResolveInfoWithAlternates(rci, pg.getSubLabel(), pg.getLabel()); } - processGroup(sortedComponents, start, (i - 1), rci0, r0Label); - rci0 = rci; - r0 = ri; - r0Label = riLabel; - start = i; } - // Process last group - processGroup(sortedComponents, start, (N - 1), rci0, r0Label); } postListReadyRunnable(); @@ -1782,55 +1790,6 @@ public class ResolverActivity extends Activity { return mFilterLastUsed; } - private void processGroup(List rList, int start, int end, - ResolvedComponentInfo ro, CharSequence roLabel) { - // Process labels from start to i - int num = end - start+1; - if (num == 1) { - // No duplicate labels. Use label for entry at start - addResolveInfoWithAlternates(ro, null, roLabel); - } else { - mHasExtendedInfo = true; - boolean usePkg = false; - final ApplicationInfo ai = ro.getResolveInfoAt(0).activityInfo.applicationInfo; - final CharSequence startApp = ai.loadLabel(mPm); - if (startApp == null) { - usePkg = true; - } - if (!usePkg) { - // Use HashSet to track duplicates - HashSet duplicates = - new HashSet(); - duplicates.add(startApp); - for (int j = start+1; j <= end ; j++) { - ResolveInfo jRi = rList.get(j).getResolveInfoAt(0); - CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm); - if ( (jApp == null) || (duplicates.contains(jApp))) { - usePkg = true; - break; - } else { - duplicates.add(jApp); - } - } - // Clear HashSet for later use - duplicates.clear(); - } - for (int k = start; k <= end; k++) { - final ResolvedComponentInfo rci = rList.get(k); - final ResolveInfo add = rci.getResolveInfoAt(0); - final CharSequence extraInfo; - if (usePkg) { - // Use package name for all entries from start to end-1 - extraInfo = add.activityInfo.packageName; - } else { - // Use application name for all entries from start to end-1 - extraInfo = add.activityInfo.applicationInfo.loadLabel(mPm); - } - addResolveInfoWithAlternates(rci, extraInfo, roLabel); - } - } - } - private void addResolveInfoWithAlternates(ResolvedComponentInfo rci, CharSequence extraInfo, CharSequence roLabel) { final int count = rci.getCount(); @@ -1912,14 +1871,6 @@ public class ResolverActivity extends Activity { return mDisplayList.size(); } - public int getDisplayInfoCount() { - return mDisplayList.size(); - } - - public DisplayResolveInfo getDisplayInfoAt(int index) { - return mDisplayList.get(index); - } - @Nullable public TargetInfo getItem(int position) { if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) { @@ -1979,36 +1930,38 @@ public class ResolverActivity extends Activity { com.android.internal.R.layout.resolve_list_item, parent, false); } - public boolean showsExtendedInfo(TargetInfo info) { - return !TextUtils.isEmpty(info.getExtendedInfo()); - } - public final void bindView(int position, View view) { onBindView(view, getItem(position)); } - private void onBindView(View view, TargetInfo info) { + protected void onBindView(View view, TargetInfo info) { final ViewHolder holder = (ViewHolder) view.getTag(); if (info == null) { holder.icon.setImageDrawable( getDrawable(R.drawable.resolver_icon_placeholder)); return; } + final CharSequence label = info.getDisplayLabel(); if (!TextUtils.equals(holder.text.getText(), label)) { holder.text.setText(info.getDisplayLabel()); } - if (showsExtendedInfo(info)) { - holder.text2.setVisibility(View.VISIBLE); - holder.text2.setText(info.getExtendedInfo()); - } else { - holder.text2.setVisibility(View.GONE); + + // Always show a subLabel for visual consistency across list items. Show an empty + // subLabel if the subLabel is the same as the label + CharSequence subLabel = info.getExtendedInfo(); + if (TextUtils.equals(label, subLabel)) subLabel = null; + + if (!TextUtils.equals(holder.text2.getText(), subLabel)) { + holder.text2.setText(subLabel); } + if (info instanceof DisplayResolveInfo && !((DisplayResolveInfo) info).hasDisplayIcon()) { - new LoadAdapterIconTask((DisplayResolveInfo) info).execute(); + new LoadIconTask((DisplayResolveInfo) info, holder.icon).execute(); + } else { + holder.icon.setImageDrawable(info.getDisplayIcon()); } - holder.icon.setImageDrawable(info.getDisplayIcon()); } } @@ -2127,13 +2080,15 @@ public class ResolverActivity extends Activity { } - abstract class LoadIconTask extends AsyncTask { + class LoadIconTask extends AsyncTask { protected final DisplayResolveInfo mDisplayResolveInfo; private final ResolveInfo mResolveInfo; + private final ImageView mTargetView; - public LoadIconTask(DisplayResolveInfo dri) { + LoadIconTask(DisplayResolveInfo dri, ImageView target) { mDisplayResolveInfo = dri; mResolveInfo = dri.getResolveInfo(); + mTargetView = target; } @Override @@ -2143,37 +2098,12 @@ public class ResolverActivity extends Activity { @Override protected void onPostExecute(Drawable d) { - mDisplayResolveInfo.setDisplayIcon(d); - } - } - - class LoadAdapterIconTask extends LoadIconTask { - public LoadAdapterIconTask(DisplayResolveInfo dri) { - super(dri); - } - - @Override - protected void onPostExecute(Drawable d) { - super.onPostExecute(d); if (mProfileView != null && mAdapter.getOtherProfile() == mDisplayResolveInfo) { bindProfileView(); + } else { + mDisplayResolveInfo.setDisplayIcon(d); + mTargetView.setImageDrawable(d); } - mAdapter.notifyDataSetChanged(); - } - } - - class LoadIconIntoViewTask extends LoadIconTask { - private final ImageView mTargetView; - - public LoadIconIntoViewTask(DisplayResolveInfo dri, ImageView target) { - super(dri); - mTargetView = target; - } - - @Override - protected void onPostExecute(Drawable d) { - super.onPostExecute(d); - mTargetView.setImageDrawable(d); } } diff --git a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java index a88a80f199a472ff7b326db9375ceeb12221a4e0..9bf4f01bab06ca433d4da78833a7895dbb5071d9 100644 --- a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java +++ b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java @@ -60,8 +60,6 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator private static final boolean DEBUG = false; - private static final int NUM_OF_TOP_ANNOTATIONS_TO_USE = 3; - // One week private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 7; @@ -80,11 +78,6 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator private static final int WATCHDOG_TIMEOUT_MILLIS = 500; private final Collator mCollator; - private final boolean mHttp; - // can be null if mHttp == false or current user has no default browser package - private final String mDefaultBrowserPackageName; - private final PackageManager mPm; - private final UsageStatsManager mUsm; private final Map mStats; private final long mCurrentTime; private final long mSinceTime; @@ -92,8 +85,6 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator private final String mReferrerPackage; private final Object mLock = new Object(); private ArrayList mTargets; - private String mContentType; - private String[] mAnnotations; private String mAction; private ComponentName mResolvedRankerName; private ComponentName mRankerServiceName; @@ -155,43 +146,17 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator public ResolverRankerServiceResolverComparator(Context context, Intent intent, String referrerPackage, AfterCompute afterCompute) { + super(context, intent); mCollator = Collator.getInstance(context.getResources().getConfiguration().locale); - String scheme = intent.getScheme(); - mHttp = "http".equals(scheme) || "https".equals(scheme); mReferrerPackage = referrerPackage; mAfterCompute = afterCompute; mContext = context; - mPm = context.getPackageManager(); - mUsm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); - mCurrentTime = System.currentTimeMillis(); mSinceTime = mCurrentTime - USAGE_STATS_PERIOD; mStats = mUsm.queryAndAggregateUsageStats(mSinceTime, mCurrentTime); - mContentType = intent.getType(); - getContentAnnotations(intent); mAction = intent.getAction(); mRankerServiceName = new ComponentName(mContext, this.getClass()); - - mDefaultBrowserPackageName = mHttp - ? mPm.getDefaultBrowserPackageNameAsUser(UserHandle.myUserId()) - : null; - } - - // get annotations of content from intent. - private void getContentAnnotations(Intent intent) { - ArrayList annotations = intent.getStringArrayListExtra( - Intent.EXTRA_CONTENT_ANNOTATIONS); - if (annotations != null) { - int size = annotations.size(); - if (size > NUM_OF_TOP_ANNOTATIONS_TO_USE) { - size = NUM_OF_TOP_ANNOTATIONS_TO_USE; - } - mAnnotations = new String[size]; - for (int i = 0; i < size; i++) { - mAnnotations[i] = annotations.get(i); - } - } } // compute features for each target according to usage stats of targets. @@ -286,36 +251,7 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator } @Override - public int compare(ResolvedComponentInfo lhsp, ResolvedComponentInfo rhsp) { - final ResolveInfo lhs = lhsp.getResolveInfoAt(0); - final ResolveInfo rhs = rhsp.getResolveInfoAt(0); - - // We want to put the one targeted to another user at the end of the dialog. - if (lhs.targetUserId != UserHandle.USER_CURRENT) { - return rhs.targetUserId != UserHandle.USER_CURRENT ? 0 : 1; - } - if (rhs.targetUserId != UserHandle.USER_CURRENT) { - return -1; - } - - if (mHttp) { - // Special case: we want filters that match URI paths/schemes to be - // ordered before others. This is for the case when opening URIs, - // to make native apps go above browsers - except for 1 even more special case - // which is the default browser, as we want that to go above them all. - if (isDefaultBrowser(lhs)) { - return -1; - } - if (isDefaultBrowser(rhs)) { - return 1; - } - final boolean lhsSpecific = ResolverActivity.isSpecificUriMatch(lhs.match); - final boolean rhsSpecific = ResolverActivity.isSpecificUriMatch(rhs.match); - if (lhsSpecific != rhsSpecific) { - return lhsSpecific ? -1 : 1; - } - } - + public int compare(ResolveInfo lhs, ResolveInfo rhs) { if (mStats != null) { final ResolverTarget lhsTarget = mTargetsDict.get(new ComponentName( lhs.activityInfo.packageName, lhs.activityInfo.name)); @@ -349,13 +285,6 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator return 0; } - @Override - public void updateChooserCounts(String packageName, int userId, String action) { - if (mUsm != null) { - mUsm.reportChooserSelection(packageName, userId, mContentType, mAnnotations, action); - } - } - // update ranking model when the connection to it is valid. @Override public void updateModel(ComponentName componentName) { @@ -407,20 +336,6 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator } } - private boolean isDefaultBrowser(ResolveInfo ri) { - // It makes sense to prefer the default browser - // only if the targeted user is the current user - if (ri.targetUserId != UserHandle.USER_CURRENT) { - return false; - } - - if (ri.activityInfo.packageName != null - && ri.activityInfo.packageName.equals(mDefaultBrowserPackageName)) { - return true; - } - return false; - } - // records metrics for evaluation. private void logMetrics(int selectedPos) { if (mRankerServiceName != null) { diff --git a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java index 37f61bf650c77152c2056f6290d0cb4078ade0e6..206efa94b7f698bda65e01cd61f7b799be4ba2ee 100644 --- a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java +++ b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java @@ -45,9 +45,9 @@ public abstract class AbstractMultiplePendingRequestsRemoteService callback, @NonNull Handler handler, - boolean bindInstantServiceAllowed, boolean verbose, int initialCapacity) { - super(context, serviceInterface, componentName, userId, callback, handler, - bindInstantServiceAllowed, verbose); + int bindingFlags, boolean verbose, int initialCapacity) { + super(context, serviceInterface, componentName, userId, callback, handler, bindingFlags, + verbose); mInitialCapacity = initialCapacity; } diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java index 0a83fcc6db61dfd0880ae7b8ec4f77139794ba5f..1155854efd559b1958d6d6cc840305a2e6726c98 100644 --- a/core/java/com/android/internal/infra/AbstractRemoteService.java +++ b/core/java/com/android/internal/infra/AbstractRemoteService.java @@ -82,7 +82,7 @@ public abstract class AbstractRemoteService mVultureCallback; private final int mUserId; private final ServiceConnection mServiceConnection = new RemoteServiceConnection(); - private final boolean mBindInstantServiceAllowed; + private final int mBindingFlags; protected I mService; private boolean mBinding; @@ -113,7 +113,7 @@ public abstract class AbstractRemoteService callback, - @NonNull Handler handler, boolean bindInstantServiceAllowed, boolean verbose) { + @NonNull Handler handler, int bindingFlags, boolean verbose) { mContext = context; mVultureCallback = callback; mVerbose = verbose; @@ -121,7 +121,7 @@ public abstract class AbstractRemoteService callback, @NonNull Handler handler, - boolean bindInstantServiceAllowed, boolean verbose) { - super(context, serviceInterface, componentName, userId, callback, handler, - bindInstantServiceAllowed, verbose); + int bindingFlags, boolean verbose) { + super(context, serviceInterface, componentName, userId, callback, handler, bindingFlags, + verbose); } @Override // from AbstractRemoteService diff --git a/core/java/com/android/internal/infra/GlobalWhitelistState.java b/core/java/com/android/internal/infra/GlobalWhitelistState.java new file mode 100644 index 0000000000000000000000000000000000000000..dfa59b7bd0acf9a7ef3960be7713b9604b16bbb4 --- /dev/null +++ b/core/java/com/android/internal/infra/GlobalWhitelistState.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.infra; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.ComponentName; +import android.util.ArraySet; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; + +import java.io.PrintWriter; +import java.util.List; + +/** + * Helper class used to manage a {@link WhitelistHelper} per user instance when the main service + * cannot hold a lock when external entities (typically {@code ActivityManagerService}) needs to + * get whitelist info. + * + *

      This class is thread safe. + */ +public class GlobalWhitelistState { + + // Uses full-name to avoid collision with service-provided mLock + protected final Object mGlobalWhitelistStateLock = new Object(); + + @Nullable + @GuardedBy("mGlobalWhitelistStateLock") + protected SparseArray mWhitelisterHelpers; + + /** + * Sets the whitelist for the given user. + */ + public void setWhitelist(@UserIdInt int userId, @Nullable List packageNames, + @Nullable List components) { + synchronized (mGlobalWhitelistStateLock) { + if (mWhitelisterHelpers == null) { + mWhitelisterHelpers = new SparseArray<>(1); + } + WhitelistHelper helper = mWhitelisterHelpers.get(userId); + if (helper == null) { + helper = new WhitelistHelper(); + mWhitelisterHelpers.put(userId, helper); + } + helper.setWhitelist(packageNames, components); + } + } + + /** + * Checks if the given package is whitelisted for the given user. + */ + public boolean isWhitelisted(@UserIdInt int userId, @NonNull String packageName) { + synchronized (mGlobalWhitelistStateLock) { + if (mWhitelisterHelpers == null) return false; + final WhitelistHelper helper = mWhitelisterHelpers.get(userId); + return helper == null ? false : helper.isWhitelisted(packageName); + } + } + + /** + * Checks if the given component is whitelisted for the given user. + */ + public boolean isWhitelisted(@UserIdInt int userId, @NonNull ComponentName componentName) { + synchronized (mGlobalWhitelistStateLock) { + if (mWhitelisterHelpers == null) return false; + final WhitelistHelper helper = mWhitelisterHelpers.get(userId); + return helper == null ? false : helper.isWhitelisted(componentName); + } + } + + /** + * Gets the whitelisted components for the given package and user. + */ + public ArraySet getWhitelistedComponents(@UserIdInt int userId, + @NonNull String packageName) { + synchronized (mGlobalWhitelistStateLock) { + if (mWhitelisterHelpers == null) return null; + final WhitelistHelper helper = mWhitelisterHelpers.get(userId); + return helper == null ? null : helper.getWhitelistedComponents(packageName); + } + } + + /** + * Resets the whitelist for the given user. + */ + public void resetWhitelist(@NonNull int userId) { + synchronized (mGlobalWhitelistStateLock) { + if (mWhitelisterHelpers == null) return; + mWhitelisterHelpers.remove(userId); + if (mWhitelisterHelpers.size() == 0) { + mWhitelisterHelpers = null; + } + } + } + + /** + * Dumps it! + */ + public void dump(@NonNull String prefix, @NonNull PrintWriter pw) { + pw.print(prefix); pw.print("State: "); + synchronized (mGlobalWhitelistStateLock) { + if (mWhitelisterHelpers == null) { + pw.println("empty"); + return; + } + pw.print(mWhitelisterHelpers.size()); pw.println(" services"); + final String prefix2 = prefix + " "; + for (int i = 0; i < mWhitelisterHelpers.size(); i++) { + final int userId = mWhitelisterHelpers.keyAt(i); + final WhitelistHelper helper = mWhitelisterHelpers.valueAt(i); + helper.dump(prefix2, "Whitelist for userId " + userId, pw); + } + } + } +} diff --git a/core/java/com/android/internal/infra/WhitelistHelper.java b/core/java/com/android/internal/infra/WhitelistHelper.java index 183b465afbfd487bfd7e1993cebae5408396fff2..d7753db6b0f7c2a8dc6349b9e1f7d571cfcf7301 100644 --- a/core/java/com/android/internal/infra/WhitelistHelper.java +++ b/core/java/com/android/internal/infra/WhitelistHelper.java @@ -31,6 +31,7 @@ import java.util.List; /** * Helper class for keeping track of whitelisted packages/activities. * + *

      NOTE: this class is not thread safe. * @hide */ public final class WhitelistHelper { diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java index 248e0266b70b5e84ebe6c942ba590cda95993fce..e4de1586bc5143001c0dca94cd9e6d4658104d47 100644 --- a/core/java/com/android/internal/os/KernelCpuThreadReader.java +++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java @@ -32,15 +32,18 @@ import java.util.ArrayList; import java.util.function.Predicate; /** - * Given a process, will iterate over the child threads of the process, and return the CPU usage - * statistics for each child thread. The CPU usage statistics contain the amount of time spent in a - * frequency band. + * Iterates over processes, and all threads owned by those processes, and return the CPU usage for + * each thread. The CPU usage statistics contain the amount of time spent in a frequency band. CPU + * usage is collected using {@link ProcTimeInStateReader}. + * + *

      We only collect CPU data for processes and threads that are owned by certain UIDs. These UIDs + * are configured via {@link #setUidPredicate}. * *

      Frequencies are bucketed together to reduce the amount of data created. This means that we - * return less frequencies than provided by {@link ProcTimeInStateReader}. The number of - * frequencies is configurable by {@link #setNumBuckets}. Frequencies are reported as the lowest - * frequency in that range. Frequencies are spread as evenly as possible across the buckets. The - * buckets do not cross over the little/big frequencies reported. + * return less frequencies than provided by {@link ProcTimeInStateReader}. The number of frequencies + * is configurable by {@link #setNumBuckets}. Frequencies are reported as the lowest frequency in + * that range. Frequencies are spread as evenly as possible across the buckets. The buckets do not + * cross over the little/big frequencies reported. * *

      N.B.: In order to bucket across little/big frequencies correctly, we assume that the {@code * time_in_state} file contains every little core frequency in ascending order, followed by every @@ -60,56 +63,39 @@ public class KernelCpuThreadReader { private static final String CPU_STATISTICS_FILENAME = "time_in_state"; /** - * The name of the file to read process command line invocation from, must be found in - * {@code /proc/$PID/} + * The name of the file to read process command line invocation from, must be found in {@code + * /proc/$PID/} */ private static final String PROCESS_NAME_FILENAME = "cmdline"; /** - * The name of the file to read thread name from, must be found in - * {@code /proc/$PID/task/$TID} + * The name of the file to read thread name from, must be found in {@code /proc/$PID/task/$TID} */ private static final String THREAD_NAME_FILENAME = "comm"; - /** - * Glob pattern for the process directory names under {@code proc} - */ + /** Glob pattern for the process directory names under {@code proc} */ private static final String PROCESS_DIRECTORY_FILTER = "[0-9]*"; - /** - * Default process name when the name can't be read - */ + /** Default process name when the name can't be read */ private static final String DEFAULT_PROCESS_NAME = "unknown_process"; - /** - * Default thread name when the name can't be read - */ + /** Default thread name when the name can't be read */ private static final String DEFAULT_THREAD_NAME = "unknown_thread"; - /** - * Default mount location of the {@code proc} filesystem - */ + /** Default mount location of the {@code proc} filesystem */ private static final Path DEFAULT_PROC_PATH = Paths.get("/proc"); - /** - * The initial {@code time_in_state} file for {@link ProcTimeInStateReader} - */ + /** The initial {@code time_in_state} file for {@link ProcTimeInStateReader} */ private static final Path DEFAULT_INITIAL_TIME_IN_STATE_PATH = DEFAULT_PROC_PATH.resolve("self/time_in_state"); - /** - * Value returned when there was an error getting an integer ID value (e.g. PID, UID) - */ + /** Value returned when there was an error getting an integer ID value (e.g. PID, UID) */ private static final int ID_ERROR = -1; - /** - * Thread ID used when reporting CPU used by other threads - */ + /** Thread ID used when reporting CPU used by other threads */ private static final int OTHER_THREADS_ID = -1; - /** - * Thread name used when reporting CPU used by other threads - */ + /** Thread name used when reporting CPU used by other threads */ private static final String OTHER_THREADS_NAME = "__OTHER_THREADS"; /** @@ -124,9 +110,7 @@ public class KernelCpuThreadReader { */ private int mMinimumTotalCpuUsageMillis; - /** - * Where the proc filesystem is mounted - */ + /** Where the proc filesystem is mounted */ private final Path mProcPath; /** @@ -135,14 +119,10 @@ public class KernelCpuThreadReader { */ private int[] mFrequenciesKhz; - /** - * Used to read and parse {@code time_in_state} files - */ + /** Used to read and parse {@code time_in_state} files */ private final ProcTimeInStateReader mProcTimeInStateReader; - /** - * Used to sort frequencies and usage times into buckets - */ + /** Used to sort frequencies and usage times into buckets */ private FrequencyBucketCreator mFrequencyBucketCreator; private final Injector mInjector; @@ -150,10 +130,9 @@ public class KernelCpuThreadReader { /** * Create with a path where `proc` is mounted. Used primarily for testing * - * @param procPath where `proc` is mounted (to find, see {@code mount | grep - * ^proc}) + * @param procPath where `proc` is mounted (to find, see {@code mount | grep ^proc}) * @param initialTimeInStatePath where the initial {@code time_in_state} file exists to define - * format + * format */ @VisibleForTesting public KernelCpuThreadReader( @@ -162,7 +141,8 @@ public class KernelCpuThreadReader { int minimumTotalCpuUsageMillis, Path procPath, Path initialTimeInStatePath, - Injector injector) throws IOException { + Injector injector) + throws IOException { mUidPredicate = uidPredicate; mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis; mProcPath = procPath; @@ -205,7 +185,7 @@ public class KernelCpuThreadReader { * #setUidPredicate}. */ @Nullable - public ArrayList getProcessCpuUsageByUids() { + public ArrayList getProcessCpuUsage() { if (DEBUG) { Slog.d(TAG, "Reading CPU thread usages for processes owned by UIDs"); } @@ -213,7 +193,7 @@ public class KernelCpuThreadReader { final ArrayList processCpuUsages = new ArrayList<>(); try (DirectoryStream processPaths = - Files.newDirectoryStream(mProcPath, PROCESS_DIRECTORY_FILTER)) { + Files.newDirectoryStream(mProcPath, PROCESS_DIRECTORY_FILTER)) { for (Path processPath : processPaths) { final int processId = getProcessId(processPath); final int uid = mInjector.getUidForPid(processId); @@ -231,7 +211,7 @@ public class KernelCpuThreadReader { } } } catch (IOException e) { - Slog.w("Failed to iterate over process paths", e); + Slog.w(TAG, "Failed to iterate over process paths", e); return null; } @@ -248,30 +228,68 @@ public class KernelCpuThreadReader { } /** - * Read all of the CPU usage statistics for each child thread of the current process - * - * @return process CPU usage containing usage of all child threads + * Get the CPU frequencies that correspond to the times reported in {@link + * ThreadCpuUsage#usageTimesMillis} */ @Nullable - public ProcessCpuUsage getCurrentProcessCpuUsage() { - return getProcessCpuUsage(mProcPath.resolve("self"), mInjector.myPid(), mInjector.myUid()); + public int[] getCpuFrequenciesKhz() { + return mFrequenciesKhz; + } + + /** Set the number of frequency buckets to use */ + void setNumBuckets(int numBuckets) { + if (numBuckets < 1) { + Slog.w(TAG, "Number of buckets must be at least 1, but was " + numBuckets); + return; + } + // If `numBuckets` hasn't changed since the last set, do nothing + if (mFrequenciesKhz != null && mFrequenciesKhz.length == numBuckets) { + return; + } + mFrequencyBucketCreator = + new FrequencyBucketCreator(mProcTimeInStateReader.getFrequenciesKhz(), numBuckets); + mFrequenciesKhz = + mFrequencyBucketCreator.getBucketMinFrequencies( + mProcTimeInStateReader.getFrequenciesKhz()); + } + + /** Set the UID predicate for {@link #getProcessCpuUsage} */ + void setUidPredicate(Predicate uidPredicate) { + mUidPredicate = uidPredicate; + } + + /** + * If a thread has strictly less than {@code minimumTotalCpuUsageMillis} total CPU usage, it + * will not be reported + */ + void setMinimumTotalCpuUsageMillis(int minimumTotalCpuUsageMillis) { + if (minimumTotalCpuUsageMillis < 0) { + Slog.w(TAG, "Negative minimumTotalCpuUsageMillis: " + minimumTotalCpuUsageMillis); + return; + } + mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis; } /** * Read all of the CPU usage statistics for each child thread of a process * * @param processPath the {@code /proc} path of the thread - * @param processId the ID of the process - * @param uid the ID of the user who owns the process + * @param processId the ID of the process + * @param uid the ID of the user who owns the process * @return process CPU usage containing usage of all child threads. Null if the process exited - * and its {@code proc} directory was removed while collecting information + * and its {@code proc} directory was removed while collecting information */ @Nullable private ProcessCpuUsage getProcessCpuUsage(Path processPath, int processId, int uid) { if (DEBUG) { - Slog.d(TAG, "Reading CPU thread usages with directory " + processPath - + " process ID " + processId - + " and user ID " + uid); + Slog.d( + TAG, + "Reading CPU thread usages with directory " + + processPath + + " process ID " + + processId + + " and user ID " + + uid); } int[] filteredThreadsCpuUsage = null; @@ -305,64 +323,15 @@ public class KernelCpuThreadReader { // Add the filtered out thread CPU usage under an "other threads" ThreadCpuUsage if (filteredThreadsCpuUsage != null) { - threadCpuUsages.add(new ThreadCpuUsage( - OTHER_THREADS_ID, OTHER_THREADS_NAME, filteredThreadsCpuUsage)); + threadCpuUsages.add( + new ThreadCpuUsage( + OTHER_THREADS_ID, OTHER_THREADS_NAME, filteredThreadsCpuUsage)); } if (DEBUG) { Slog.d(TAG, "Read CPU usage of " + threadCpuUsages.size() + " threads"); } - return new ProcessCpuUsage( - processId, - getProcessName(processPath), - uid, - threadCpuUsages); - } - - /** - * Set the number of frequency buckets to use - */ - void setNumBuckets(int numBuckets) { - if (numBuckets < 1) { - Slog.w(TAG, "Number of buckets must be at least 1, but was " + numBuckets); - return; - } - // If `numBuckets` hasn't changed since the last set, do nothing - if (mFrequenciesKhz != null && mFrequenciesKhz.length == numBuckets) { - return; - } - mFrequencyBucketCreator = new FrequencyBucketCreator( - mProcTimeInStateReader.getFrequenciesKhz(), numBuckets); - mFrequenciesKhz = mFrequencyBucketCreator.getBucketMinFrequencies( - mProcTimeInStateReader.getFrequenciesKhz()); - } - - /** - * Set the UID predicate for {@link #getProcessCpuUsageByUids} - */ - void setUidPredicate(Predicate uidPredicate) { - mUidPredicate = uidPredicate; - } - - /** - * If a thread has strictly less than {@code minimumTotalCpuUsageMillis} total CPU usage, it - * will not be reported - */ - void setMinimumTotalCpuUsageMillis(int minimumTotalCpuUsageMillis) { - if (minimumTotalCpuUsageMillis < 0) { - Slog.w(TAG, "Negative minimumTotalCpuUsageMillis: " + minimumTotalCpuUsageMillis); - return; - } - mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis; - } - - /** - * Get the CPU frequencies that correspond to the times reported in - * {@link ThreadCpuUsage#usageTimesMillis} - */ - @Nullable - public int[] getCpuFrequenciesKhz() { - return mFrequenciesKhz; + return new ProcessCpuUsage(processId, getProcessName(processPath), uid, threadCpuUsages); } /** @@ -370,7 +339,7 @@ public class KernelCpuThreadReader { * * @param threadDirectory the {@code /proc} directory of the thread * @return thread CPU usage. Null if the thread exited and its {@code proc} directory was - * removed while collecting information + * removed while collecting information */ @Nullable private ThreadCpuUsage getThreadCpuUsage(Path threadDirectory) { @@ -398,27 +367,21 @@ public class KernelCpuThreadReader { return new ThreadCpuUsage(threadId, threadName, cpuUsages); } - /** - * Get the command used to start a process - */ + /** Get the command used to start a process */ private String getProcessName(Path processPath) { final Path processNamePath = processPath.resolve(PROCESS_NAME_FILENAME); - final String processName = - ProcStatsUtil.readSingleLineProcFile(processNamePath.toString()); + final String processName = ProcStatsUtil.readSingleLineProcFile(processNamePath.toString()); if (processName != null) { return processName; } return DEFAULT_PROCESS_NAME; } - /** - * Get the name of a thread, given the {@code /proc} path of the thread - */ + /** Get the name of a thread, given the {@code /proc} path of the thread */ private String getThreadName(Path threadPath) { final Path threadNamePath = threadPath.resolve(THREAD_NAME_FILENAME); - final String threadName = - ProcStatsUtil.readNullSeparatedFile(threadNamePath.toString()); + final String threadName = ProcStatsUtil.readNullSeparatedFile(threadNamePath.toString()); if (threadName == null) { return DEFAULT_THREAD_NAME; } @@ -441,9 +404,8 @@ public class KernelCpuThreadReader { } } - /** - * Get the sum of all CPU usage across all frequencies - */ + /** Get the sum of all CPU usage across all frequencies */ + @SuppressWarnings("ForLoopReplaceableByForEach") private static int totalCpuUsage(int[] cpuUsage) { int total = 0; for (int i = 0; i < cpuUsage.length; i++) { @@ -452,9 +414,7 @@ public class KernelCpuThreadReader { return total; } - /** - * Add two CPU frequency usages together - */ + /** Add two CPU frequency usages together */ private static int[] sumCpuUsage(int[] a, int[] b) { int[] summed = new int[a.length]; for (int i = 0; i < a.length; i++) { @@ -463,9 +423,7 @@ public class KernelCpuThreadReader { return summed; } - /** - * Puts frequencies and usage times into buckets - */ + /** Puts frequencies and usage times into buckets */ @VisibleForTesting public static class FrequencyBucketCreator { private final int mNumBuckets; @@ -480,7 +438,7 @@ public class KernelCpuThreadReader { * Buckets based of a list of frequencies * * @param frequencies the frequencies to base buckets off - * @param numBuckets how many buckets to create + * @param numBuckets how many buckets to create */ @VisibleForTesting public FrequencyBucketCreator(long[] frequencies, int numBuckets) { @@ -502,20 +460,20 @@ public class KernelCpuThreadReader { // Ensure that we don't have more buckets than frequencies mLittleNumBuckets = Math.min(littleNumBuckets, mBigFrequenciesStartIndex); - mBigNumBuckets = Math.min( - bigNumBuckets, frequencies.length - mBigFrequenciesStartIndex); + mBigNumBuckets = + Math.min(bigNumBuckets, frequencies.length - mBigFrequenciesStartIndex); mNumBuckets = mLittleNumBuckets + mBigNumBuckets; // Set the size of each little and big bucket. If they have no buckets, the size is zero - mLittleBucketSize = mLittleNumBuckets == 0 ? 0 : - mBigFrequenciesStartIndex / mLittleNumBuckets; - mBigBucketSize = mBigNumBuckets == 0 ? 0 : - (frequencies.length - mBigFrequenciesStartIndex) / mBigNumBuckets; + mLittleBucketSize = + mLittleNumBuckets == 0 ? 0 : mBigFrequenciesStartIndex / mLittleNumBuckets; + mBigBucketSize = + mBigNumBuckets == 0 + ? 0 + : (frequencies.length - mBigFrequenciesStartIndex) / mBigNumBuckets; } - /** - * Find the index where frequencies change from little core to big core - */ + /** Find the index where frequencies change from little core to big core */ @VisibleForTesting public static int getBigFrequenciesStartIndex(long[] frequenciesKhz) { for (int i = 0; i < frequenciesKhz.length - 1; i++) { @@ -527,16 +485,14 @@ public class KernelCpuThreadReader { return frequenciesKhz.length; } - /** - * Get the minimum frequency in each bucket - */ + /** Get the minimum frequency in each bucket */ @VisibleForTesting public int[] getBucketMinFrequencies(long[] frequenciesKhz) { Preconditions.checkArgument(frequenciesKhz.length == mNumFrequencies); // If there's only one bucket, we bucket everything together so the first bucket is the // min frequency if (mNumBuckets == 1) { - return new int[]{(int) frequenciesKhz[0]}; + return new int[] {(int) frequenciesKhz[0]}; } final int[] bucketMinFrequencies = new int[mNumBuckets]; @@ -561,6 +517,7 @@ public class KernelCpuThreadReader { * @return the bucketed usage times */ @VisibleForTesting + @SuppressWarnings("ForLoopReplaceableByForEach") public int[] getBucketedValues(long[] values) { Preconditions.checkArgument(values.length == mNumFrequencies); final int[] bucketed = new int[mNumBuckets]; @@ -580,18 +537,18 @@ public class KernelCpuThreadReader { } // Initialize the big buckets for (int i = mBigFrequenciesStartIndex; i < values.length; i++) { - final int bucketIndex = Math.min( - mLittleNumBuckets + (i - mBigFrequenciesStartIndex) / mBigBucketSize, - mNumBuckets - 1); + final int bucketIndex = + Math.min( + mLittleNumBuckets + + (i - mBigFrequenciesStartIndex) / mBigBucketSize, + mNumBuckets - 1); bucketed[bucketIndex] += values[i]; } return bucketed; } } - /** - * CPU usage of a process - */ + /** CPU usage of a process */ public static class ProcessCpuUsage { public final int processId; public final String processName; @@ -610,46 +567,23 @@ public class KernelCpuThreadReader { } } - /** - * CPU usage of a thread - */ + /** CPU usage of a thread */ public static class ThreadCpuUsage { public final int threadId; public final String threadName; public final int[] usageTimesMillis; - ThreadCpuUsage( - int threadId, - String threadName, - int[] usageTimesMillis) { + ThreadCpuUsage(int threadId, String threadName, int[] usageTimesMillis) { this.threadId = threadId; this.threadName = threadName; this.usageTimesMillis = usageTimesMillis; } } - /** - * Used to inject static methods from {@link Process} - */ + /** Used to inject static methods from {@link Process} */ @VisibleForTesting public static class Injector { - /** - * Get the PID of the current process - */ - public int myPid() { - return Process.myPid(); - } - - /** - * Get the UID that owns the current process - */ - public int myUid() { - return Process.myUid(); - } - - /** - * Get the UID for the process with ID {@code pid} - */ + /** Get the UID for the process with ID {@code pid} */ public int getUidForPid(int pid) { return Process.getUidForPid(pid); } diff --git a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java index b8dfe0d63a66dffb8ffa63f6b116f053ebf87ff6..3851ce6d9cbdc70993cba4c676ac2dd3278d87f6 100644 --- a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java +++ b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java @@ -47,33 +47,29 @@ import java.util.regex.Pattern; public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { private static final String TAG = "KernelCpuThreadReaderSettingsObserver"; - /** - * The number of frequency buckets to report - */ + /** The number of frequency buckets to report */ private static final String NUM_BUCKETS_SETTINGS_KEY = "num_buckets"; + private static final int NUM_BUCKETS_DEFAULT = 8; - /** - * List of UIDs to report data for - */ + /** List of UIDs to report data for */ private static final String COLLECTED_UIDS_SETTINGS_KEY = "collected_uids"; + private static final String COLLECTED_UIDS_DEFAULT = "0-0;1000-1000"; - /** - * Minimum total CPU usage to report - */ + /** Minimum total CPU usage to report */ private static final String MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY = "minimum_total_cpu_usage_millis"; + private static final int MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT = 10000; private final Context mContext; - @Nullable - private final KernelCpuThreadReader mKernelCpuThreadReader; + @Nullable private final KernelCpuThreadReader mKernelCpuThreadReader; /** - * @return returns a created {@link KernelCpuThreadReader} that will be modified by any - * change in settings, returns null if creation failed + * @return returns a created {@link KernelCpuThreadReader} that will be modified by any change + * in settings, returns null if creation failed */ @Nullable public static KernelCpuThreadReader getSettingsModifiedReader(Context context) { @@ -81,10 +77,10 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { KernelCpuThreadReaderSettingsObserver settingsObserver = new KernelCpuThreadReaderSettingsObserver(context); // Register the observer to listen for setting changes - Uri settingsUri = - Settings.Global.getUriFor(Settings.Global.KERNEL_CPU_THREAD_READER); - context.getContentResolver().registerContentObserver( - settingsUri, false, settingsObserver, UserHandle.USER_SYSTEM); + Uri settingsUri = Settings.Global.getUriFor(Settings.Global.KERNEL_CPU_THREAD_READER); + context.getContentResolver() + .registerContentObserver( + settingsUri, false, settingsObserver, UserHandle.USER_SYSTEM); // Return the observer's reader return settingsObserver.mKernelCpuThreadReader; } @@ -92,10 +88,11 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { private KernelCpuThreadReaderSettingsObserver(Context context) { super(BackgroundThread.getHandler()); mContext = context; - mKernelCpuThreadReader = KernelCpuThreadReader.create( - NUM_BUCKETS_DEFAULT, - UidPredicate.fromString(COLLECTED_UIDS_DEFAULT), - MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT); + mKernelCpuThreadReader = + KernelCpuThreadReader.create( + NUM_BUCKETS_DEFAULT, + UidPredicate.fromString(COLLECTED_UIDS_DEFAULT), + MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT); } @Override @@ -103,9 +100,7 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { updateReader(); } - /** - * Update the reader with new settings - */ + /** Update the reader with new settings */ private void updateReader() { if (mKernelCpuThreadReader == null) { return; @@ -113,8 +108,10 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { final KeyValueListParser parser = new KeyValueListParser(','); try { - parser.setString(Settings.Global.getString( - mContext.getContentResolver(), Settings.Global.KERNEL_CPU_THREAD_READER)); + parser.setString( + Settings.Global.getString( + mContext.getContentResolver(), + Settings.Global.KERNEL_CPU_THREAD_READER)); } catch (IllegalArgumentException e) { Slog.e(TAG, "Bad settings", e); return; @@ -122,8 +119,9 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { final UidPredicate uidPredicate; try { - uidPredicate = UidPredicate.fromString( - parser.getString(COLLECTED_UIDS_SETTINGS_KEY, COLLECTED_UIDS_DEFAULT)); + uidPredicate = + UidPredicate.fromString( + parser.getString(COLLECTED_UIDS_SETTINGS_KEY, COLLECTED_UIDS_DEFAULT)); } catch (NumberFormatException e) { Slog.w(TAG, "Failed to get UID predicate", e); return; @@ -132,14 +130,13 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { mKernelCpuThreadReader.setNumBuckets( parser.getInt(NUM_BUCKETS_SETTINGS_KEY, NUM_BUCKETS_DEFAULT)); mKernelCpuThreadReader.setUidPredicate(uidPredicate); - mKernelCpuThreadReader.setMinimumTotalCpuUsageMillis(parser.getInt( - MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY, - MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT)); + mKernelCpuThreadReader.setMinimumTotalCpuUsageMillis( + parser.getInt( + MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY, + MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT)); } - /** - * Check whether a UID belongs to a set of UIDs - */ + /** Check whether a UID belongs to a set of UIDs */ @VisibleForTesting public static class UidPredicate implements Predicate { private static final Pattern UID_RANGE_PATTERN = Pattern.compile("([0-9]+)-([0-9]+)"); @@ -150,14 +147,14 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { * Create a UID predicate from a string representing a list of UID ranges * *

      UID ranges are a pair of integers separated by a '-'. If you want to specify a single - * UID (e.g. UID 1000), you can use {@code 1000-1000}. Lists of ranges are separated by - * a single ';'. For example, this would be a valid string representation: {@code + * UID (e.g. UID 1000), you can use {@code 1000-1000}. Lists of ranges are separated by a + * single ';'. For example, this would be a valid string representation: {@code * "1000-1999;2003-2003;2004-2004;2050-2060"}. * *

      We do not use ',' to delimit as it is already used in separating different setting * arguments. * - * @throws NumberFormatException if the input string is incorrectly formatted + * @throws NumberFormatException if the input string is incorrectly formatted * @throws IllegalArgumentException if an UID range has a lower end than start */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) @@ -169,9 +166,10 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { throw new NumberFormatException( "Failed to recognize as number range: " + uidSpecifier); } - acceptedUidRanges.add(Range.create( - Integer.parseInt(uidRangeMatcher.group(1)), - Integer.parseInt(uidRangeMatcher.group(2)))); + acceptedUidRanges.add( + Range.create( + Integer.parseInt(uidRangeMatcher.group(1)), + Integer.parseInt(uidRangeMatcher.group(2)))); } return new UidPredicate(acceptedUidRanges); } @@ -181,6 +179,7 @@ public class KernelCpuThreadReaderSettingsObserver extends ContentObserver { } @Override + @SuppressWarnings("ForLoopReplaceableByForEach") public boolean test(Integer uid) { for (int i = 0; i < mAcceptedUidRanges.size(); i++) { if (mAcceptedUidRanges.get(i).contains(uid)) { diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java index 524a5cc353f3a4c712893e4ff65906749c43c45d..b0855f494ffd74537d94c56ceaf527084fea4094 100644 --- a/core/java/com/android/internal/os/RoSystemProperties.java +++ b/core/java/com/android/internal/os/RoSystemProperties.java @@ -59,6 +59,8 @@ public class RoSystemProperties { // ------ ro.fw.* ------------ // public static final boolean FW_SYSTEM_USER_SPLIT = SystemProperties.getBoolean("ro.fw.system_user_split", false); + public static final boolean MULTIUSER_HEADLESS_SYSTEM_USER = + SystemProperties.getBoolean("ro.fw.multiuser.headless_system_user", false); // ------ ro.crypto.* -------- // public static final CryptoProperties.state_values CRYPTO_STATE = diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 992ddd8f1525daf09fc7998d4c44ca732aa36a9a..8ca0bd1df1eb28c9e231d84f630af67faed2c826 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -24,6 +24,7 @@ import android.content.pm.ApplicationInfo; import android.net.Credentials; import android.net.LocalServerSocket; import android.net.LocalSocket; +import android.os.Build; import android.os.FactoryTest; import android.os.IVold; import android.os.Process; @@ -236,7 +237,7 @@ public final class Zygote { public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir, - String packageName, String[] packagesForUID, String sandboxId) { + String packageName, String[] packagesForUID, String sandboxId, int targetSdkVersion) { ZygoteHooks.preFork(); // Resets nice priority for zygote process. resetNicePriority(); @@ -246,6 +247,7 @@ public final class Zygote { packagesForUID, sandboxId); // Enable tracing as soon as possible for the child process. if (pid == 0) { + Zygote.disableExecuteOnly(targetSdkVersion); Trace.setTracingEnabled(true, runtimeFlags); // Note that this event ends at the end of handleChildProc, @@ -514,6 +516,7 @@ public final class Zygote { private static Runnable usapMain(LocalServerSocket usapPoolSocket, FileDescriptor writePipe) { final int pid = Process.myPid(); + Process.setArgV0(Process.is64Bit() ? "usap64" : "usap32"); LocalSocket sessionSocket = null; DataOutputStream usapOutputStream = null; @@ -599,6 +602,8 @@ public final class Zygote { args.mInstructionSet, args.mAppDataDir, args.mPackageName, args.mPackagesForUid, args.mSandboxId); + disableExecuteOnly(args.mTargetSdkVersion); + if (args.mNiceName != null) { Process.setArgV0(args.mNiceName); } @@ -649,6 +654,17 @@ public final class Zygote { } } + /** + * Mark execute-only segments of libraries read+execute for apps with targetSdkVersion T[] cloneOrNull(@Nullable T[] array) { + return (array != null) ? array.clone() : null; + } + public static @Nullable ArraySet cloneOrNull(@Nullable ArraySet array) { return (array != null) ? new ArraySet(array) : null; } diff --git a/core/java/com/android/internal/util/SyncResultReceiver.java b/core/java/com/android/internal/util/SyncResultReceiver.java index 60af5117489b35938ffbd39f6b4f78e8c01b6b6c..00e91017a9ebc7ce6cd104042989bac4c6ed76cf 100644 --- a/core/java/com/android/internal/util/SyncResultReceiver.java +++ b/core/java/com/android/internal/util/SyncResultReceiver.java @@ -19,10 +19,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Bundle; import android.os.Parcelable; -import android.os.RemoteException; import com.android.internal.os.IResultReceiver; +import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -96,6 +96,15 @@ public final class SyncResultReceiver extends IResultReceiver.Stub { return mBundle == null ? null : mBundle.getParcelable(EXTRA); } + /** + * Gets the result from an operation that returns a {@code Parcelable} list. + */ + @Nullable + public

      ArrayList

      getParcelableListResult() throws TimeoutException { + waitResult(); + return mBundle == null ? null : mBundle.getParcelableArrayList(EXTRA); + } + /** * Gets the optional result from an operation that returns an extra {@code int} (besides the * result code). @@ -149,6 +158,17 @@ public final class SyncResultReceiver extends IResultReceiver.Stub { return bundle; } + /** + * Creates a bundle for a {@code Parcelable} list so it can be retrieved by + * {@link #getParcelableResult()}. + */ + @NonNull + public static Bundle bundleFor(@Nullable ArrayList value) { + final Bundle bundle = new Bundle(); + bundle.putParcelableArrayList(EXTRA, value); + return bundle; + } + /** * Creates a bundle for an {@code int} value so it can be retrieved by * {@link #getParcelableResult()} - typically used to return an extra {@code int} (as the 1st @@ -162,7 +182,7 @@ public final class SyncResultReceiver extends IResultReceiver.Stub { } /** @hide */ - public static final class TimeoutException extends RemoteException { + public static final class TimeoutException extends RuntimeException { private TimeoutException(String msg) { super(msg); } diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 9722fcb129f6dfddb4715b10ed0b8ef6637c2ce7..a160b57fe2a19dcfc1c8a9d04418f874d50e1c07 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -868,6 +868,13 @@ public class ResolverDrawerLayout extends ViewGroup { setMeasuredDimension(sourceWidth, heightSize); } + /** + * @return The space reserved by views with 'alwaysShow=true' + */ + public int getUncollapsibleHeight() { + return mUncollapsibleHeight; + } + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int width = getWidth(); diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 561dcad390a9ebbb3e25e65c91644f331b24c809..9fc79cb606e6fc6284eeee7a8494f0ff1c7fb1a0 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -1150,16 +1150,6 @@ public class SystemConfig { XmlUtils.skipCurrentTag(parser); } } - // If the storage model feature flag is disabled, we need to fiddle - // around with permission definitions to return us to pre-Q behavior. - // STOPSHIP(b/112545973): remove once feature enabled by default - if (!StorageManager.hasIsolatedStorage()) { - if (newPermissions.contains(android.Manifest.permission.READ_MEDIA_AUDIO) || - newPermissions.contains(android.Manifest.permission.READ_MEDIA_VIDEO) || - newPermissions.contains(android.Manifest.permission.READ_MEDIA_IMAGES)) { - return; - } - } if (!newPermissions.isEmpty()) { mSplitPermissions.add(new SplitPermissionInfo(splitPerm, newPermissions, targetSdk)); } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 664f7f47cf18166ae7b5f77de69604d42c7e3849..000c0449358a4e3742d01d424207f2e9eab17adf 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -303,6 +303,7 @@ cc_library_shared { "libnativewindow", "libhwui", "libdl", + "libdl_android", "libstatslog", "server_configurable_flags", ], diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index e0b7629013fb4e2614c55793354a2b8cf844582b..a4e37093def0497582ff0a8f01a1f296bb931740 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -39,7 +39,6 @@ static jclass gBitmap_class; static jfieldID gBitmap_nativePtr; static jmethodID gBitmap_constructorMethodID; static jmethodID gBitmap_reinitMethodID; -static jmethodID gBitmap_getAllocationByteCountMethodID; namespace android { @@ -193,11 +192,6 @@ void reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info, info.width(), info.height(), isPremultiplied); } -int getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap) -{ - return env->CallIntMethod(javaBitmap, gBitmap_getAllocationByteCountMethodID); -} - jobject createBitmap(JNIEnv* env, Bitmap* bitmap, int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets, int density) { @@ -236,8 +230,7 @@ Bitmap& toBitmap(JNIEnv* env, jobject bitmap) { return localBitmap->bitmap(); } -Bitmap& toBitmap(JNIEnv* env, jlong bitmapHandle) { - SkASSERT(env); +Bitmap& toBitmap(jlong bitmapHandle) { LocalScopedBitmap localBitmap(bitmapHandle); return localBitmap->bitmap(); } @@ -1227,7 +1220,6 @@ int register_android_graphics_Bitmap(JNIEnv* env) gBitmap_nativePtr = GetFieldIDOrDie(env, gBitmap_class, "mNativePtr", "J"); gBitmap_constructorMethodID = GetMethodIDOrDie(env, gBitmap_class, "", "(JIIIZ[BLandroid/graphics/NinePatch$InsetStruct;Z)V"); gBitmap_reinitMethodID = GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V"); - gBitmap_getAllocationByteCountMethodID = GetMethodIDOrDie(env, gBitmap_class, "getAllocationByteCount", "()I"); return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods, NELEM(gBitmapMethods)); } diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h index 06877915856e392aae0b69c0f4b14d61e9556f10..6934d26cdc78f750a5f355116c13338fda85fc9b 100644 --- a/core/jni/android/graphics/Bitmap.h +++ b/core/jni/android/graphics/Bitmap.h @@ -41,7 +41,7 @@ jobject createBitmap(JNIEnv* env, Bitmap* bitmap, void toSkBitmap(jlong bitmapHandle, SkBitmap* outBitmap); Bitmap& toBitmap(JNIEnv* env, jobject bitmap); -Bitmap& toBitmap(JNIEnv* env, jlong bitmapHandle); +Bitmap& toBitmap(jlong bitmapHandle); // NDK access void imageInfo(JNIEnv* env, jobject bitmap, AndroidBitmapInfo* info); @@ -56,8 +56,6 @@ bool unlockPixels(JNIEnv* env, jobject bitmap); void reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info, bool isPremultiplied); -int getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap); - } // namespace bitmap } // namespace android diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 47b1548edb6763a0cec4d39a27356711552dba8c..3f05c3b57c6991bf90faece23cd1ab5189032658 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -180,7 +180,8 @@ static bool needsFineScale(const SkISize fullSize, const SkISize decodedSize, } static jobject doDecode(JNIEnv* env, std::unique_ptr stream, - jobject padding, jobject options, jlong colorSpaceHandle) { + jobject padding, jobject options, jlong inBitmapHandle, + jlong colorSpaceHandle) { // Set default values for the options parameters. int sampleSize = 1; bool onlyDecodeSize = false; @@ -323,14 +324,14 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr stream, android::Bitmap* reuseBitmap = nullptr; unsigned int existingBufferSize = 0; - if (javaBitmap != NULL) { - reuseBitmap = &bitmap::toBitmap(env, javaBitmap); + if (javaBitmap != nullptr) { + reuseBitmap = &bitmap::toBitmap(inBitmapHandle); if (reuseBitmap->isImmutable()) { ALOGW("Unable to reuse an immutable bitmap as an image decoder target."); - javaBitmap = NULL; + javaBitmap = nullptr; reuseBitmap = nullptr; } else { - existingBufferSize = bitmap::getBitmapAllocationByteCount(env, javaBitmap); + existingBufferSize = reuseBitmap->getAllocationByteCount(); } } @@ -513,7 +514,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr stream, } static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage, - jobject padding, jobject options, jlong colorSpaceHandle) { + jobject padding, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) { jobject bitmap = NULL; std::unique_ptr stream(CreateJavaInputStreamAdaptor(env, is, storage)); @@ -522,13 +523,14 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteA std::unique_ptr bufferedStream( SkFrontBufferedStream::Make(std::move(stream), SkCodec::MinBufferedBytesNeeded())); SkASSERT(bufferedStream.get() != NULL); - bitmap = doDecode(env, std::move(bufferedStream), padding, options, colorSpaceHandle); + bitmap = doDecode(env, std::move(bufferedStream), padding, options, inBitmapHandle, + colorSpaceHandle); } return bitmap; } static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor, - jobject padding, jobject bitmapFactoryOptions, jlong colorSpaceHandle) { + jobject padding, jobject bitmapFactoryOptions, jlong inBitmapHandle, jlong colorSpaceHandle) { NPE_CHECK_RETURN_ZERO(env, fileDescriptor); @@ -565,7 +567,7 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fi if (::lseek(descriptor, 0, SEEK_CUR) == 0) { assert(isSeekable(dupDescriptor)); return doDecode(env, std::move(fileStream), padding, bitmapFactoryOptions, - colorSpaceHandle); + inBitmapHandle, colorSpaceHandle); } // Use a buffered stream. Although an SkFILEStream can be rewound, this @@ -574,25 +576,26 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fi std::unique_ptr stream(SkFrontBufferedStream::Make(std::move(fileStream), SkCodec::MinBufferedBytesNeeded())); - return doDecode(env, std::move(stream), padding, bitmapFactoryOptions, colorSpaceHandle); + return doDecode(env, std::move(stream), padding, bitmapFactoryOptions, inBitmapHandle, + colorSpaceHandle); } static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset, - jobject padding, jobject options, jlong colorSpaceHandle) { + jobject padding, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) { Asset* asset = reinterpret_cast(native_asset); // since we know we'll be done with the asset when we return, we can // just use a simple wrapper return doDecode(env, skstd::make_unique(asset), padding, options, - colorSpaceHandle); + inBitmapHandle, colorSpaceHandle); } static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, - jint offset, jint length, jobject options, jlong colorSpaceHandle) { + jint offset, jint length, jobject options, jlong inBitmapHandle, jlong colorSpaceHandle) { AutoJavaByteArray ar(env, byteArray); return doDecode(env, skstd::make_unique(ar.ptr() + offset, length, false), - nullptr, options, colorSpaceHandle); + nullptr, options, inBitmapHandle, colorSpaceHandle); } static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) { @@ -604,22 +607,22 @@ static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) { static const JNINativeMethod gMethods[] = { { "nativeDecodeStream", - "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;", + "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;", (void*)nativeDecodeStream }, { "nativeDecodeFileDescriptor", - "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;", + "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;", (void*)nativeDecodeFileDescriptor }, { "nativeDecodeAsset", - "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;", + "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;", (void*)nativeDecodeAsset }, { "nativeDecodeByteArray", - "([BIILandroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;", + "([BIILandroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;", (void*)nativeDecodeByteArray }, diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp index 9c07e2d64c6efe8c757e288748ce7b468ba52aa4..6ffa72ad89031d39d0137fac5404e039f0c947a0 100644 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp @@ -125,13 +125,14 @@ static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, * reportSizeToVM not supported */ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX, - jint inputY, jint inputWidth, jint inputHeight, jobject options, jlong colorSpaceHandle) { + jint inputY, jint inputWidth, jint inputHeight, jobject options, jlong inBitmapHandle, + jlong colorSpaceHandle) { // Set default options. int sampleSize = 1; SkColorType colorType = kN32_SkColorType; bool requireUnpremul = false; - jobject javaBitmap = NULL; + jobject javaBitmap = nullptr; bool isHardware = false; sk_sp colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); // Update the default options with any options supplied by the client. @@ -158,11 +159,11 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in android::Bitmap* recycledBitmap = nullptr; size_t recycledBytes = 0; if (javaBitmap) { - recycledBitmap = &bitmap::toBitmap(env, javaBitmap); + recycledBitmap = &bitmap::toBitmap(inBitmapHandle); if (recycledBitmap->isImmutable()) { ALOGW("Warning: Reusing an immutable bitmap as an image decoder target."); } - recycledBytes = bitmap::getBitmapAllocationByteCount(env, javaBitmap); + recycledBytes = recycledBitmap->getAllocationByteCount(); } SkBitmapRegionDecoder* brd = reinterpret_cast(brdHandle); @@ -258,7 +259,7 @@ static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) { static const JNINativeMethod gBitmapRegionDecoderMethods[] = { { "nativeDecodeRegion", - "(JIIIILandroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;", + "(JIIIILandroid/graphics/BitmapFactory$Options;JJ)Landroid/graphics/Bitmap;", (void*)nativeDecodeRegion}, { "nativeGetHeight", "(J)I", (void*)nativeGetHeight}, diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp index bb291e74ce0d3ca587c25a53f06d62d54c71ac0a..15f951688d43c6ee391e70c95ded56a1ab5d5c2b 100644 --- a/core/jni/android/graphics/NinePatch.cpp +++ b/core/jni/android/graphics/NinePatch.cpp @@ -84,13 +84,13 @@ public: delete[] patch; } - static jlong getTransparentRegion(JNIEnv* env, jobject, jobject jbitmap, + static jlong getTransparentRegion(JNIEnv* env, jobject, jlong bitmapPtr, jlong chunkHandle, jobject dstRect) { Res_png_9patch* chunk = reinterpret_cast(chunkHandle); SkASSERT(chunk); SkBitmap bitmap; - GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); + bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); SkRect dst; GraphicsJNI::jrect_to_rect(env, dstRect, &dst); @@ -156,7 +156,7 @@ static const JNINativeMethod gNinePatchMethods[] = { { "validateNinePatchChunk", "([B)J", (void*) SkNinePatchGlue::validateNinePatchChunk }, { "nativeFinalize", "(J)V", (void*) SkNinePatchGlue::finalize }, - { "nativeGetTransparentRegion", "(Landroid/graphics/Bitmap;JLandroid/graphics/Rect;)J", + { "nativeGetTransparentRegion", "(JJLandroid/graphics/Rect;)J", (void*) SkNinePatchGlue::getTransparentRegion } }; diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index 298f7f8f78e54355262259f9e214e817c4813cda..44d2cac8739dafb17738d9a8cf48ca71d0476db5 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -62,14 +62,14 @@ static jlong Shader_getNativeFinalizer(JNIEnv*, jobject) { /////////////////////////////////////////////////////////////////////////////////////////////// -static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jobject jbitmap, +static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle, jint tileModeX, jint tileModeY) { const SkMatrix* matrix = reinterpret_cast(matrixPtr); sk_sp image; - if (jbitmap) { + if (bitmapHandle) { // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise, // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility. - image = android::bitmap::toBitmap(env, jbitmap).makeImage(); + image = android::bitmap::toBitmap(bitmapHandle).makeImage(); } if (!image.get()) { @@ -222,7 +222,7 @@ static const JNINativeMethod gShaderMethods[] = { }; static const JNINativeMethod gBitmapShaderMethods[] = { - { "nativeCreate", "(JLandroid/graphics/Bitmap;II)J", (void*)BitmapShader_constructor }, + { "nativeCreate", "(JJII)J", (void*)BitmapShader_constructor }, }; static const JNINativeMethod gLinearGradientMethods[] = { diff --git a/core/jni/android/graphics/pdf/PdfRenderer.cpp b/core/jni/android/graphics/pdf/PdfRenderer.cpp index 32ac30fdabb05027d63ea088388419d499e3ec9a..761830b0e97c623da18c5307a504f40e13640be8 100644 --- a/core/jni/android/graphics/pdf/PdfRenderer.cpp +++ b/core/jni/android/graphics/pdf/PdfRenderer.cpp @@ -73,12 +73,12 @@ static void nativeClosePage(JNIEnv* env, jclass thiz, jlong pagePtr) { } static void nativeRenderPage(JNIEnv* env, jclass thiz, jlong documentPtr, jlong pagePtr, - jobject jbitmap, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom, + jlong bitmapPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom, jlong transformPtr, jint renderMode) { FPDF_PAGE page = reinterpret_cast(pagePtr); SkBitmap skBitmap; - GraphicsJNI::getSkBitmap(env, jbitmap, &skBitmap); + bitmap::toBitmap(bitmapPtr).getSkBitmap(&skBitmap); const int stride = skBitmap.width() * 4; @@ -117,7 +117,7 @@ static const JNINativeMethod gPdfRenderer_Methods[] = { {"nativeClose", "(J)V", (void*) nativeClose}, {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount}, {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting}, - {"nativeRenderPage", "(JJLandroid/graphics/Bitmap;IIIIJI)V", (void*) nativeRenderPage}, + {"nativeRenderPage", "(JJJIIIIJI)V", (void*) nativeRenderPage}, {"nativeOpenPageAndGetSize", "(JILandroid/graphics/Point;)J", (void*) nativeOpenPageAndGetSize}, {"nativeClosePage", "(J)V", (void*) nativeClosePage} }; diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp index d50e60c1a899dd3e22846e8f68c950539d1beb6b..09f0e8e232bffd280d56c1a6f0d2540c9c5e62f6 100644 --- a/core/jni/android/opengl/util.cpp +++ b/core/jni/android/opengl/util.cpp @@ -703,27 +703,27 @@ static int getType(SkColorType colorType) } static jint util_getInternalFormat(JNIEnv *env, jclass clazz, - jobject jbitmap) + jlong bitmapPtr) { SkBitmap nativeBitmap; - GraphicsJNI::getSkBitmap(env, jbitmap, &nativeBitmap); + bitmap::toBitmap(bitmapPtr).getSkBitmap(&nativeBitmap); return getInternalFormat(nativeBitmap.colorType()); } static jint util_getType(JNIEnv *env, jclass clazz, - jobject jbitmap) + jlong bitmapPtr) { SkBitmap nativeBitmap; - GraphicsJNI::getSkBitmap(env, jbitmap, &nativeBitmap); + bitmap::toBitmap(bitmapPtr).getSkBitmap(&nativeBitmap); return getType(nativeBitmap.colorType()); } static jint util_texImage2D(JNIEnv *env, jclass clazz, jint target, jint level, jint internalformat, - jobject jbitmap, jint type, jint border) + jlong bitmapPtr, jint type, jint border) { SkBitmap bitmap; - GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); + bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); SkColorType colorType = bitmap.colorType(); if (internalformat < 0) { internalformat = getInternalFormat(colorType); @@ -748,10 +748,10 @@ static jint util_texImage2D(JNIEnv *env, jclass clazz, static jint util_texSubImage2D(JNIEnv *env, jclass clazz, jint target, jint level, jint xoffset, jint yoffset, - jobject jbitmap, jint format, jint type) + jlong bitmapPtr, jint format, jint type) { SkBitmap bitmap; - GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); + bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); SkColorType colorType = bitmap.colorType(); int internalFormat = getInternalFormat(colorType); if (format < 0) { @@ -1068,10 +1068,10 @@ static const JNINativeMethod gVisibilityMethods[] = { }; static const JNINativeMethod gUtilsMethods[] = { - { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat }, - { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType }, - { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D }, - { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D }, + { "native_getInternalFormat", "(J)I", (void*) util_getInternalFormat }, + { "native_getType", "(J)I", (void*) util_getType }, + { "native_texImage2D", "(IIIJII)I", (void*)util_texImage2D }, + { "native_texSubImage2D", "(IIIIJII)I", (void*)util_texSubImage2D }, }; static const JNINativeMethod gEtc1Methods[] = { diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp index f40b461a6dfd6721a392e94c8ce41cb1e97d4778..bd4862dfb08de13de7a876a7f25a586699ce64c2 100644 --- a/core/jni/android_content_res_ApkAssets.cpp +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -106,8 +106,7 @@ static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { const ApkAssets* apk_assets = reinterpret_cast(ptr); - (void)apk_assets; - return JNI_TRUE; + return apk_assets->IsUpToDate() ? JNI_TRUE : JNI_FALSE; } static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) { diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp index 9c48c33dc2ed4999c0e7996e148da7e70b9f8c25..7a8c5c8a7b36da225c598d99f76f24dbcf131dd6 100644 --- a/core/jni/android_graphics_Canvas.cpp +++ b/core/jni/android_graphics_Canvas.cpp @@ -54,20 +54,20 @@ static jlong getNativeFinalizer(JNIEnv* env, jobject clazz) { } // Native wrapper constructor used by Canvas(Bitmap) -static jlong initRaster(JNIEnv* env, jobject, jobject jbitmap) { +static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) { SkBitmap bitmap; - if (jbitmap != NULL) { - GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); + if (bitmapHandle != 0) { + bitmap::toBitmap(bitmapHandle).getSkBitmap(&bitmap); } return reinterpret_cast(Canvas::create_canvas(bitmap)); } // Set the given bitmap as the new draw target (wrapped in a new SkCanvas), // optionally copying canvas matrix & clip state. -static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap) { +static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle) { SkBitmap bitmap; - if (jbitmap != NULL) { - GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); + if (bitmapHandle != 0) { + bitmap::toBitmap(bitmapHandle).getSkBitmap(&bitmap); } get_canvas(canvasHandle)->setBitmap(bitmap); } @@ -397,7 +397,7 @@ static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmap jlong paintHandle, jint dstDensity, jint srcDensity) { Canvas* canvas = get_canvas(canvasHandle); - Bitmap& bitmap = android::bitmap::toBitmap(env, bitmapHandle); + Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle); const android::Res_png_9patch* chunk = reinterpret_cast(chunkHandle); const Paint* paint = reinterpret_cast(paintHandle); @@ -423,11 +423,11 @@ static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmap } } -static void drawBitmap(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap, +static void drawBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, jfloat left, jfloat top, jlong paintHandle, jint canvasDensity, jint screenDensity, jint bitmapDensity) { Canvas* canvas = get_canvas(canvasHandle); - Bitmap& bitmap = android::bitmap::toBitmap(env, jbitmap); + Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle); const Paint* paint = reinterpret_cast(paintHandle); if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) { @@ -458,22 +458,22 @@ static void drawBitmap(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap } } -static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap, +static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, jlong matrixHandle, jlong paintHandle) { const SkMatrix* matrix = reinterpret_cast(matrixHandle); const Paint* paint = reinterpret_cast(paintHandle); - Bitmap& bitmap = android::bitmap::toBitmap(env, jbitmap); + Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle); get_canvas(canvasHandle)->drawBitmap(bitmap, *matrix, paint); } -static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap, +static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, jlong paintHandle, jint screenDensity, jint bitmapDensity) { Canvas* canvas = get_canvas(canvasHandle); const Paint* paint = reinterpret_cast(paintHandle); - Bitmap& bitmap = android::bitmap::toBitmap(env, jbitmap); + Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle); if (screenDensity != 0 && screenDensity != bitmapDensity) { Paint filteredPaint; if (paint) { @@ -512,7 +512,7 @@ static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle, get_canvas(canvasHandle)->drawBitmap(*androidBitmap, x, y, paint); } -static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap, +static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, jint meshWidth, jint meshHeight, jfloatArray jverts, jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) { if (Canvas::GetApiLevel() < __ANDROID_API_P__) { @@ -527,7 +527,7 @@ static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jobject jbi AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount); const Paint* paint = reinterpret_cast(paintHandle); - Bitmap& bitmap = android::bitmap::toBitmap(env, jbitmap); + Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle); get_canvas(canvasHandle)->drawBitmapMesh(bitmap, meshWidth, meshHeight, vertA.ptr() + vertIndex*2, colorA.ptr() + colorIndex, paint); @@ -651,13 +651,13 @@ static void setCompatibilityVersion(JNIEnv* env, jobject, jint apiLevel) { static const JNINativeMethod gMethods[] = { {"nGetNativeFinalizer", "()J", (void*) CanvasJNI::getNativeFinalizer}, - {"nInitRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster}, {"nFreeCaches", "()V", (void*) CanvasJNI::freeCaches}, {"nFreeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}, {"nSetCompatibilityVersion", "(I)V", (void*) CanvasJNI::setCompatibilityVersion}, // ------------ @FastNative ---------------- - {"nSetBitmap", "(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap}, + {"nInitRaster", "(J)J", (void*) CanvasJNI::initRaster}, + {"nSetBitmap", "(JJ)V", (void*) CanvasJNI::setBitmap}, {"nGetClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds}, // ------------ @CriticalNative ---------------- @@ -706,10 +706,10 @@ static const JNINativeMethod gDrawMethods[] = { {"nDrawPath","(JJJ)V", (void*) CanvasJNI::drawPath}, {"nDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices}, {"nDrawNinePatch", "(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch}, - {"nDrawBitmapMatrix", "(JLandroid/graphics/Bitmap;JJ)V", (void*)CanvasJNI::drawBitmapMatrix}, - {"nDrawBitmapMesh", "(JLandroid/graphics/Bitmap;II[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh}, - {"nDrawBitmap","(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap}, - {"nDrawBitmap","(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect}, + {"nDrawBitmapMatrix", "(JJJJ)V", (void*)CanvasJNI::drawBitmapMatrix}, + {"nDrawBitmapMesh", "(JJII[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh}, + {"nDrawBitmap","(JJFFJIII)V", (void*) CanvasJNI::drawBitmap}, + {"nDrawBitmap","(JJFFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect}, {"nDrawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray}, {"nDrawText","(J[CIIFFIJ)V", (void*) CanvasJNI::drawTextChars}, {"nDrawText","(JLjava/lang/String;IIFFIJ)V", (void*) CanvasJNI::drawTextString}, diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp index b9301d40d4b8db84e022c923d31862cc9fe92776..71edfd553e7eadce99961699c718abd1d4127859 100644 --- a/core/jni/android_hardware_input_InputApplicationHandle.cpp +++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp @@ -56,30 +56,25 @@ bool NativeInputApplicationHandle::updateInfo() { JNIEnv* env = AndroidRuntime::getJNIEnv(); jobject obj = env->NewLocalRef(mObjWeak); if (!obj) { - releaseInfo(); return false; } - if (!mInfo) { - mInfo = new InputApplicationInfo(); - } - - mInfo->name = getStringField(env, obj, gInputApplicationHandleClassInfo.name, ""); + mInfo.name = getStringField(env, obj, gInputApplicationHandleClassInfo.name, ""); - mInfo->dispatchingTimeout = env->GetLongField(obj, + mInfo.dispatchingTimeout = env->GetLongField(obj, gInputApplicationHandleClassInfo.dispatchingTimeoutNanos); jobject tokenObj = env->GetObjectField(obj, gInputApplicationHandleClassInfo.token); if (tokenObj) { - mInfo->token = ibinderForJavaObject(env, tokenObj); + mInfo.token = ibinderForJavaObject(env, tokenObj); env->DeleteLocalRef(tokenObj); } else { - mInfo->token.clear(); + mInfo.token.clear(); } env->DeleteLocalRef(obj); - return true; + return mInfo.token.get() != nullptr; } diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index c8f81e2193c8640b9f5dda437453287debdc9b71..a296d647657bb6cfe5b3e4f491a381846fdb06c4 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -144,6 +144,7 @@ static struct { static jclass gAudioMixingRuleClass; static struct { jfieldID mCriteria; + jfieldID mAllowPrivilegedPlaybackCapture; // other fields unused by JNI } gAudioMixingRuleFields; @@ -1868,6 +1869,8 @@ static jint convertAudioMixToNative(JNIEnv *env, jobject jRule = env->GetObjectField(jAudioMix, gAudioMixFields.mRule); jobject jRuleCriteria = env->GetObjectField(jRule, gAudioMixingRuleFields.mCriteria); + nAudioMix->mAllowPrivilegedPlaybackCapture = + env->GetBooleanField(jRule, gAudioMixingRuleFields.mAllowPrivilegedPlaybackCapture); env->DeleteLocalRef(jRule); jobjectArray jCriteria = (jobjectArray)env->CallObjectMethod(jRuleCriteria, gArrayListMethods.toArray); @@ -2226,6 +2229,11 @@ android_media_AudioSystem_isHapticPlaybackSupported(JNIEnv *env, jobject thiz) return AudioSystem::isHapticPlaybackSupported(); } +static jint +android_media_AudioSystem_setAllowedCapturePolicy(JNIEnv *env, jobject thiz, jint uid, jint flags) { + return AudioSystem::setAllowedCapturePolicy(uid, flags); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = { @@ -2301,6 +2309,7 @@ static const JNINativeMethod gMethods[] = { {"isHapticPlaybackSupported", "()Z", (void *)android_media_AudioSystem_isHapticPlaybackSupported}, {"getHwOffloadEncodingFormatsSupportedForA2DP", "(Ljava/util/ArrayList;)I", (void*)android_media_AudioSystem_getHwOffloadEncodingFormatsSupportedForA2DP}, + {"setAllowedCapturePolicy", "(II)I", (void *)android_media_AudioSystem_setAllowedCapturePolicy}, }; static const JNINativeMethod gEventHandlerMethods[] = { @@ -2456,6 +2465,8 @@ int register_android_media_AudioSystem(JNIEnv *env) gAudioMixingRuleClass = MakeGlobalRefOrDie(env, audioMixingRuleClass); gAudioMixingRuleFields.mCriteria = GetFieldIDOrDie(env, audioMixingRuleClass, "mCriteria", "Ljava/util/ArrayList;"); + gAudioMixingRuleFields.mAllowPrivilegedPlaybackCapture = + GetFieldIDOrDie(env, audioMixingRuleClass, "mAllowPrivilegedPlaybackCapture", "Z"); jclass audioMixMatchCriterionClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMixingRule$AudioMixMatchCriterion"); diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index d7a981ed3e9d00956e7a97bf1d975af517600da0..82acf6fb432bef4e87cae3519159f7ff0303ff74 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -470,6 +470,7 @@ static jbyteArray android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, std::vector buf(MAXPACKETSIZE, 0); int res = resNetworkResult(fd, &rcode, buf.data(), MAXPACKETSIZE); + jniSetFileDescriptorOfFD(env, javaFd, -1); if (res < 0) { throwErrnoException(env, "resNetworkResult", -res); return nullptr; @@ -490,6 +491,7 @@ static jbyteArray android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) { int fd = jniGetFDFromFileDescriptor(env, javaFd); resNetworkCancel(fd); + jniSetFileDescriptorOfFD(env, javaFd, -1); } static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) { diff --git a/core/jni/android_view_CompositionSamplingListener.cpp b/core/jni/android_view_CompositionSamplingListener.cpp index 7141e6e141763829fd43b4b1e37cf80d7779e9c1..1c885e39cde1f197fb89e94c1ace651ce585ac59 100644 --- a/core/jni/android_view_CompositionSamplingListener.cpp +++ b/core/jni/android_view_CompositionSamplingListener.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "CompositionSamplingListener" #include "android_util_Binder.h" +#include "core_jni_helpers.h" #include @@ -122,6 +123,7 @@ int register_android_view_CompositionSamplingListener(JNIEnv* env) { LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); jclass clazz = env->FindClass("android/view/CompositionSamplingListener"); + gListenerClassInfo.mClass = MakeGlobalRefOrDie(env, clazz); gListenerClassInfo.mDispatchOnSampleCollected = env->GetStaticMethodID( clazz, "dispatchOnSampleCollected", "(Landroid/view/CompositionSamplingListener;F)V"); return 0; diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index ecc2dd0d3598aec5b20fd87531309719b674a5fb..e7cbf938b128d9345e688ff63257946a8f31625f 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -736,11 +736,11 @@ static void android_view_ThreadedRenderer_buildLayer(JNIEnv* env, jobject clazz, } static jboolean android_view_ThreadedRenderer_copyLayerInto(JNIEnv* env, jobject clazz, - jlong proxyPtr, jlong layerPtr, jobject jbitmap) { + jlong proxyPtr, jlong layerPtr, jlong bitmapPtr) { RenderProxy* proxy = reinterpret_cast(proxyPtr); DeferredLayerUpdater* layer = reinterpret_cast(layerPtr); SkBitmap bitmap; - GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); + bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); return proxy->copyLayerInto(layer, bitmap); } @@ -911,9 +911,9 @@ static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env, static jint android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env, jobject clazz, jobject jsurface, jint left, jint top, - jint right, jint bottom, jobject jbitmap) { + jint right, jint bottom, jlong bitmapPtr) { SkBitmap bitmap; - GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); + bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); sp surface = android_view_Surface_getSurface(env, jsurface); return RenderProxy::copySurfaceInto(surface, left, top, right, bottom, &bitmap); } @@ -1106,7 +1106,7 @@ static const JNINativeMethod gMethods[] = { { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor }, { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer }, { "nBuildLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_buildLayer }, - { "nCopyLayerInto", "(JJLandroid/graphics/Bitmap;)Z", (void*) android_view_ThreadedRenderer_copyLayerInto }, + { "nCopyLayerInto", "(JJJ)Z", (void*) android_view_ThreadedRenderer_copyLayerInto }, { "nPushLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_pushLayerUpdate }, { "nCancelLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_cancelLayerUpdate }, { "nDetachSurfaceTexture", "(JJ)V", (void*) android_view_ThreadedRenderer_detachSurfaceTexture }, @@ -1135,7 +1135,7 @@ static const JNINativeMethod gMethods[] = { { "nRemoveFrameMetricsObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_removeFrameMetricsObserver }, - { "nCopySurfaceInto", "(Landroid/view/Surface;IIIILandroid/graphics/Bitmap;)I", + { "nCopySurfaceInto", "(Landroid/view/Surface;IIIIJ)I", (void*)android_view_ThreadedRenderer_copySurfaceInto }, { "nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;", (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode }, diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index cde188439a10eb47b0fe483957dc08158807eeb3..70b343624ea97725116fab4f9342e072a736985f 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +71,7 @@ #include #include #include +#include #include #include #include @@ -1975,6 +1978,26 @@ static void com_android_internal_os_Zygote_nativeEmptyUsapPool(JNIEnv* env, jcla } } +static int disable_execute_only(struct dl_phdr_info *info, size_t size, void *data) { + // Search for any execute-only segments and mark them read+execute. + for (int i = 0; i < info->dlpi_phnum; i++) { + if ((info->dlpi_phdr[i].p_type == PT_LOAD) && (info->dlpi_phdr[i].p_flags == PF_X)) { + mprotect(reinterpret_cast(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr), + info->dlpi_phdr[i].p_memsz, PROT_READ | PROT_EXEC); + } + } + // Return non-zero to exit dl_iterate_phdr. + return 0; +} + +/** + * @param env Managed runtime environment + * @return True if disable was successful. + */ +static jboolean com_android_internal_os_Zygote_nativeDisableExecuteOnly(JNIEnv* env, jclass) { + return dl_iterate_phdr(disable_execute_only, nullptr) == 0; +} + static const JNINativeMethod gMethods[] = { { "nativeSecurityInit", "()V", (void *) com_android_internal_os_Zygote_nativeSecurityInit }, @@ -2007,7 +2030,9 @@ static const JNINativeMethod gMethods[] = { { "nativeGetUsapPoolCount", "()I", (void *) com_android_internal_os_Zygote_nativeGetUsapPoolCount }, { "nativeEmptyUsapPool", "()V", - (void *) com_android_internal_os_Zygote_nativeEmptyUsapPool } + (void *) com_android_internal_os_Zygote_nativeEmptyUsapPool }, + { "nativeDisableExecuteOnly", "()Z", + (void *) com_android_internal_os_Zygote_nativeDisableExecuteOnly } }; int register_com_android_internal_os_Zygote(JNIEnv* env) { diff --git a/core/proto/android/app/alarmmanager.proto b/core/proto/android/app/alarmmanager.proto index 58df9225bb7eddf74c1ab052ba748fc784df382b..fe276363d71a51579be37e520644c0d904dd1cd5 100644 --- a/core/proto/android/app/alarmmanager.proto +++ b/core/proto/android/app/alarmmanager.proto @@ -17,7 +17,7 @@ syntax = "proto2"; import "frameworks/base/core/proto/android/app/pendingintent.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; diff --git a/core/proto/android/app/notification.proto b/core/proto/android/app/notification.proto index a6f13d76b9f61115f7529aee8373151479eaee51..bf0b35245ec5a46a6c9c7065802e32834e4725f4 100644 --- a/core/proto/android/app/notification.proto +++ b/core/proto/android/app/notification.proto @@ -19,7 +19,7 @@ option java_multiple_files = true; package android.app; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; /** * An android.app.Notification object. diff --git a/core/proto/android/app/notification_channel.proto b/core/proto/android/app/notification_channel.proto index 435d32f59a3564905e273f5221703d146829334b..c835b90ec9695d57991c2bb584d7d6c596c26566 100644 --- a/core/proto/android/app/notification_channel.proto +++ b/core/proto/android/app/notification_channel.proto @@ -20,7 +20,7 @@ option java_multiple_files = true; package android.app; import "frameworks/base/core/proto/android/media/audioattributes.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; /** * An android.app.NotificationChannel object. diff --git a/core/proto/android/app/notification_channel_group.proto b/core/proto/android/app/notification_channel_group.proto index 6d6ceb2f7cbedf6e31718a68f888168d81f24ddc..c064bb124eb8fdd9e1d173864eb64d93e08f8574 100644 --- a/core/proto/android/app/notification_channel_group.proto +++ b/core/proto/android/app/notification_channel_group.proto @@ -20,7 +20,7 @@ option java_multiple_files = true; package android.app; import "frameworks/base/core/proto/android/app/notification_channel.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; /** * An android.app.NotificationChannelGroup object. diff --git a/core/proto/android/app/notificationmanager.proto b/core/proto/android/app/notificationmanager.proto index 27204ccae0d9977ffdcaa57a3952f351c2e953f6..b88315e2dbeee119b8b666cd108c584849ab4fa3 100644 --- a/core/proto/android/app/notificationmanager.proto +++ b/core/proto/android/app/notificationmanager.proto @@ -19,7 +19,7 @@ option java_multiple_files = true; package android.app; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; /** * An android.app.NotificationManager.Policy object. diff --git a/core/proto/android/app/pendingintent.proto b/core/proto/android/app/pendingintent.proto index 04ce8507cd637642bd3aff336a8eaa1ef5a64cae..b8e61a3fd0d4339373369c5c5a64c7270affb557 100644 --- a/core/proto/android/app/pendingintent.proto +++ b/core/proto/android/app/pendingintent.proto @@ -20,7 +20,7 @@ option java_multiple_files = true; package android.app; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; /** * An android.app.PendingIntent object. diff --git a/core/proto/android/app/profilerinfo.proto b/core/proto/android/app/profilerinfo.proto index 20fa3adbf5b92b2f345950d3651bde2dc2af3bf4..d318533b77e5c76c1248149f26a92534f8182c8f 100644 --- a/core/proto/android/app/profilerinfo.proto +++ b/core/proto/android/app/profilerinfo.proto @@ -17,7 +17,7 @@ syntax = "proto2"; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package android.app; diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index b4be3f59960449884525f84a755cc71fc860aad2..d0295274dbe44d823169ac8f404c70a77ed51102 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2319,4 +2319,16 @@ enum PageId { // Settings > Display > Theme DARK_UI_SETTINGS = 1698; + + // Settings > global bubble settings + BUBBLE_SETTINGS = 1699; + + // Settings > app > bubble settings + APP_BUBBLE_SETTINGS = 1700; + + // OPEN: Settings > System > Aware > Info dialog + DIALOG_AWARE_STATUS = 1701; + + // Open: Settings > app > bubble settings > confirmation dialog + DIALOG_APP_BUBBLE_SETTINGS = 1702; } diff --git a/core/proto/android/app/window_configuration.proto b/core/proto/android/app/window_configuration.proto index 6cc1a40de8736ec4f4c3dee381b314294c2d5eec..18439da1a56fe4f628d35b4bdfeb1a7503b2a54f 100644 --- a/core/proto/android/app/window_configuration.proto +++ b/core/proto/android/app/window_configuration.proto @@ -20,7 +20,7 @@ option java_multiple_files = true; package android.app; import "frameworks/base/core/proto/android/graphics/rect.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; /** Proto representation for WindowConfiguration.java class. */ message WindowConfigurationProto { diff --git a/core/proto/android/content/clipdata.proto b/core/proto/android/content/clipdata.proto index 4f1c308f0981590c0dd3072056273b3980da1581..72bbb2ab045cf14d462fbc42efcf2af532b8eca5 100644 --- a/core/proto/android/content/clipdata.proto +++ b/core/proto/android/content/clipdata.proto @@ -21,7 +21,7 @@ option java_multiple_files = true; import "frameworks/base/core/proto/android/content/clipdescription.proto"; import "frameworks/base/core/proto/android/content/intent.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; // An android.content.ClipData object. message ClipDataProto { diff --git a/core/proto/android/content/clipdescription.proto b/core/proto/android/content/clipdescription.proto index bc0e9407faf8a01b0d2dc3404893d48333cace26..71455632fadbed103d12251dc7b3148171645c9f 100644 --- a/core/proto/android/content/clipdescription.proto +++ b/core/proto/android/content/clipdescription.proto @@ -20,7 +20,7 @@ package android.content; option java_multiple_files = true; import "frameworks/base/core/proto/android/os/persistablebundle.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; // An android.content.ClipDescription object. message ClipDescriptionProto { diff --git a/core/proto/android/content/component_name.proto b/core/proto/android/content/component_name.proto index 232d685843355d0d8f46d0c01594173dcd69bfcd..5cd0b05a4d3786e31726a4da9b712956dbd17476 100644 --- a/core/proto/android/content/component_name.proto +++ b/core/proto/android/content/component_name.proto @@ -19,7 +19,7 @@ option java_multiple_files = true; package android.content; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; /** * An android.content.ComponentName object. diff --git a/core/proto/android/content/configuration.proto b/core/proto/android/content/configuration.proto index 06f9735c3deaa5b3acb9644dcb5594cb06249cbe..57ced09240f27189fc830edfa5e29c996dcee87d 100644 --- a/core/proto/android/content/configuration.proto +++ b/core/proto/android/content/configuration.proto @@ -21,7 +21,7 @@ package android.content; import "frameworks/base/core/proto/android/app/window_configuration.proto"; import "frameworks/base/core/proto/android/content/locale.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; /** * An android Configuration object. diff --git a/core/proto/android/content/featureinfo.proto b/core/proto/android/content/featureinfo.proto index 87bf404c6245a6f2a1083a71c6ff084db467edff..1473ba97c9beea80d8d132d087185102e33850d0 100644 --- a/core/proto/android/content/featureinfo.proto +++ b/core/proto/android/content/featureinfo.proto @@ -16,7 +16,7 @@ syntax = "proto2"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; diff --git a/core/proto/android/content/intent.proto b/core/proto/android/content/intent.proto index 99ed6875075e4f9e7553b096411c5e3e17144217..2de538d358c46e0e08945449c5f76872f891806b 100644 --- a/core/proto/android/content/intent.proto +++ b/core/proto/android/content/intent.proto @@ -21,7 +21,7 @@ option java_multiple_files = true; import "frameworks/base/core/proto/android/content/component_name.proto"; import "frameworks/base/core/proto/android/os/patternmatcher.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; // Next Tag: 13 message IntentProto { diff --git a/core/proto/android/content/locale.proto b/core/proto/android/content/locale.proto index 86743bf6b91cb8eb42a92dd8b78d373444831bc0..d8af754dc2ea7350fb39c55cd39c20185f8002d9 100644 --- a/core/proto/android/content/locale.proto +++ b/core/proto/android/content/locale.proto @@ -17,7 +17,7 @@ syntax = "proto2"; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package android.content; diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto index ebb2fa62a3516531fbb6e835159306367e09e283..4a7d04361a5b058c9f95d0c31b9aa950e919046b 100644 --- a/core/proto/android/content/package_item_info.proto +++ b/core/proto/android/content/package_item_info.proto @@ -17,7 +17,7 @@ syntax = "proto2"; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package android.content.pm; diff --git a/core/proto/android/graphics/point.proto b/core/proto/android/graphics/point.proto index 04d879fd405c9805ff468b51b33ecd4f7314fd07..8180a061e2daae65756cc9323512c8c8a9de8095 100644 --- a/core/proto/android/graphics/point.proto +++ b/core/proto/android/graphics/point.proto @@ -17,7 +17,7 @@ syntax = "proto2"; package android.graphics; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; diff --git a/core/proto/android/graphics/rect.proto b/core/proto/android/graphics/rect.proto index c216b2bdbda126d3ddfe5ce4896ece1e59c9ae78..0bb0494406d9cb7dedef9a08ec0d458311fb195d 100644 --- a/core/proto/android/graphics/rect.proto +++ b/core/proto/android/graphics/rect.proto @@ -17,7 +17,7 @@ syntax = "proto2"; package android.graphics; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; diff --git a/core/proto/android/internal/locallog.proto b/core/proto/android/internal/locallog.proto index df0b90b058cda5cb35c4309253c11c99839f20b1..ecf76a1063d2c258d5cb762f72583160b5ec708e 100644 --- a/core/proto/android/internal/locallog.proto +++ b/core/proto/android/internal/locallog.proto @@ -19,7 +19,7 @@ package com.android.internal.util; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message LocalLogProto { option (.android.msg_privacy).dest = DEST_EXPLICIT; diff --git a/core/proto/android/media/audioattributes.proto b/core/proto/android/media/audioattributes.proto index d679d9c24f73715567be712aa4b36ef80f7bd429..288b55518bf3b2868c11e24071703e5ab69a3d40 100644 --- a/core/proto/android/media/audioattributes.proto +++ b/core/proto/android/media/audioattributes.proto @@ -19,7 +19,7 @@ option java_multiple_files = true; package android.media; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; /** * An android.media.AudioAttributes object. diff --git a/core/proto/android/net/OWNERS b/core/proto/android/net/OWNERS index a845dcbec661ff822b9b1f8c3c183b876b58c3fa..509699b9fb4bbd885fd94f8c0c9e779751191188 100644 --- a/core/proto/android/net/OWNERS +++ b/core/proto/android/net/OWNERS @@ -1,5 +1,3 @@ -set noparent - ek@google.com lorenzo@google.com satk@google.com diff --git a/core/proto/android/net/network.proto b/core/proto/android/net/network.proto index e13ca9f682ebdd3e342fa1b29288c0ec3dce6d91..ca9ae61ab58290ed301b2727b2ed44775133c14d 100644 --- a/core/proto/android/net/network.proto +++ b/core/proto/android/net/network.proto @@ -19,7 +19,7 @@ option java_multiple_files = true; package android.net; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; /** * An android.net.Network object. diff --git a/core/proto/android/net/networkcapabilities.proto b/core/proto/android/net/networkcapabilities.proto index 0338bf8f37b68c02a127af29557ce1ef0e0122ad..be0cad18a24df426259e795bca74a48d2305490e 100644 --- a/core/proto/android/net/networkcapabilities.proto +++ b/core/proto/android/net/networkcapabilities.proto @@ -20,7 +20,7 @@ package android.net; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; /** * An android.net.NetworkCapabilities object. diff --git a/core/proto/android/net/networkrequest.proto b/core/proto/android/net/networkrequest.proto index d260b13a29ebcb8e6f2ff1f1cdd4272a21760999..b35a0203ff02225af56ff7af4ef8691096810963 100644 --- a/core/proto/android/net/networkrequest.proto +++ b/core/proto/android/net/networkrequest.proto @@ -21,7 +21,7 @@ package android.net; option java_multiple_files = true; import "frameworks/base/core/proto/android/net/networkcapabilities.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; /** * An android.net.NetworkRequest object. diff --git a/core/proto/android/os/backtrace.proto b/core/proto/android/os/backtrace.proto index 8bbae179f5201e9922d78f3ec0993d209ebbc331..31ff241cb8464bf6e2bda4500fcc5c22907aa128 100644 --- a/core/proto/android/os/backtrace.proto +++ b/core/proto/android/os/backtrace.proto @@ -19,7 +19,7 @@ package android.os; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message BackTraceProto { option (android.msg_privacy).dest = DEST_EXPLICIT; diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto index 516fa7b9336bc4af8f0bb775ec5fc6cc6baf4826..892ebf70ca75b665ca05e19bf54f1d914cb1054d 100644 --- a/core/proto/android/os/batterystats.proto +++ b/core/proto/android/os/batterystats.proto @@ -22,7 +22,7 @@ package android.os; import "frameworks/base/core/proto/android/app/job/enums.proto"; import "frameworks/base/core/proto/android/os/powermanager.proto"; import "frameworks/base/core/proto/android/telephony/enums.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message BatteryStatsProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/os/batterytype.proto b/core/proto/android/os/batterytype.proto index 2388c1edea7e4f1f0abea1267970c676f5b7c409..82e194fdf335b830a6385cfc5b7c18f404c4f636 100644 --- a/core/proto/android/os/batterytype.proto +++ b/core/proto/android/os/batterytype.proto @@ -20,7 +20,7 @@ package android.os; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message BatteryTypeProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/os/bundle.proto b/core/proto/android/os/bundle.proto index 5556936c3c78e0dbb71d1b32f7b9e1318eff25bd..dc593296204225fc7be32c77326a9f8a3bcb3ed6 100644 --- a/core/proto/android/os/bundle.proto +++ b/core/proto/android/os/bundle.proto @@ -19,7 +19,7 @@ package android.os; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; // An android.os.Bundle object. message BundleProto { diff --git a/core/proto/android/os/cpufreq.proto b/core/proto/android/os/cpufreq.proto index 46f4901d8a297e09a2f49bb38477942c110704fd..b86da1a52195b27f7cb02b7212ea544bcb16f1bf 100644 --- a/core/proto/android/os/cpufreq.proto +++ b/core/proto/android/os/cpufreq.proto @@ -17,7 +17,7 @@ syntax = "proto2"; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package android.os; diff --git a/core/proto/android/os/cpuinfo.proto b/core/proto/android/os/cpuinfo.proto index ce69fc9d4037916be059182b1ce87e57af0ee583..7477db4e8a66b6a5e63eaebb988acea3cfe8c020 100644 --- a/core/proto/android/os/cpuinfo.proto +++ b/core/proto/android/os/cpuinfo.proto @@ -17,7 +17,7 @@ syntax = "proto2"; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package android.os; diff --git a/libs/incident/proto/android/os/header.proto b/core/proto/android/os/header.proto similarity index 100% rename from libs/incident/proto/android/os/header.proto rename to core/proto/android/os/header.proto diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index a5350c9dc1b81668c27534936665458f4f3d92e5..9a9c9d14154b9ae4689aee0c375cfd5b01cce175 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -22,7 +22,9 @@ import "frameworks/base/core/proto/android/os/batterytype.proto"; import "frameworks/base/core/proto/android/os/cpufreq.proto"; import "frameworks/base/core/proto/android/os/cpuinfo.proto"; import "frameworks/base/core/proto/android/os/data.proto"; +import "frameworks/base/core/proto/android/os/header.proto"; import "frameworks/base/core/proto/android/os/kernelwake.proto"; +import "frameworks/base/core/proto/android/os/metadata.proto"; import "frameworks/base/core/proto/android/os/pagetypeinfo.proto"; import "frameworks/base/core/proto/android/os/procrank.proto"; import "frameworks/base/core/proto/android/os/ps.proto"; @@ -46,13 +48,12 @@ import "frameworks/base/core/proto/android/service/notification.proto"; import "frameworks/base/core/proto/android/service/package.proto"; import "frameworks/base/core/proto/android/service/print.proto"; import "frameworks/base/core/proto/android/service/procstats.proto"; +import "frameworks/base/core/proto/android/service/restricted_image.proto"; import "frameworks/base/core/proto/android/service/usb.proto"; import "frameworks/base/core/proto/android/util/event_log_tags.proto"; import "frameworks/base/core/proto/android/util/log.proto"; -import "frameworks/base/libs/incident/proto/android/os/header.proto"; -import "frameworks/base/libs/incident/proto/android/os/metadata.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; -import "frameworks/base/libs/incident/proto/android/section.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/section.proto"; package android.os; @@ -314,6 +315,12 @@ message IncidentProto { (section).args = "role --proto" ]; + optional android.service.restricted_image.RestrictedImagesDumpProto restricted_images = 3025 [ + (section).type = SECTION_DUMPSYS, + (section).userdebug_and_eng_only = true, + (section).args = "incidentcompanion --restricted_image" + ]; + // Reserved for OEMs. extensions 50000 to 100000; } diff --git a/core/proto/android/os/kernelwake.proto b/core/proto/android/os/kernelwake.proto index 5021a06b176924e987f12fb14d9f015be974031c..885e62dac5231f2ef9d167ee78f13899c5e44847 100644 --- a/core/proto/android/os/kernelwake.proto +++ b/core/proto/android/os/kernelwake.proto @@ -17,7 +17,7 @@ syntax = "proto2"; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package android.os; diff --git a/core/proto/android/os/looper.proto b/core/proto/android/os/looper.proto index b9b8cf58450273bbaec2e87ef0cb8b34bdf3c86e..d1ef7bc40b83d7bfa5b5c5cc125819f886587f46 100644 --- a/core/proto/android/os/looper.proto +++ b/core/proto/android/os/looper.proto @@ -20,7 +20,7 @@ package android.os; option java_multiple_files = true; import "frameworks/base/core/proto/android/os/messagequeue.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message LooperProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/os/message.proto b/core/proto/android/os/message.proto index 8aaec7094f6e5a6ea0846b829f7accbf24d7349e..9b48b67fc125fcb46525624e731568513b396749 100644 --- a/core/proto/android/os/message.proto +++ b/core/proto/android/os/message.proto @@ -17,7 +17,7 @@ syntax = "proto2"; package android.os; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; message MessageProto { diff --git a/core/proto/android/os/messagequeue.proto b/core/proto/android/os/messagequeue.proto index 61bbee709fd8ccaf060205b01ca4b96414c9e6ad..9800865622dd4d76306f62716a17559d5c5b1bed 100644 --- a/core/proto/android/os/messagequeue.proto +++ b/core/proto/android/os/messagequeue.proto @@ -20,7 +20,7 @@ package android.os; option java_multiple_files = true; import "frameworks/base/core/proto/android/os/message.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message MessageQueueProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/libs/incident/proto/android/os/metadata.proto b/core/proto/android/os/metadata.proto similarity index 95% rename from libs/incident/proto/android/os/metadata.proto rename to core/proto/android/os/metadata.proto index 3b0e9c9aa17a0362ab7b37b3727b32213d06aad5..6242b27bc5c9a8a0f17922e0fcde6e815f21444d 100644 --- a/libs/incident/proto/android/os/metadata.proto +++ b/core/proto/android/os/metadata.proto @@ -27,7 +27,7 @@ message IncidentMetadata { // The id of the incident report. optional int64 report_id = 1; - // The sequence number of the report. + // No longer filled in as of Qt. optional int32 sequence_number = 2; // privacy level of the incident report. @@ -38,8 +38,10 @@ message IncidentMetadata { } optional Destination dest = 3; + // No longer filled in as of Qt. optional int32 request_size = 4; + // No longer filled in as of Qt. optional bool use_dropbox = 5; // stats of each section taken in this incident report. diff --git a/core/proto/android/os/pagetypeinfo.proto b/core/proto/android/os/pagetypeinfo.proto index 65e713900ae70f64ad5e0dd1db29893b6eba0c7f..c795e2edfc2045be84def865715a7727b060a414 100644 --- a/core/proto/android/os/pagetypeinfo.proto +++ b/core/proto/android/os/pagetypeinfo.proto @@ -17,7 +17,7 @@ syntax = "proto2"; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package android.os; diff --git a/core/proto/android/os/patternmatcher.proto b/core/proto/android/os/patternmatcher.proto index 520f2f5b8542a93a15dfa44367b86737216c237b..ab2010db582966cca054bc9cd5c59dd44f7b2b33 100644 --- a/core/proto/android/os/patternmatcher.proto +++ b/core/proto/android/os/patternmatcher.proto @@ -16,7 +16,7 @@ syntax = "proto2"; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package android.os; diff --git a/core/proto/android/os/persistablebundle.proto b/core/proto/android/os/persistablebundle.proto index 712f87c9b1473c8c8fa761f7def5d7d68a7aa365..785250c77de23317adf50c7ad243628020cb42a6 100644 --- a/core/proto/android/os/persistablebundle.proto +++ b/core/proto/android/os/persistablebundle.proto @@ -19,7 +19,7 @@ package android.os; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; // An android.os.PersistableBundle object. message PersistableBundleProto { diff --git a/core/proto/android/os/powermanager.proto b/core/proto/android/os/powermanager.proto index 20b0a7446ae8b37683ec04c3a286a9aa46cc13ab..52b092c34052d95c6c879656572e19670c8e943c 100644 --- a/core/proto/android/os/powermanager.proto +++ b/core/proto/android/os/powermanager.proto @@ -20,7 +20,7 @@ package android.os; option java_multiple_files = true; import "frameworks/base/core/proto/android/os/worksource.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message PowerManagerProto { /* User activity events in PowerManager.java. */ diff --git a/core/proto/android/os/procrank.proto b/core/proto/android/os/procrank.proto index f7edaf4aef20f724b32ae840f296047be73d43d8..62f75bb9ac8404b57103b8f281782b90e1b77769 100644 --- a/core/proto/android/os/procrank.proto +++ b/core/proto/android/os/procrank.proto @@ -19,7 +19,7 @@ package android.os; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; // Memory usage of running processes message ProcrankProto { diff --git a/core/proto/android/os/ps.proto b/core/proto/android/os/ps.proto index e032b574607f7962cb9330481e3b1fe2f2ca5996..993bfb64c674474b5eb5f5eed672f9ca639d97de 100644 --- a/core/proto/android/os/ps.proto +++ b/core/proto/android/os/ps.proto @@ -20,7 +20,7 @@ package android.os; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message PsProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/os/statsdata.proto b/core/proto/android/os/statsdata.proto index 25d76b869259035a29c7cdda547e318b1ac7ce1c..b89b606d0f268433ab8106497c68a404293e0ea8 100644 --- a/core/proto/android/os/statsdata.proto +++ b/core/proto/android/os/statsdata.proto @@ -19,7 +19,7 @@ option java_multiple_files = true; package android.os; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; // Dump of statsd report data (dumpsys stats --proto). message StatsDataDumpProto { diff --git a/core/proto/android/os/system_properties.proto b/core/proto/android/os/system_properties.proto index 1f63be93fb94063262c2ac433d0843b45367bd4d..d06e1a6c1ccd46847ece8a0120d1aef880b4a097 100644 --- a/core/proto/android/os/system_properties.proto +++ b/core/proto/android/os/system_properties.proto @@ -18,7 +18,7 @@ syntax = "proto2"; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package android.os; diff --git a/core/proto/android/os/worksource.proto b/core/proto/android/os/worksource.proto index 0a9c2ed6e2591d0682ef494e9053214f3961ed80..1763d446848e1c3dbc0ec81544a8a22dca30d333 100644 --- a/core/proto/android/os/worksource.proto +++ b/core/proto/android/os/worksource.proto @@ -17,7 +17,7 @@ syntax = "proto2"; package android.os; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; diff --git a/libs/incident/proto/android/privacy.proto b/core/proto/android/privacy.proto similarity index 100% rename from libs/incident/proto/android/privacy.proto rename to core/proto/android/privacy.proto diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto index 76a3b5d7f06ab5e65703967d80a9d9eac87507bc..e43b6a04b93cc544329817fb7528fd597df34c6a 100644 --- a/core/proto/android/providers/settings.proto +++ b/core/proto/android/providers/settings.proto @@ -23,7 +23,7 @@ option java_outer_classname = "SettingsServiceProto"; import "frameworks/base/core/proto/android/providers/settings/global.proto"; import "frameworks/base/core/proto/android/providers/settings/secure.proto"; import "frameworks/base/core/proto/android/providers/settings/system.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message SettingsServiceDumpProto { option (android.msg_privacy).dest = DEST_EXPLICIT; diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index 62df6e73deea0522e2d847110fa2757edc287cdb..d124feb2436e095c3c1d06ebe39caba746463eb0 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -20,7 +20,7 @@ package android.providers.settings; option java_multiple_files = true; import "frameworks/base/core/proto/android/providers/settings/common.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; // Note: it's a conscious decision to add each setting as a separate field. This // allows annotating each setting with its own privacy tag. diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 27a18ee9e52a5335b22b69c71f8d90cca0694750..91d5bc8e45f23f828470b83c45ce5b4b8af2edf6 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -20,7 +20,7 @@ package android.providers.settings; option java_multiple_files = true; import "frameworks/base/core/proto/android/providers/settings/common.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; // Note: it's a conscious decision to add each setting as a separate field. This // allows annotating each setting with its own privacy tag. diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto index 41a74982585df16499afec1db4df74f9937293d8..f8143de8121fe563a76ca12d2d6ac553625e30a4 100644 --- a/core/proto/android/providers/settings/system.proto +++ b/core/proto/android/providers/settings/system.proto @@ -20,7 +20,7 @@ package android.providers.settings; option java_multiple_files = true; import "frameworks/base/core/proto/android/providers/settings/common.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; // Note: it's a conscious decision to add each setting as a separate field. This // allows annotating each setting with its own privacy tag. diff --git a/libs/incident/proto/android/section.proto b/core/proto/android/section.proto similarity index 100% rename from libs/incident/proto/android/section.proto rename to core/proto/android/section.proto diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index 79a5dd78ffb39e3dec63a50525563fa128a8c636..a7d4734f43b9651dff8a978cbdcc049065367e1e 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -34,7 +34,7 @@ import "frameworks/base/core/proto/android/os/powermanager.proto"; import "frameworks/base/core/proto/android/server/intentresolver.proto"; import "frameworks/base/core/proto/android/server/windowmanagerservice.proto"; import "frameworks/base/core/proto/android/util/common.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; @@ -756,6 +756,7 @@ message ActivityManagerServiceDumpProcessesProto { optional string file = 2 [ (.android.privacy).dest = DEST_EXPLICIT ]; optional int32 pid = 3; optional int32 uid = 4; + optional bool is_user_initiated = 5; } optional Dump dump = 2; } diff --git a/core/proto/android/server/alarmmanagerservice.proto b/core/proto/android/server/alarmmanagerservice.proto index b74f28d38fa8652f101738a1b885c8241ce2ee74..490f7295c851348f116d462ba025f4ccf2d72458 100644 --- a/core/proto/android/server/alarmmanagerservice.proto +++ b/core/proto/android/server/alarmmanagerservice.proto @@ -21,7 +21,7 @@ import "frameworks/base/core/proto/android/app/pendingintent.proto"; import "frameworks/base/core/proto/android/internal/locallog.proto"; import "frameworks/base/core/proto/android/os/worksource.proto"; import "frameworks/base/core/proto/android/server/forceappstandbytracker.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package com.android.server; diff --git a/core/proto/android/server/animationadapter.proto b/core/proto/android/server/animationadapter.proto index 0bcc4888710151971417be7f7a48762f58d7b1c7..70627edf2cb37bd1a794a6c3b04567e6c8bcf0c0 100644 --- a/core/proto/android/server/animationadapter.proto +++ b/core/proto/android/server/animationadapter.proto @@ -18,7 +18,7 @@ syntax = "proto2"; import "frameworks/base/core/proto/android/graphics/point.proto"; import "frameworks/base/core/proto/android/view/remote_animation_target.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package com.android.server.wm; option java_multiple_files = true; diff --git a/core/proto/android/server/appwindowthumbnail.proto b/core/proto/android/server/appwindowthumbnail.proto index a1be7218893cc384f77257b1727b2f0df4dac9c7..f22cdc5411581f1b338f7a52645787e7efcac2e7 100644 --- a/core/proto/android/server/appwindowthumbnail.proto +++ b/core/proto/android/server/appwindowthumbnail.proto @@ -17,7 +17,7 @@ syntax = "proto2"; import "frameworks/base/core/proto/android/server/surfaceanimator.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package com.android.server.wm; option java_multiple_files = true; diff --git a/core/proto/android/server/face.proto b/core/proto/android/server/face.proto index 6ecf3289b588d1ef6becfcfb0bac53f24ad477fe..8b77586d3ff5308544efc61d4b951439472ac770 100644 --- a/core/proto/android/server/face.proto +++ b/core/proto/android/server/face.proto @@ -17,7 +17,7 @@ syntax = "proto2"; package com.android.server.biometrics.face; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; option java_outer_classname = "FaceServiceProto"; diff --git a/core/proto/android/server/fingerprint.proto b/core/proto/android/server/fingerprint.proto index c5eb85c5d17b212ef5ebe0d8789c349c483f6efa..a264f18f921cb1fbf1182565b0823c5a58609f85 100644 --- a/core/proto/android/server/fingerprint.proto +++ b/core/proto/android/server/fingerprint.proto @@ -17,7 +17,7 @@ syntax = "proto2"; package com.android.server.biometrics.fingerprint; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; option java_outer_classname = "FingerprintServiceProto"; diff --git a/core/proto/android/server/forceappstandbytracker.proto b/core/proto/android/server/forceappstandbytracker.proto index 54f30c3b91060fe50d1239379eb1592a58fec169..89424bc9017cb3056e31d97f37737c8fed571e37 100644 --- a/core/proto/android/server/forceappstandbytracker.proto +++ b/core/proto/android/server/forceappstandbytracker.proto @@ -17,7 +17,7 @@ syntax = "proto2"; import "frameworks/base/core/proto/android/server/statlogger.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package com.android.server; diff --git a/core/proto/android/server/intentresolver.proto b/core/proto/android/server/intentresolver.proto index e67723e5abfad56bd8a3ab8cdd76758a43a098a0..7ac50e0c0970301107018b3890f97deb66789818 100644 --- a/core/proto/android/server/intentresolver.proto +++ b/core/proto/android/server/intentresolver.proto @@ -19,7 +19,7 @@ option java_multiple_files = true; package com.android.server; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message IntentResolverProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto index 6c9d13a572eeecd0a3b9ddf375b46daf355df7dc..1e0b0d84e04dc641d1f8cdc231bc1b527220e100 100644 --- a/core/proto/android/server/jobscheduler.proto +++ b/core/proto/android/server/jobscheduler.proto @@ -30,7 +30,7 @@ import "frameworks/base/core/proto/android/os/bundle.proto"; import "frameworks/base/core/proto/android/os/persistablebundle.proto"; import "frameworks/base/core/proto/android/server/forceappstandbytracker.proto"; import "frameworks/base/core/proto/android/server/job/enums.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; // Next tag: 21 message JobSchedulerServiceDumpProto { diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto index 9bf1825a8da63e4816c9fdbe3c1dbfe984a550b4..091e1c2bb05a04466bf1cecf1b1da73efcbc516e 100644 --- a/core/proto/android/server/powermanagerservice.proto +++ b/core/proto/android/server/powermanagerservice.proto @@ -28,7 +28,7 @@ import "frameworks/base/core/proto/android/os/worksource.proto"; import "frameworks/base/core/proto/android/providers/settings.proto"; import "frameworks/base/core/proto/android/server/wirelesschargerdetector.proto"; import "frameworks/base/core/proto/android/view/enums.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message PowerManagerServiceDumpProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/server/rolemanagerservice.proto b/core/proto/android/server/rolemanagerservice.proto index 3453a6649e02b8fe0f5522ebeec059084b472b14..146522ce9c8b471d97b1e63c5835012a117ce96c 100644 --- a/core/proto/android/server/rolemanagerservice.proto +++ b/core/proto/android/server/rolemanagerservice.proto @@ -20,7 +20,7 @@ package com.android.server.role; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message RoleManagerServiceDumpProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/server/statlogger.proto b/core/proto/android/server/statlogger.proto index 65b1af79136bc7374bca27f5a1e7ecf2b3ab1632..8593da806dae6e471cc86aa55a8f5c92f4604a9d 100644 --- a/core/proto/android/server/statlogger.proto +++ b/core/proto/android/server/statlogger.proto @@ -20,7 +20,7 @@ package com.android.server; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; // Dump from StatLogger. message StatLoggerProto { diff --git a/core/proto/android/server/surfaceanimator.proto b/core/proto/android/server/surfaceanimator.proto index e3e8baae77edff1092326e53c7096df314d0fb62..15f47140bec7c2f4f09ab5b5574dabe7e9a808c8 100644 --- a/core/proto/android/server/surfaceanimator.proto +++ b/core/proto/android/server/surfaceanimator.proto @@ -18,7 +18,7 @@ syntax = "proto2"; import "frameworks/base/core/proto/android/server/animationadapter.proto"; import "frameworks/base/core/proto/android/view/surfacecontrol.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package com.android.server.wm; option java_multiple_files = true; diff --git a/core/proto/android/server/usagestatsservice.proto b/core/proto/android/server/usagestatsservice.proto index 050ec7a0a95fcc230eb5bea5709bfe4bd6f8f44e..f26eefad24e18e649dc6310b776e8a06bd1dbc67 100644 --- a/core/proto/android/server/usagestatsservice.proto +++ b/core/proto/android/server/usagestatsservice.proto @@ -17,7 +17,7 @@ syntax = "proto2"; package com.android.server.usage; import "frameworks/base/core/proto/android/content/configuration.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index 3767ed5531812772ec2e3b1420f24101e4ded27d..dbd219141e3e25898073799e3179055b17f1bc52 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -27,7 +27,7 @@ import "frameworks/base/core/proto/android/view/displayinfo.proto"; import "frameworks/base/core/proto/android/view/enums.proto"; import "frameworks/base/core/proto/android/view/surface.proto"; import "frameworks/base/core/proto/android/view/windowlayoutparams.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package com.android.server.wm; diff --git a/core/proto/android/server/wirelesschargerdetector.proto b/core/proto/android/server/wirelesschargerdetector.proto index 2118deb6edeb0d42e85fde428ef0cbc1a4ab933e..1c98fb9c4b762d40873316992b0867b2e7e7372b 100644 --- a/core/proto/android/server/wirelesschargerdetector.proto +++ b/core/proto/android/server/wirelesschargerdetector.proto @@ -19,7 +19,7 @@ package com.android.server.power; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message WirelessChargerDetectorProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/service/adb.proto b/core/proto/android/service/adb.proto index 006081399f64a1b0ed231844900d259061fe2d8c..493f9b87d3abbfe2fdf467fe9ea78ff89ad99f12 100644 --- a/core/proto/android/service/adb.proto +++ b/core/proto/android/service/adb.proto @@ -20,7 +20,7 @@ package android.service.adb; option java_multiple_files = true; option java_outer_classname = "AdbServiceProto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message AdbServiceDumpProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/service/battery.proto b/core/proto/android/service/battery.proto index 34cb2292fc5f53db6a837ae9233fc88c2ef08b57..586411f8ad962b59ed793532c4ed887248a84995 100644 --- a/core/proto/android/service/battery.proto +++ b/core/proto/android/service/battery.proto @@ -21,7 +21,7 @@ option java_multiple_files = true; option java_outer_classname = "BatteryServiceProto"; import "frameworks/base/core/proto/android/os/enums.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message BatteryServiceDumpProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/service/batterystats.proto b/core/proto/android/service/batterystats.proto index 25b47d3f88f2b86fad7b7b6aa7eda1b04be0a2f4..3ae45caf1bde219388e6daff6c400159b0011763 100644 --- a/core/proto/android/service/batterystats.proto +++ b/core/proto/android/service/batterystats.proto @@ -21,7 +21,7 @@ option java_multiple_files = true; option java_outer_classname = "BatteryStatsServiceProto"; import "frameworks/base/core/proto/android/os/batterystats.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; // Dump of batterystats aggregate data (dumpsys batterystats --proto). message BatteryStatsServiceDumpProto { diff --git a/core/proto/android/service/diskstats.proto b/core/proto/android/service/diskstats.proto index 1012eb0b17a67ea3118b3efd08f2a23fdbb3a48b..f79de394a50fb360d767b31f6ee864b978425392 100644 --- a/core/proto/android/service/diskstats.proto +++ b/core/proto/android/service/diskstats.proto @@ -17,7 +17,7 @@ syntax = "proto2"; package android.service.diskstats; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; option java_outer_classname = "DiskStatsServiceProto"; diff --git a/core/proto/android/service/graphicsstats.proto b/core/proto/android/service/graphicsstats.proto index bb32495e1d303dd452071c27208f9ab196667cd4..11f046748b3248a4023fbaa3ecccb6438eb2d3ad 100644 --- a/core/proto/android/service/graphicsstats.proto +++ b/core/proto/android/service/graphicsstats.proto @@ -20,7 +20,7 @@ package android.service; option java_multiple_files = true; option java_outer_classname = "GraphicsStatsServiceProto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; // This file is based on frameworks/base/libs/hwui/protos/graphicsstats.proto. // Please try to keep the two files in sync. diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto index 02d44838e1bcd1dac74d2b141b22ee1acd532176..8ebb4a9f6649644cff1d446adc1a32379bd6220b 100644 --- a/core/proto/android/service/netstats.proto +++ b/core/proto/android/service/netstats.proto @@ -17,7 +17,7 @@ syntax = "proto2"; package android.service; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; option java_outer_classname = "NetworkStatsServiceProto"; diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto index 4ef26dd5924010579c04c6c7b71bdb46cad38e34..1ec05fb5e9fc2e0d98b47e7b560cad8a2302350a 100644 --- a/core/proto/android/service/notification.proto +++ b/core/proto/android/service/notification.proto @@ -25,7 +25,7 @@ import "frameworks/base/core/proto/android/app/notification_channel_group.proto" import "frameworks/base/core/proto/android/app/notificationmanager.proto"; import "frameworks/base/core/proto/android/content/component_name.proto"; import "frameworks/base/core/proto/android/media/audioattributes.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message NotificationServiceDumpProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto index 7f96d701cdbf623c35c646bb8382ec30a3373725..6ffa0c943037b5f289cbd07d458f50af4ed9fa35 100644 --- a/core/proto/android/service/package.proto +++ b/core/proto/android/service/package.proto @@ -18,7 +18,7 @@ syntax = "proto2"; package android.service.pm; import "frameworks/base/core/proto/android/content/featureinfo.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; option java_outer_classname = "PackageServiceProto"; diff --git a/core/proto/android/service/print.proto b/core/proto/android/service/print.proto index a44915618b0468ad37d278f2070d7d46b8c06c94..abf1b29a210d30e3e3f70c1dc9fd3b3f855d50da 100644 --- a/core/proto/android/service/print.proto +++ b/core/proto/android/service/print.proto @@ -21,7 +21,7 @@ option java_multiple_files = true; option java_outer_classname = "PrintServiceProto"; import "frameworks/base/core/proto/android/content/component_name.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message PrintServiceDumpProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto index da801ffcc99a0d880bb0359f3fecf4dbf35ef13b..f49a04422c0e7e0c623ef117970c9ae844ac6436 100644 --- a/core/proto/android/service/procstats.proto +++ b/core/proto/android/service/procstats.proto @@ -22,7 +22,7 @@ option java_outer_classname = "ProcessStatsServiceProto"; import "frameworks/base/core/proto/android/util/common.proto"; import "frameworks/base/core/proto/android/service/procstats_enum.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; /** * Data from ProcStatsService Dumpsys diff --git a/core/proto/android/service/restricted_image.proto b/core/proto/android/service/restricted_image.proto new file mode 100644 index 0000000000000000000000000000000000000000..4a33d478fbde4ae062f264456665e184e53233b6 --- /dev/null +++ b/core/proto/android/service/restricted_image.proto @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package android.service.restricted_image; + +option java_multiple_files = true; +option java_outer_classname = "RestrictedImage"; + +import "frameworks/base/core/proto/android/privacy.proto"; + +// Restricted Image proto is for collecting images from the user with their +// permission for the purpose of debugging photos. +message RestrictedImagesDumpProto { + option (android.msg_privacy).dest = DEST_EXPLICIT; + + repeated RestrictedImageSetProto sets = 1; +} + +message RestrictedImageSetProto { + option (android.msg_privacy).dest = DEST_EXPLICIT; + + // Name of the service producing the data. + optional string category = 1; + + // The images + repeated RestrictedImageProto images = 2; + + // Additional metadata + optional bytes metadata = 3; +} + +message RestrictedImageProto { + option (android.msg_privacy).dest = DEST_EXPLICIT; + + // Type of image data + optional string mime_type = 1; + + // The image data + optional bytes image_data = 2; + + // Metadata about the image. Typically this has another proto schema, + // but it is undefined exactly what that is in AOSP code. + optional bytes metadata = 3; +} diff --git a/core/proto/android/service/runtime.proto b/core/proto/android/service/runtime.proto index ecbccef7a94cb2d61343ea03bb847320584614da..440264d5a5cf355e7b2cd6794e48d46f1788d954 100644 --- a/core/proto/android/service/runtime.proto +++ b/core/proto/android/service/runtime.proto @@ -17,7 +17,7 @@ syntax = "proto2"; package android.service.runtime; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; option java_outer_classname = "RuntimeServiceProto"; diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto index 367c54086ade1abf2764e98e043033e2675b2630..2e1de79b199f70c127d1467eb0800f8454a3408c 100644 --- a/core/proto/android/service/usb.proto +++ b/core/proto/android/service/usb.proto @@ -22,7 +22,7 @@ option java_outer_classname = "UsbServiceProto"; import "frameworks/base/core/proto/android/content/component_name.proto"; import "frameworks/base/core/proto/android/service/enums.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; message UsbServiceDumpProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; diff --git a/core/proto/android/stats/connectivity/resolv_stats.proto b/core/proto/android/stats/connectivity/resolv_stats.proto new file mode 100644 index 0000000000000000000000000000000000000000..43eb67397fa573710403157b479038d055488373 --- /dev/null +++ b/core/proto/android/stats/connectivity/resolv_stats.proto @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.stats.connectivity; +import "frameworks/base/core/proto/android/net/networkcapabilities.proto"; + +enum EventType { + EVENT_UNKNOWN = 0; + EVENT_GETADDRINFO = 1; + EVENT_GETHOSTBYNAME = 2; + EVENT_GETHOSTBYADDR = 3; + EVENT_RES_NSEND = 4; +} + +enum PrivateDnsModes { + OFF = 0; + OPPORTUNISTIC = 1; + STRICT = 2; +} +// The return value of the DNS resolver for each DNS lookups. +// bionic/libc/include/netdb.h +// system/netd/resolv/include/netd_resolv/resolv.h +enum ReturnCode { + RC_EAI_NO_ERROR = 0; + RC_EAI_ADDRFAMILY = 1; + RC_EAI_AGAIN = 2; + RC_EAI_BADFLAGS = 3; + RC_EAI_FAIL = 4; + RC_EAI_FAMILY = 5; + RC_EAI_MEMORY = 6; + RC_EAI_NODATA = 7; + RC_EAI_NONAME = 8; + RC_EAI_SERVICE = 9; + RC_EAI_SOCKTYPE = 10; + RC_EAI_SYSTEM = 11; + RC_EAI_BADHINTS = 12; + RC_EAI_PROTOCOL = 13; + RC_EAI_OVERFLOW = 14; + RC_RESOLV_TIMEOUT = 255; + RC_EAI_MAX = 256; +} + + +enum NsRcode { + ns_r_noerror = 0; // No error occurred. + ns_r_formerr = 1; // Format error. + ns_r_servfail = 2; // Server failure. + ns_r_nxdomain = 3; // Name error. + ns_r_notimpl = 4; // Unimplemented. + ns_r_refused = 5; // Operation refused. + // these are for BIND_UPDATE + ns_r_yxdomain = 6; // Name exists + ns_r_yxrrset = 7; // RRset exists + ns_r_nxrrset = 8; // RRset does not exist + ns_r_notauth = 9; // Not authoritative for zone + ns_r_notzone = 10; // Zone of record different from zone section + ns_r_max = 11; + // The following are EDNS extended rcodes + ns_r_badvers = 16; + // The following are TSIG errors + //ns_r_badsig = 16, + ns_r_badkey = 17; + ns_r_badtime = 18; +} + +// Currently defined type values for resources and queries. +enum NsType { + ns_t_invalid = 0; // Cookie. + ns_t_a = 1; // Host address. + ns_t_ns = 2; // Authoritative server. + ns_t_md = 3; // Mail destination. + ns_t_mf = 4; // Mail forwarder. + ns_t_cname = 5; // Canonical name. + ns_t_soa = 6; // Start of authority zone. + ns_t_mb = 7; // Mailbox domain name. + ns_t_mg = 8; // Mail group member. + ns_t_mr = 9; // Mail rename name. + ns_t_null = 10; // Null resource record. + ns_t_wks = 11; // Well known service. + ns_t_ptr = 12; // Domain name pointer. + ns_t_hinfo = 13; // Host information. + ns_t_minfo = 14; // Mailbox information. + ns_t_mx = 15; // Mail routing information. + ns_t_txt = 16; // Text strings. + ns_t_rp = 17; // Responsible person. + ns_t_afsdb = 18; // AFS cell database. + ns_t_x25 = 19; // X_25 calling address. + ns_t_isdn = 20; // ISDN calling address. + ns_t_rt = 21; // Router. + ns_t_nsap = 22; // NSAP address. + ns_t_nsap_ptr = 23; // Reverse NSAP lookup (deprecated). + ns_t_sig = 24; // Security signature. + ns_t_key = 25; // Security key. + ns_t_px = 26; // X.400 mail mapping. + ns_t_gpos = 27; // Geographical position (withdrawn). + ns_t_aaaa = 28; // IPv6 Address. + ns_t_loc = 29; // Location Information. + ns_t_nxt = 30; // Next domain (security). + ns_t_eid = 31; // Endpoint identifier. + ns_t_nimloc = 32; // Nimrod Locator. + ns_t_srv = 33; // Server Selection. + ns_t_atma = 34; // ATM Address + ns_t_naptr = 35; // Naming Authority PoinTeR + ns_t_kx = 36; // Key Exchange + ns_t_cert = 37; // Certification record + ns_t_a6 = 38; // IPv6 address (experimental) + ns_t_dname = 39; // Non-terminal DNAME + ns_t_sink = 40; // Kitchen sink (experimentatl) + ns_t_opt = 41; // EDNS0 option (meta-RR) + ns_t_apl = 42; // Address prefix list (RFC 3123) + ns_t_ds = 43; // Delegation Signer + ns_t_sshfp = 44; // SSH Fingerprint + ns_t_ipseckey = 45; // IPSEC Key + ns_t_rrsig = 46; // RRset Signature + ns_t_nsec = 47; // Negative security + ns_t_dnskey = 48; // DNS Key + ns_t_dhcid = 49; // Dynamic host configuratin identifier + ns_t_nsec3 = 50; // Negative security type 3 + ns_t_nsec3param = 51;// Negative security type 3 parameters + ns_t_hip = 55; // Host Identity Protocol + ns_t_spf = 99; // Sender Policy Framework + ns_t_tkey = 249; // Transaction key + ns_t_tsig = 250; // Transaction signature. + ns_t_ixfr = 251; // Incremental zone transfer. + ns_t_axfr = 252; // Transfer zone of authority. + ns_t_mailb = 253; // Transfer mailbox records. + ns_t_maila = 254; // Transfer mail agent records. + ns_t_any = 255; // Wildcard match. + ns_t_zxfr = 256; // BIND-specific, nonstandard. + ns_t_dlv = 32769; // DNSSEC look-aside validatation. + ns_t_max = 65536; +} + +enum IpVersion { + IPV4 = 0; + IPV6 = 1; + MIXED = 2; +} + +enum TransportType { + UDP = 0; + TCP = 1; + DOT = 2; + DOT_UDP = 3; + DOT_TCP = 4; +} + +message DnsQueryEvent { + optional NsRcode rrcode = 1; + optional NsType rrtype = 2; + optional bool cache_hit = 3; + optional IpVersion ipversion = 4; + optional TransportType transport = 5; + optional int32 packet_retransmits = 6; // Used only by the UDP transport + optional int32 reconnects = 7; // Used only by TCP and DOT + optional int32 latency_micros = 8; + optional int32 active_experiments = 9; + optional android.net.NetworkCapabilitiesProto.Transport network_type = 10; +} + +message DnsQueryEventRe { + repeated DnsQueryEvent dns_query_event = 1; +} + + +message DnsCallEvent { + +} + diff --git a/core/proto/android/util/common.proto b/core/proto/android/util/common.proto index f8f78851d74a587ec0d44a37e395f5ddb8faacdb..aad24d140395cf9a49fc08255ad99bfcc1f07df8 100644 --- a/core/proto/android/util/common.proto +++ b/core/proto/android/util/common.proto @@ -17,7 +17,7 @@ syntax = "proto2"; package android.util; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; diff --git a/core/proto/android/util/event_log_tags.proto b/core/proto/android/util/event_log_tags.proto index 457219fde835bf71dd0d7a99a7aee19269a1380e..40bab9e16a01c2678e347611e26b40f84dee5ed3 100644 --- a/core/proto/android/util/event_log_tags.proto +++ b/core/proto/android/util/event_log_tags.proto @@ -19,7 +19,7 @@ package android.util; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; // Proto representation of event.logtags. // Usually sit in /system/etc/event-log-tags. diff --git a/core/proto/android/util/log.proto b/core/proto/android/util/log.proto index 416c055fe4016aebfd989ad5c302ecf90ad31bac..09870ae55cfea783d117397b47624eb020ea5b27 100644 --- a/core/proto/android/util/log.proto +++ b/core/proto/android/util/log.proto @@ -17,7 +17,7 @@ syntax = "proto2"; package android.util; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; diff --git a/core/proto/android/view/displaycutout.proto b/core/proto/android/view/displaycutout.proto index 0a3310148145c769fd0db60317a5a262e4fa99da..ff98e99b4725c8c7251be3e8c050d4aacc42b0ee 100644 --- a/core/proto/android/view/displaycutout.proto +++ b/core/proto/android/view/displaycutout.proto @@ -17,7 +17,7 @@ syntax = "proto2"; import "frameworks/base/core/proto/android/graphics/rect.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package android.view; option java_multiple_files = true; diff --git a/core/proto/android/view/displayinfo.proto b/core/proto/android/view/displayinfo.proto index 29757fc073f77fcac27832c2b831faad047e78eb..49c0a290c36878002e03c870b9877c929df3a65d 100644 --- a/core/proto/android/view/displayinfo.proto +++ b/core/proto/android/view/displayinfo.proto @@ -17,7 +17,7 @@ syntax = "proto2"; package android.view; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; option java_multiple_files = true; diff --git a/core/proto/android/view/remote_animation_target.proto b/core/proto/android/view/remote_animation_target.proto index 808c5143fe8ef393e53659e6968af215ceb8b30b..24d27858bd20c35f1d9a186bff77be8c5c683647 100644 --- a/core/proto/android/view/remote_animation_target.proto +++ b/core/proto/android/view/remote_animation_target.proto @@ -23,7 +23,7 @@ import "frameworks/base/core/proto/android/app/window_configuration.proto"; import "frameworks/base/core/proto/android/graphics/point.proto"; import "frameworks/base/core/proto/android/graphics/rect.proto"; import "frameworks/base/core/proto/android/view/surfacecontrol.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; /** Proto representation for android.view.RemoteAnimationTarget.java class. */ message RemoteAnimationTargetProto { diff --git a/core/proto/android/view/surfacecontrol.proto b/core/proto/android/view/surfacecontrol.proto index 8a252beeae590bcd4fcb93a01e7b69d9bd230c36..cbb243ba7872f1f7a1cc2f0984120d4ddddc1d0e 100644 --- a/core/proto/android/view/surfacecontrol.proto +++ b/core/proto/android/view/surfacecontrol.proto @@ -19,7 +19,7 @@ package android.view; option java_multiple_files = true; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; /** * Represents a {@link android.view.SurfaceControl} object. diff --git a/core/proto/android/view/windowlayoutparams.proto b/core/proto/android/view/windowlayoutparams.proto index 8a011e9067d881ee45c5cb107e380a7a742a8f80..075ebcfc5e86b678ca4b7206504d1acf455ddf10 100644 --- a/core/proto/android/view/windowlayoutparams.proto +++ b/core/proto/android/view/windowlayoutparams.proto @@ -18,7 +18,7 @@ syntax = "proto2"; import "frameworks/base/core/proto/android/graphics/pixelformat.proto"; import "frameworks/base/core/proto/android/view/display.proto"; -import "frameworks/base/libs/incident/proto/android/privacy.proto"; +import "frameworks/base/core/proto/android/privacy.proto"; package android.view; option java_multiple_files = true; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 8bfa038c8e8f8b510050f2811823119c93a7f94a..3788f479a1a4e35ff0027ec13121dbf011edbd58 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -116,6 +116,7 @@ + @@ -266,6 +267,7 @@ + @@ -627,6 +629,9 @@ + + @@ -784,8 +789,7 @@ - + {@code targetSdkVersion} is 4 or higher. - @deprecated replaced by new strongly-typed permission groups in Q. --> - - - - - - - - - - - - - - - Protection level: dangerous + + @deprecated Applications should use {@link android.telecom.CallRedirectionService} instead + of the {@link android.content.Intent#ACTION_NEW_OUTGOING_CALL} broadcast. --> + + + + + + + + + + + + diff --git a/core/res/res/drawable/ic_bluetooth_share_icon.xml b/core/res/res/drawable/ic_bluetooth_share_icon.xml new file mode 100644 index 0000000000000000000000000000000000000000..2446402be93f3b3d5e5e5c3d90cf68e0507206cb --- /dev/null +++ b/core/res/res/drawable/ic_bluetooth_share_icon.xml @@ -0,0 +1,27 @@ + + + + + + \ No newline at end of file diff --git a/core/res/res/drawable/ic_drag_handle.xml b/core/res/res/drawable/ic_drag_handle.xml index 67ab84d4080d4de7d514b758847fc2d0c1401b74..9b0e204608ff117b63db9cf45dcfe12391c30740 100644 --- a/core/res/res/drawable/ic_drag_handle.xml +++ b/core/res/res/drawable/ic_drag_handle.xml @@ -13,11 +13,9 @@ See the License for the specific language governing permissions and limitations under the License. --> - - - \ No newline at end of file + + + + diff --git a/packages/SystemUI/res/drawable/ic_signal_airplane.xml b/core/res/res/drawable/ic_qs_airplane.xml similarity index 76% rename from packages/SystemUI/res/drawable/ic_signal_airplane.xml rename to core/res/res/drawable/ic_qs_airplane.xml index f708ed9cb8a68f0cf46cf00749f87b01a1841991..166d415a6dcc5960710713556433b88a45fbf7f5 100644 --- a/packages/SystemUI/res/drawable/ic_signal_airplane.xml +++ b/core/res/res/drawable/ic_qs_airplane.xml @@ -15,12 +15,11 @@ limitations under the License. --> - - + diff --git a/packages/SystemUI/res/drawable/ic_qs_auto_rotate.xml b/core/res/res/drawable/ic_qs_auto_rotate.xml similarity index 100% rename from packages/SystemUI/res/drawable/ic_qs_auto_rotate.xml rename to core/res/res/drawable/ic_qs_auto_rotate.xml diff --git a/core/res/res/drawable/ic_qs_bluetooth.xml b/core/res/res/drawable/ic_qs_bluetooth.xml new file mode 100644 index 0000000000000000000000000000000000000000..91fcff0dfab64e29343e438cc12ff19ed7518b18 --- /dev/null +++ b/core/res/res/drawable/ic_qs_bluetooth.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_dnd.xml b/core/res/res/drawable/ic_qs_dnd.xml similarity index 61% rename from packages/SystemUI/res/drawable/ic_dnd.xml rename to core/res/res/drawable/ic_qs_dnd.xml index 09a6aabf40bbad3ecae8c45120e51b15156604ec..b361169ce1af0d7b5257ddc5ecc8470534273309 100644 --- a/packages/SystemUI/res/drawable/ic_dnd.xml +++ b/core/res/res/drawable/ic_qs_dnd.xml @@ -14,17 +14,12 @@ limitations under the License. --> + android:viewportWidth="24.0" > - - + android:fillColor="@android:color/white" + android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM7,11h10v2L7,13z"/> diff --git a/packages/SystemUI/res/drawable/ic_signal_flashlight.xml b/core/res/res/drawable/ic_qs_flashlight.xml similarity index 100% rename from packages/SystemUI/res/drawable/ic_signal_flashlight.xml rename to core/res/res/drawable/ic_qs_flashlight.xml diff --git a/core/res/res/drawable/ic_settings_bluetooth.xml b/core/res/res/drawable/ic_settings_bluetooth.xml index 6e32e1a7f631e8e2f4c9182afe02ee1e6d939c85..91fcff0dfab64e29343e438cc12ff19ed7518b18 100644 --- a/core/res/res/drawable/ic_settings_bluetooth.xml +++ b/core/res/res/drawable/ic_settings_bluetooth.xml @@ -14,12 +14,12 @@ limitations under the License. --> - + android:fillColor="@android:color/white" + android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88z"/> + \ No newline at end of file diff --git a/core/res/res/drawable/perm_group_aural.xml b/core/res/res/drawable/perm_group_aural.xml index 0465e98c71c0f92461ddbc7df8a533a60f0ffb6c..b2737f24b86e6705d7c712df4523fff1815cf546 100644 --- a/core/res/res/drawable/perm_group_aural.xml +++ b/core/res/res/drawable/perm_group_aural.xml @@ -19,6 +19,12 @@ Copyright (C) 2015 The Android Open Source Project android:viewportWidth="24.0" android:viewportHeight="24.0"> + android:fillColor="#000000" + android:pathData="M20,2H8C6.9,2,6,2.9,6,4v12c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2z M20,16H8V4h12V16z" /> + + diff --git a/core/res/res/drawable/perm_group_visual.xml b/core/res/res/drawable/perm_group_visual.xml index bf9a0b102f05d1ae679042b5a194a61fc556b0a9..9b21c279e30a478b7516fdaca1992244deec2da0 100644 --- a/core/res/res/drawable/perm_group_visual.xml +++ b/core/res/res/drawable/perm_group_visual.xml @@ -19,6 +19,8 @@ Copyright (C) 2015 The Android Open Source Project android:viewportWidth="24.0" android:viewportHeight="24.0"> + android:fillColor="#000000" + android:pathData="M20,4v12H8V4H20 M20,2H8C6.9,2,6,2.9,6,4v12c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V4C22,2.9,21.1,2,20,2L20,2z M2,6v14 c0,1.1,0.9,2,2,2h14v-2H4V6H2z M15.67,11l-2.5,2.98L11.5,11.8L9,15h10L15.67,11z" /> + diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index 2860ee478db881609d8928017325706af1306229..1f8041741b46ef5f0d8ff9a878ec97b1f528b8af 100644 --- a/core/res/res/layout/chooser_grid.xml +++ b/core/res/res/layout/chooser_grid.xml @@ -20,7 +20,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:maxCollapsedHeight="288dp" + android:maxCollapsedHeight="0dp" android:maxCollapsedHeightSmall="56dp" android:id="@id/contentPanel"> @@ -32,12 +32,12 @@ @@ -62,8 +62,8 @@ android:textAppearance="?attr/textAppearanceMedium" android:textSize="20sp" android:gravity="center" - android:paddingTop="18dp" - android:paddingBottom="18dp" + android:paddingTop="@dimen/chooser_view_spacing" + android:paddingBottom="@dimen/chooser_view_spacing" android:paddingLeft="24dp" android:paddingRight="24dp" android:layout_below="@id/profile_button" diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml index 4a3dfba63c6d906c1fc5c6ce41f24f4cad148cbf..7065149e268e0bebb1286a4469cdc631926c6748 100644 --- a/core/res/res/layout/resolve_grid_item.xml +++ b/core/res/res/layout/resolve_grid_item.xml @@ -22,46 +22,43 @@ android:layout_height="wrap_content" android:minHeight="100dp" android:gravity="center" - android:paddingTop="8dp" + android:paddingTop="24dp" android:paddingBottom="8dp" + android:paddingLeft="2dp" + android:paddingRight="2dp" android:focusable="true" android:background="?attr/selectableItemBackgroundBorderless"> - + + + + - + android:lines="1" + android:ellipsize="end" /> + + + android:ellipsize="end"/> + diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 78c0b801dc625946496d72a785036e690e5d254c..bb8f3d6a6811b10fce787dccede6733361c1d677 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -141,8 +141,10 @@ "Wi-Fi-oproepe" "VoWifi" "Af" - "Verkies Wi-Fi" - "Verkies mobiel" + + + + "Net Wi-Fi" "{0}: Nie aangestuur nie" "{0}: {1}" @@ -228,7 +230,8 @@ "Foutverslag" "Beëindig sessie" "Skermkiekie" - "Neem foutverslag" + + "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." "Interaktiewe verslag" "Gebruik dit in die meeste gevalle. Maak dit vir jou moontlik om die vordering van die verslag na te spoor, meer besonderhede oor die probleem in te voer en skermkiekies te neem. Dit sal dalk sommige afdelings wat minder gebruik word en waarvoor verslagdoening lank duur, weglaat." @@ -281,9 +284,12 @@ "Ligging" "toegang te verkry tot hierdie toestel se ligging" "Gee <b>%1$s</b> toegang tot hierdie toestel se ligging?" - "Die program sal net toegang tot die ligging hê terwyl jy die program gebruik." - "Gee <b>%1$s</b> altyd toegang tot toestelligging?" - "Die program sal altyd toegang tot die ligging hê, selfs wanneer jy nie die program gebruik nie." + + + + + + "Kalender" "by jou kalender in te gaan" "Gee <b>%1$s</b> toegang tot jou kalender?" @@ -316,7 +322,10 @@ "Gee <b>%1$s</b> toegang tot jou musiek?" "Foto\'s en video\'s" "toegang tot jou foto\'s en video\'s" - "Laat <b>%1$s</b> toe om by jou foto\'s en video\'s in te gaan, insluitend gemerkte liggings?" + + + + "Venster-inhoud ophaal" "Die inhoud ondersoek van \'n venster waarmee jy interaksie het." "Verken deur raak aanskakel" @@ -509,8 +518,10 @@ "Laat die program toe om met kortveldkommunikasie- (NFC) merkers, kaarte en lesers te kommunikeer." "deaktiveer jou skermslot" "Laat die program toe om die sleutelslot en enige verwante wagwoordsekuriteit te deaktiveer. Byvoorbeeld, die foon deaktiveer die sleutelslot wanneer ’n oproep inkom, en atkiveer dit dan weer wanneer die oproep eindig." - "versoek skermslot-kompleksiteit" - "Laat die program toe om die skermslot-kompleksiteitvlak (hoog, medium, laag of geen) te leer, wat die moontlike omvang van die lengte en soort skermslot aandui. Hierdie program kan ook aan gebruikers voorstel dat hulle die skermslot na \'n sekere vlak toe opdateer, maar gebruikers kan dit vrylik ignoreer en weggaan. Let daarop dat die skermslot nie in skoonteks geberg word nie sodat die program nie die presiese wagwoord ken nie." + + + + "gebruik biometriese hardeware" "Laat die program toe om biometriese hardeware vir stawing te gebruik" "bestuur vingerafdrukhardeware" @@ -565,37 +576,59 @@ "Laat program toe om metodes te benut om gesigtemplate vir gebruik by te voeg en uit te vee." "gebruik gesigstawinghardeware" "Laat die program toe om gesigstawinghardeware vir stawing te gebruik" - "Kon nie gesig verwerk nie. Probeer weer." - "Gesig is te helder. Probeer met minder lig." - "Gesig is te donker. Maak ligbron oop." - "Beweeg sensor verder weg van gesig af." - "Bring sensor nader aan gesig." - "Beweeg sensor na bo." - "Beweeg sensor na onder." - "Beweeg sensor na regs." - "Beweeg sensor na links." - "Kyk na die sensor." - "Geen gesig bespeur nie." - "Te veel beweging." + + + + + + + + + + + + + + + + + + + + + + + + "Skryf jou gesig asseblief weer in." - "Ander gesig is bespeur." + + "Te eenders. Verander asseblief jou pose." - "Kyk asseblief meer reguit na die kamera." - "Kyk asseblief meer reguit na die kamera." + + + + "Hou asseblief jou kop regop." - "Ontbloot asseblief jou gesig." + + + + "Gesighardeware is nie beskikbaar nie." - "Gesiguittelling is bereik. Probeer weer." + + "Gesig kan nie geberg word nie." "Gesighandeling is gekanselleer." "Gesigstawing is deur gebruiker gekanselleer." "Te veel pogings. Probeer later weer." "Te veel pogings. Gesigstawingsensor is gedeaktiveer." - "Probeer weer." - "Geen gesigte is geregistreer nie." - "Hierdie toestel het nie \'n gesigstawingsensor nie." + + + + + + "Gesig %d" @@ -1213,9 +1246,16 @@ "Maak %1$s oop" "%1$s sal toemaak sonder om te stoor" "%1$s het berginglimiet oorskry" + + "Hoopstorting is ingesamel. Tik om te deel." "Deel hoopstorting?" - "Die proses %1$s het sy prosesberginglimiet van %2$s oorskry. \'n Hoopstorting is beskikbaar wat jy met sy ontwikkelaar kan deel. Pas op: Hierdie hoopstorting kan enige van jou persoonlike inligting bevat waartoe die program toegang het." + + + + + + "Kies \'n handeling vir teks" "Luiervolume" "Mediavolume" @@ -1254,8 +1294,10 @@ "Tik om alle netwerke te sien" "Koppel" "Alle netwerke" - "\'n Wi‑Fi-netwerk voorgestel deur %s is beskikbaar" - "Wil jy koppel aan netwerke wat %s voorgestel het?" + + + + "Ja" "Nee" "Wi-Fi sal outomaties aanskakel" @@ -1267,9 +1309,14 @@ "Meld by netwerk aan" - "Wi‑Fi het geen internettoegang nie" + + "Tik vir opsies" "Gekoppel" + + + + "Veranderings aan jou warmkolinstellings" "Jou warmkolband het verander." "Hierdie toestel steun nie jou voorkeur vir net 5 GHz nie. Hierdie toestel sal in plaas daarvan die 5 GHz-band gebruik wanneer dit beskikbaar is." @@ -1354,6 +1401,10 @@ "USB-ontfouter gekoppel" "Tik om USB-ontfouting af te skakel" "Kies om USB-ontfouting te deaktiveer." + + + + "Vloeistof of vuilgoed in USB-poort" "USB-poort is outomaties gedeaktiveer. Tik om meer te wete te kom." "Veilig om USB-poort te gebruik" @@ -1905,8 +1956,6 @@ "Tik om werkprofiel te ontsluit" "Gekoppel aan %1$s" "Tik om lêers te bekyk" - "Speld vas" - "Ontspeld" "Programinligting" "−%1$s" "Begin tans demonstrasie …" @@ -1997,6 +2046,22 @@ "Roetinemodus-inligtingkennisgewing" "Battery kan afloop voordat dit normaalweg gelaai word" "Batterybespaarder is geaktiveer om batterylewe te verleng" + + + + + + + + + + + + + + + + "Vouer" "Android-program" "Lêer" diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index cabca7f495a57d58d948fb79ef81a906315181e6..b42fd20116d718b4fa49c9576a5999c6d4cded3b 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -141,8 +141,10 @@ "የWi-Fi ጥሪ" "VoWifi" "ጠፍቷል" - "Wi-Fi ተመርጧል" - "የተንቀሳቃሽ ስልክ ተመራጭ ነው" + + + + "Wi-Fi ብቻ" "{0}፡አልተላለፈም" "{0}: {1}" @@ -228,7 +230,8 @@ "የሳንካ ሪፖርት" "ክፍለ-ጊዜን አብቃ" "ቅጽበታዊ ገጽ እይታ" - "የሳንካ ሪፖርት ውሰድ" + + "ይሄ እንደ የኢሜይል መልዕክት አድርጎ የሚልከውን ስለመሣሪያዎ የአሁኑ ሁኔታ መረጃ ይሰበስባል። የሳንካ ሪፖርቱን ከመጀመር ጀምሮ እስኪላክ ድረስ ትንሽ ጊዜ ይወስዳል፤ እባክዎ ይታገሱ።" "መስተጋብራዊ ሪፖርት" "በአብዛኛዎቹ ሁኔታዎች ላይ ይህን ይጠቀሙ። የሪፖርቱን ሂደት እንዲከታተሉ፣ ስለችግሩ ተጨማሪ ዝርዝሮችን እንዲያስገቡ እና ቅጽበታዊ ገጽ እይታዎችን እንዲያነሱ ያስችልዎታል። ሪፖርት ለማድረግ ረዥም ጊዜ የሚወስዱ አንዳንድ ብዙም ጥቅም ላይ የማይውሉ ክፍሎችን ሊያልፋቸው ይችላል።" @@ -281,9 +284,12 @@ "መገኛ አካባቢ" "የዚህን መሣሪያ አካባቢ ይድረሱበት" "<b>%1$s</b> የዚህ መሣሪያ አካባቢን እንዲደርስ ይፈቀድለት?" - "መተግበሪያው እርስዎ ሲጠቀሙበት ብቻ ነው የአካባቢው መዳረሻ የሚኖረው።" - "ሁልጊዜ <b>%1$s</b> የዚህ መሣሪያ አካባቢን እንዲደርስ ይፈቀድለት?" - "መተግበሪያው ሁልጊዜ የአካባቢው መዳረሽ ይኖረዋል፣ እርስዎ መተግበሪያውን እየተጠቀሙ ባይሆኑም እንኳ።" + + + + + + "ቀን መቁጠሪያ" "የእርስዎን ቀን መቁጠሪያ ይድረሱበት" "<b>%1$s</b> ቀን መቁጠሪያዎን እንዲደርስ ይፈቀድለት?" @@ -316,7 +322,10 @@ "<b>%1$s</b> ቀን ሙዚቃዎን እንዲደርስ ይፈቀድለት?" "ፎቶዎች እና ቪዲዮዎች" "የእርስዎን ፎቶዎች እና ቪዲዮዎች መድረስ" - "የእርስዎን ፎቶዎች እና ቪዲዮዎች መለያ ስያሜ የተደረገባቸውን መገኛ አካባቢዎች ጨምሮ <b>%1$s</b> እንዲደርስ ይፈቀድለት?" + + + + "የመስኮት ይዘት ሰርስረው ያውጡ" "መስተጋበር የሚፈጥሩት የመስኮት ይዘት ይመርምሩ።" "በመንካት ያስሱን ያብሩ" @@ -509,8 +518,10 @@ "ከቅርብ ግኑኙነት መስክ (NFC) መለያዎች፣ ካርዶች እና አንባቢ ጋር ለማገናኘት ለመተግበሪያው ይፈቅዳሉ።" "የማያ ገጽዎን መቆለፊያ ያሰናክሉ" "መተግበሪያው መቆለፊያውና ማንኛውም የተጎዳኘ የይለፍ ቃል ደህንነት እንዲያሰናክል ይፈቅድለታል። ለምሳሌ ስልኩ ገቢ የስልክ ጥሪ በሚቀበልበት ጊዜ መቆለፊያውን ያሰናክልና ከዚያም ጥሪው ሲጠናቀቅ መቆለፊያውን በድጋሚ ያነቃዋል።" - "የማያ ገጽ መቆለፊያ ውስብስብነትን ጠይቅ" - "መተግበሪያው የማያ ገጽ መቆለፊያው ውስብስብነት ደረጃ (ከፍተኛ፣ መካከለኛ፣ ዝቅተኛ ወይም ምንም) እንዲያውቅ ያስችለዋል፣ ይህም ሊሆኑ የሚችለው የማያ ገጽ መቆለፊያው ርዝመት እና አይነት ክልል ያመለክታል። መተግበሪያው እንዲሁም ለተጠቃሚዎች የማያ ገጽ መቆለፊያውን ወደተወሰነ ደረጃ እንዲያዘምኑት ሊጠቁማቸው ይችላል። የማያ ገጽ መቆለፊያው በስነጣ አልባ ጽሑፍ እንደማይከማች ልብ ይበሉ፣ በዚህም መተግበሪያው ትክክለኛውን የይለፍ ቃል አያውቅም።" + + + + "ባዮሜትራዊ ሃርድዌርን መጠቀም" "መተግበሪያው የባዮሜትራዊ ሃርድዌር ለማረጋገጥ ስራ እንዲጠቀም ያስችለዋል" "የጣት አሻራ ሃርድዌርን አስተዳድር" @@ -565,37 +576,59 @@ "መተግበሪያው ጥቅም ላይ እንዲውሉ የፊት ቅንብር ደንቦችን ለማከል እና ለመሰረዝ የሚያስችሉ ስልቶችን እንዲያስጀምር ያስችለዋል።" "የፊት ማረጋገጫ ሃርድዌር ይጠቀሙ" "መተግበሪያው የማረጋገጫ ሃርድዌር ለማረጋገጥ ሥራ እንዲጠቀም ያስችለዋል" - "ፊትን መሥራት አልተቻለም። እባክዎ እንደገና ይሞክሩ።" - "ፊት ከልክ በላይ ብሩህ ነው። እባክዎ በዝቅተኛ ብርሃን ውስጥ ይሞክሩት።" - "ፊት ከልክ በላይ ጨለም ያለ ነው። እባክዎ የብርሃን ምንጩን ይግለጹት።" - "እባክዎ ዳሳሹን ከፊት አሁንም ያርቁት።" - "እባክዎ ዳሳሹን ወደ ፊት ያስጠጉት።" - "እባክዎ ዳሳሽን ከፍ ያድርጉት።" - "እባክዎ ዳሳሽን ዝቅ ያድርጉት።" - "እባክዎ ዳስሽን ወደ ቀኝ ያንቀሳቅሱት።" - "እባክዎ ዳስሽን ወደ ግራ ያንቀሳቅሱት።" - "እባክዎ ዳሳሹ ላይ ይመልከቱ።" - "ምንም መልክ አልተገኘም።" - "ከልክ በላይ ብዙ እንቅስቃሴ።" + + + + + + + + + + + + + + + + + + + + + + + + "እባክዎ ፊትዎን እንደገና ያስመዝግቡ" - "የተለየ ፊት ተገኝቷል።" + + "በጣም ይመሳሰላል፣ እባክዎ የእርስዎን ፎቶ አነሳስ ይለውጡ" - "እባክዎ ወደ ካሜራው በቀጥታ ይመልከቱ።" - "እባክዎ ወደ ካሜራው በቀጥታ ይመልከቱ።" + + + + "እባክዎ ጭንቅላትዎን ቀጥ ያድርጉ።" - "እባክዎ ከፊትዎ ላይ ያለውን ጨርቅ ያንሱ።" + + + + "የፊት ሃርድዌር አይገኝም።" - "የፊት ማብቂያ ጊዜ ደርሷል። እንደገና ይሞክሩ።" + + "ፊት ሊከማች አይችልም።" "የፊት ሥርዓተ ክወና ተሰርዟል።" "ፊትን ማረጋገጥ በተጠቃሚ ተሰርዟል።" "ከልክ በላይ ብዙ ሙከራዎች። በኋላ ላይ እንደገና ይሞክሩ።" "በጣም ብዙ ሙከራዎች። የፊት ማረጋገጫ ተሰናክሏል።" - "እንደገና ይሞክሩ።" - "ምንም ፊት አልተመዘገበም።" - "ይህ መሣሪያ የፊት ማረጋገጫ ዳሳሽ የለውም።" + + + + + + "ፊት %d" @@ -1213,9 +1246,16 @@ "%1$sን ይክፈቱ" "%1$s ሳያስቀምጥ ይዘጋል" "%1$s የማህደረ ትውስታ ገደብን አልፏል" + + "የቆሻሻ ቁልል ተሰብስቧል። ለማጋራት መታ ያድርጉ።" "የቆሻሻ ቁልል ይጋራ?" - "የ%1$s ሂደት የማህደረ ትውስታ ሂደት %2$s ገደቡን አልፏል። የቆሻሻ ቁልል ከገንቢው ጋር እንዲያጋሩ ለእርስዎ ሊገኝ ይችላል። ጥንቃቄ ያድርጉ፦ ይህ የቆሻሻ ቁልል መተግበሪያው መዳረሻ ያለው የሆነ የእርስዎ የግል መረጃን ሊይዝ ይችላል።" + + + + + + "ለፅሁፍ ድርጊት ምረጥ" "የስልክ ጥሪ ድምፅ" " ማህደረ መረጃ ክፍልፍል" @@ -1254,8 +1294,10 @@ "ሁሉንም አውታረ መረቦችን ለማየት መታ ያድርጉ" "አገናኝ" "ሁሉም አውታረ መረቦች" - "በ%s የተጠቆመ የWi-Fi አውታረ መረብ ይገኛል" - "በ%s ከተጠቆሙ አውታረ መረቦች ጋር መገናኘት ይፈልጋሉ?" + + + + "አዎ" "አይ" "Wi‑Fi በራስ-ሰር ይበራል" @@ -1267,9 +1309,14 @@ "ወደ አውታረ መረብ በመለያ ይግቡ" - "Wi-Fi በይነመረብ መዳረሻ የለውም" + + "ለአማራጮች መታ ያድርጉ" "ተገናኝቷል" + + + + "በእርስዎ ሆትስፖት ቅንብሮች ላይ ለውጦች" "የእርስዎ ሆትስፖት ባንድ ተለውጧል።" "ይህ መሣሪያ የእርስዎን ምርጫ ለ5GHz ብቻ አይደግፍም። በምትኩ፣ ይህ መሣሪያ ሲገኝ 5GHz ባንድ ይጠቀማል።" @@ -1354,6 +1401,10 @@ "USB አድስ ተያይዟል" "የዩኤስቢ ማረሚያን ለማጥፋት መታ ያድርጉ" "USB ማረሚያ ላለማንቃት ምረጥ።" + + + + "በዩኤስቢ ወደብ ውስጥ ፈሳሽ ወይም ፍርስራሽ" "የዩኤስቢ ወደብ በራስ-ሰር ተሰናክሏል። የበለጠ ለመረዳት መታ ያድርጉ።" "የዩኤስቢ ወደቡን መጠቀም አደጋ የለውም" @@ -1905,8 +1956,6 @@ "የስራ መገለጫውን እገዳ ለማንሳት መታ ያድርጉ" "ከ%1$s ጋር ተገናኝቷል" "ፋይሎችን ለመመልከት መታ ያድርጉ" - "ፒን" - "ንቀል" "የመተግበሪያ መረጃ" "−%1$s" "ማሳያን በማስጀመር ላይ…" @@ -1997,6 +2046,22 @@ "የዕለት ተዕለት ሁነታ መረጃ ማሳወቂያዎች" "ባትሪ ከተለመደው ኃይል መሙላት በፊት ሊያልቅ ይችላል" "የባትሪ ቆጣቢ የባትሪ ዕድሜን ለማራዘም ገብሯል።" + + + + + + + + + + + + + + + + "አቃፊ" "የAndroid መተግበሪያ" "ፋይል" diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index e7dad2fa407dd6fdd3bfe0e87b20cb4f2ca6ea42..5a24461c0ccb42bc79c4628a9c73a14d43ad9ff1 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -141,8 +141,10 @@ "ৱাই-ফাই কলিং" "VoWifi" "অফ হৈ আছে" - "ৱাই-ফাইক অগ্ৰাধিকাৰ দিয়া হৈছে" - "ম\'বাইলক অগ্ৰাধিকাৰ দিয়া হৈছে" + + + + "কোৱল ৱাই-ফাই" "{0}: ফৰৱাৰ্ড কৰা নহ\'ল" "{0}: {1}" @@ -228,7 +230,8 @@ "বাগ সম্পর্কীয় অভিযোগ" "ছেশ্বন সমাপ্ত কৰক" "স্ক্ৰীণশ্বট" - "বাগ সম্পর্কীয় অভিযোগ লওক" + + "এই কার্যই ইমেইল বাৰ্তা হিচাপে পঠিয়াবলৈ আপোনাৰ ডিভাইচৰ বৰ্তমান অৱস্থাৰ বিষয়ে তথ্য সংগ্ৰহ কৰিব৷ ইয়াক বাগ সম্পর্কীয় অভিযোগ পঠিওৱা কাৰ্য আৰম্ভ কৰোঁতে অলপ সময় লাগিব; অনুগ্ৰহ কৰি ধৈৰ্য ধৰক।" "ইণ্টাৰেক্টিভ অভিযোগ" "বেছিভাগ পৰিস্থিতিত এয়া ব্যৱহাৰ কৰক। ই আপোনাক অভিযোগৰ অগ্ৰগতি ট্ৰেক কৰিবলৈ, সমস্যাটোৰ সম্পর্কে অধিক বিৱৰণ দিবলৈ আৰু স্ক্ৰীণশ্বট ল\'বলৈ অনুমতি দিয়ে। ই কম ব্যৱহাৰ হোৱা সেই শাখাসমূহক অন্তৰ্ভুক্ত নকৰিব পাৰে যিবোৰক অভিযোগ কৰিবলৈ দীৰ্ঘ সময়ৰ প্ৰয়োজন হয়।" @@ -281,9 +284,12 @@ "অৱস্থান" "এই ডিভাইচৰ অৱস্থান ব্যৱহাৰ কৰিব পাৰে" "<b>%1$s</b>ক এই ডিভাইচটোৰ অৱস্থান জানিবলৈ অনুমতি দিবনে?" - "আপুনি এই এপ্ ব্য়ৱহাৰ কৰি থাকোঁতে ই সদায় অৱস্থান চাব পাৰে।" - "<b>%1$s</b>ক সদায় এই ডিভাইচৰ অৱস্থান চাবলৈ অনুমতি দিবনে?" - "আপুনি এই এপ্ ব্য়ৱহাৰ কৰি থকা নাই যদিও ই সদায় অৱস্থান চাব পাৰে।" + + + + + + "কেলেণ্ডাৰ" "আপোনাৰ কেলেণ্ডাৰ ব্যৱহাৰ কৰিব পাৰে" "<b>%1$s</b>ক আপোনাৰ কেলেণ্ডাৰ চাবলৈ অনুমতি দিবনে?" @@ -316,7 +322,10 @@ "<b>%1$s</b>ক আপোনাৰ সংগীত এক্সেছ কৰিবলৈ দিবনে?" "ফট’ আৰু ভিডিঅ’" "আপোনাৰ ফট’ আৰু ভিডিঅ’সমূহ এক্সেছ কৰিবলৈ" - "টেগ কৰা অৱস্থানসহ আপোনাৰ ফট’ আৰু ভিডিঅ’ <b>%1$s</b>ক এক্সেছ কৰিবলৈ দিবনে?" + + + + "ৱিণ্ড’ সমল বিচাৰি উলিওৱাৰ" "আপুনি চাই থকা ৱিণ্ড’খনৰ সমল পৰীক্ষা কৰাৰ।" "স্পৰ্শৰ দ্বাৰা অন্বেষণ কৰাৰ সুবিধা অন কৰাৰ" @@ -509,8 +518,10 @@ "এপটোক নিয়েৰ ফিল্ড কমিউনিকেশ্বন (NFC) টেগ, কাৰ্ড আৰু ৰিডাৰসমূহৰ সৈতে যোগাযোগ কৰিবলৈ অনুমতি দিয়ে।" "আপোনাৰ স্ক্ৰীণ ল\'ক অক্ষম কৰক" "এপটোক কী ল\'ক আৰু জড়িত হোৱা যিকোনো পাছৱৰ্ডৰ সুৰক্ষা অক্ষম কৰিব দিয়ে৷ উদাহৰণস্বৰূপে, কোনো অন্তৰ্গামী ফ\'ন কল উঠোৱাৰ সময়ত ফ\'নটোৱে কী-লকটো অক্ষম কৰে, তাৰ পিছত কল শেষ হ\'লেই কী লকটো পুনৰ সক্ষম কৰে৷" - "স্ক্ৰীণ লকৰ জটিলতাৰ অনুৰোধ জনোৱা" - "এপটোক স্ক্ৰীণ ল’কৰ জটিলতাৰ স্তৰ (উচ্চ, মধ্যম, নিম্ন বা একেবাৰে নাই), শিকিবলৈ অনুমতি দিয়ে ই স্ক্ৰীণ ল’কৰ সম্ভাব্য দৈৰ্ঘ্য বা স্ক্ৰীণ ল’কৰ প্ৰকাৰ দৰ্শায়। লগতে এপটোৱে ব্যৱহাৰকাৰীক স্ক্ৰীণ ল’কটো এটা নিৰ্দিষ্ট স্তৰলৈ আপডে’ট কৰিবলৈ পৰামৰ্শ দিব পাৰে যিটো ব্যৱহাৰকাৰীয়ে উপেক্ষা কৰি পৰৱর্তী পৃষ্ঠালৈ যাব পাৰে। মনত ৰাখিব যে স্ক্ৰীণ ল’কটো সাধাৰণ পাঠ হিচাপে সঞ্চয় কৰা নহয় সেয়ে এপটোৱে সঠিক পাছৱৰ্ডটো জানিব নোৱাৰে।" + + + + "বায়োমেট্ৰিক হাৰ্ডৱেৰ ব্য়ৱহাৰ কৰক" "বিশ্বাসযোগ্য়তা প্ৰমাণীকৰণৰ বাবে এপক বায়োমেট্ৰিক হাৰ্ডৱেৰ ব্য়ৱহাৰ কৰিবলৈ অনুমতি দিয়ে" "ফিংগাৰপ্ৰিণ্ট হাৰ্ডৱেৰ পৰিচালনা কৰিব পাৰে" @@ -565,37 +576,59 @@ "মুখমণ্ডলৰ টেম্প্লেট যোগ কৰাৰ বা মচাৰ পদ্ধতি কামত লগাবলৈ আহ্বান কৰিবলৈ এপটোক অনুমতি দিয়ে।" "মুখমণ্ডল সত্যাপন হাৰ্ডৱেৰ ব্যৱহাৰ কৰক" "বিশ্বাসযোগ্য়তা প্ৰমাণীকৰণৰ বাবে এপক মুখমণ্ডল সত্যাপন হাৰ্ডৱেৰ ব্য়ৱহাৰ কৰিবলৈ অনুমতি দিয়ে" - "মুখমণ্ডল চিনাক্ত কৰিব পৰা নাই; আকৌ চেষ্টা কৰক।" - "মুখমণ্ডল অত্যন্ত উজ্জ্বল হৈছে। অনুগ্ৰহ কৰি পোহৰ কম থকা ঠাইত চেষ্টা কৰক।" - "মুখমণ্ডল অত্যন্ত আন্ধাৰ হৈছে। অনুগ্ৰহ কৰি পোহৰ থকা ঠাইলৈ যাওক।" - "অনুগ্ৰহ কৰি মুখৰ পৰা ছেন্সৰ অলপ দূৰত ৰাখক।" - "অনুগ্ৰহ কৰি ছেন্সৰটো মুখৰ ওচৰলৈ আনক।" - "অনুগ্ৰহ কৰি ছেন্সৰটো ওপৰলৈ নিয়ক।" - "অনুগ্ৰহ কৰি ছেন্সৰটো তললৈ নিয়ক।" - "অনুগ্ৰহ কৰি ছেন্সৰটো সোঁফাললৈ নিয়ক।" - "অনুগ্ৰহ কৰি ছেন্সৰটো বাওঁফাললৈ নিয়ক।" - "অনুগ্ৰহ কৰি ছেন্সৰটোলৈ চাওক।" - "কোনো মুখমণ্ডল চিনাক্ত কৰিব পৰা নগ’ল।" - "বেছি লৰচৰ কৰি আছে।" + + + + + + + + + + + + + + + + + + + + + + + + "আপোনাৰ মুখমণ্ডল পুনৰ পঞ্জীয়ন কৰক।" - "অন্য মুখমণ্ডল চিনাক্ত কৰা হৈছে।" + + "একে ধৰণৰ হৈছে, অনুগ্ৰহ কৰি আপোনাৰ প’জটো সলনি কৰক।" - "অনুগ্ৰহ কৰি পোনে পোনে কেমেৰালৈ চাওক।" - "অনুগ্ৰহ কৰি পোনে পোনে কেমেৰালৈ চাওক।" + + + + "অনুগ্ৰহ কৰি আপোনাৰ মূৰটো উলম্বভাৱে চিধা কৰক।" - "আপোনাৰ মুখমণ্ডলৰপৰা আৱৰণ আঁতৰাওক।" + + + + "মুখমণ্ডলৰ হাৰ্ডৱেৰ উপলব্ধ নহয়।" - "মুখমণ্ডল গ্ৰহণৰ সময়সীমা উকলি গৈছে। আকৌ চেষ্টা কৰক।" + + "মুখমণ্ডল সঞ্চয় কৰিব নোৱাৰি।" "মুখমণ্ডলৰ প্ৰক্ৰিয়া বাতিল কৰা হ’ল।" "ব্যৱহাৰকাৰীয়ে মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ বাতিল কৰিছে।" "অত্যধিক ভুল প্ৰয়াস। কিছুসময়ৰ পাছত আকৌ চেষ্টা কৰক।" "অত্যধিক প্ৰয়াস। মুখমণ্ডলৰ জৰিয়তে সত্যাপন অক্ষম কৰা হ’ল।" - "আকৌ চেষ্টা কৰক।" - "কোনো মুখমণ্ডল যোগ কৰা নহ’ল।" - "এই ডিভাইচটোত মুখমণ্ডল সত্যাপন ছেন্সৰ নাই।" + + + + + + "মুখমণ্ডল %d" @@ -1213,9 +1246,16 @@ "%1$s খোলক" "%1$s ছেভ নকৰাকৈ বন্ধ হ\'ব" "%1$s মেম\'ৰিৰ সীমা অতিক্ৰম কৰিছে" + + "হীপ ডাম্প সংগ্ৰহ কৰা হ’ল। শ্বেয়াৰ কৰিবলৈ টিপক" "হীপ ডাম্প শ্বেয়াৰ কৰিবনে?" - "এই %1$s প্ৰক্ৰিয়াটোৱে তাৰ মেম\'ৰিৰ সীমা %2$s অতিক্ৰম কৰিছে। ইয়াৰ বিকাশকৰ্তাৰ সৈতে আপুনি শ্বেয়াৰ কৰিবপৰাকৈ হীপ ডাম্প মজুত আছে। সাৱধান হ\'ব: এই হীপ ডাম্পত এপ্লিকেশ্বনটোৱে ব্যৱহাৰ কৰা আপোনাৰ কোনো ব্য়ক্তিগত তথ্য়ও থাকিব পাৰে।" + + + + + + "বার্তাৰ বাবে কাৰ্য বাছনি কৰক" "ৰিংগাৰৰ ধ্বনি" "মিডিয়াৰ ধ্বনি" @@ -1254,8 +1294,10 @@ "সকলো নেটৱৰ্ক চাবলৈ টিপক" "সংযোগ কৰক" "সকলো নেটৱৰ্ক" - "%sএ প্ৰস্তাৱ কৰা এটা ৱাই-ফাই নেটৱৰ্ক উপলব্ধ হৈছে" - "আপুনি %sএ প্ৰস্তাৱ কৰা নেটৱৰ্ককেইটাৰ সৈতে সংযোগ কৰিবনে?" + + + + "হয়" "নহয়" "ৱাই-ফাই স্বয়ংক্ৰিয়ভাৱে অন হ\'ব" @@ -1267,9 +1309,14 @@ "নেটৱৰ্কত ছাইন ইন কৰক" - "ৱাই-ফাইত ইণ্টাৰনেট নাই" + + "অধিক বিকল্পৰ বাবে টিপক" "সংযোগ কৰা হ’ল" + + + + "আপোনাৰ হটস্পট ছেটিংসমূহত কৰা সালসলনি" "আপোনাৰ হটস্পটৰ বেণ্ড সলনি কৰা হ’ল।" "আপোনাৰ কেৱল ৫গিগাহাৰ্টজৰ প্ৰতি অগ্ৰাধিকাৰ এই ডিভাচইচটোৱে সমৰ্থন নকৰে। ইয়াৰ পৰিৱৰ্তে, ডিভাচইচটোৱে যেতিয়া ৫গিগাহাৰ্টজ বেণ্ড উপলব্ধ হ’ব তেতিয়া সেইয়া ব্যৱহাৰ কৰিব।" @@ -1354,6 +1401,10 @@ "ইউএছবি ডিবাগিং সংযোগ কৰা হ’ল" "ইউএছবি ডিবাগিং বন্ধ কৰিবলৈ টিপক" "ইউএছবি ডিবাগিং অক্ষম কৰিবলৈ বাছনি কৰক।" + + + + "ইউএছবি প’ৰ্টত তৰল বা ধূলি-মাকতি আছে" "ইউএছবি প’ৰ্ট স্বয়ংক্ৰিয়ভাৱে অক্ষম কৰা হয়। অধিক জানিবৰ বাবে টিপক।" "ইউএছবি প’ৰ্ট ব্যৱহাৰ কৰিব পৰাকৈ নিৰাপদ" @@ -1906,8 +1957,6 @@ "কৰ্মস্থানৰ প্ৰ’ফাইল আনলক কৰিবলৈ টিপক" "%1$sৰ সৈতে সংযুক্ত হৈ আছে" "ফাইলসমূহ চাবৰ বাবে টিপক" - "পিন" - "আনপিন" "এপ্ সম্পৰ্কীয় তথ্য" "−%1$s" "ডেম\' আৰম্ভ কৰি থকা হৈছে…" @@ -1998,6 +2047,22 @@ "ৰুটিন ম’ডৰ তথ্য জাননী" "চ্চাৰ্জ কৰাৰ সচৰাচৰ সময়ৰ আগতেই বেটাৰি শেষ হ’ব পাৰে" "বেটাৰিৰ খৰচ কমাবলৈ বেটাৰি সঞ্চয়কাৰী অন কৰা হৈছে" + + + + + + + + + + + + + + + + "ফ’ল্ডাৰ" "Android এপ্লিকেশ্বন" "ফাইল" diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 7186da9559d44d6aaae988cff01f8dadad5f48e9..9ac4a2a05046c04a8b2a16ea7330d60236fe5a5f 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -141,8 +141,10 @@ "WiFi Zəngi" "VoWifi" "Deaktiv" - "Wi-Fi tərcih edilir" - "Mobil tərcih" + + + + "Yalnız Wi-Fi" "{0}: Yönləndirilmədi" "{0}: {1}" @@ -228,7 +230,8 @@ "Baq hesabatı" "Sessiyanı sonlandırın" "Skrinşot" - "Baqı xəbər verin" + + "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." "İnteraktiv hesabat" "Bir çox hallarda bundan istifadə edin. Bu hesabatın gedişatını izləməyə, problem haqqında daha ətraflı məlumat daxil etməyə və skrinşot etməyə imkan verir. Bu, çox vaxt tələb edən bəzi az istifadə olunan bölmələri ixtisar edə bilər." @@ -281,9 +284,12 @@ "Yer" "cihazın yerini bilmək" "<b>%1$s</b> tətbiqinə bu cihazın məkanına daxil olmaq icazəsi verilsin?" - "Tətbiq yalnız ondan istifadə etiyiniz zaman məkanı əldə edə bilər." - "<b>%1$s</b> tətbiqinə bu cihazın məkanına daxil olmaq icazəsi verilsin?" - "Tətbiq hətta ondan istifadə etmədiyiniz zaman belə məkanı əldə edə bilər." + + + + + + "Təqvim" "təqvimə daxil olun" "<b>%1$s</b> tətbiqinə təqvimə daxil olmaq icazəsi verilsin?" @@ -316,7 +322,10 @@ "<b>%1$s</b> tətbiqinin musiqiyə daxil olmağına icazə verilsin?" "Foto və videolar" "foto və videolara daxil olun" - "<b>%1$s</b> tətbiqinə işarələnmiş məkanlar daxil olmaqla, foto və videolara girişinə icazə verilsin?" + + + + "Pəncərənin məzmununu əldə edin" "Əlaqədə olduğunuz pəncərənin məzmununu nəzərdən keçirin." "Toxunaraq Kəşf et funksiyasını yandırın" @@ -509,8 +518,10 @@ "Tətbiqə Yaxın Məsafə Kommunikasiyası (NFC) teqləri, kartları və oxuyucuları ilə əlaqə qurmağa icazə verir." "Ekran kilidini deaktiv edir" "Tətbiqə kilid açarını və təhlükəsizlik parolunu deaktiv etməyə imkan verir. Qanuni misal budur ki, telefon zəng qəbul edən zaman kilidi açır və zəng qurtarandan sonra kilidi bağlayır." - "ekran kilidi mürəkkəbliyi tələb edin" - "Tətbiqə ekran kilidinin uzunluq intervalı və növünü göstərən ekran kilidi mürəkkəbliliyinin səviyyəsini (yüksək, orta, aşağı və ya heç biri) öyrənməyə icazə verir. Tətbiq, istifadəçilərə ekran kilidini müəyyən səviyyəyə yeniləməyi təklif edə bilər, lakin istifadəçilər istənilən vaxt bunu iqnor edə bilər. Nəzərə alın ki, ekran kilidi açıq şəkildə saxlanılmır, buna görə də tətbiq dəqiq parolu bilmir." + + + + "biometrik proqramdan istifadə edin" "Doğrulama üçün biometrik proqramdan istifadə etməyə imkan verir" "barmaq izi avadanlığını idarə edin" @@ -565,37 +576,59 @@ "Proqramdan istifadə üçün barmaq izi şablonlarını əlavə etmək və silmək məqsədilə üsullara müraciət etməyə imkan verir." "üz identifikasiyası proqramından istifadə edin" "Tətbiqin üz identifikasiyası proqramından identifikasiya zamanı istifadə etməsinə icazə verir" - "Üz tanınmadı. Yenidən cəhd edin." - "Üz çox parlaqdır. Daha zəif işıqda sınayın." - "Üz çox tünddür. İşığı ortaya çıxarın." - "Sensoru üzdən kənara gətirin." - "Sensoru üzə biraz da yaxınlaşdırın." - "Sensoru daha yuxarı gətirin." - "Sensoru daha aşağı gətirin." - "Sensoru sağa gətirin." - "Sensoru sola gətirin." - "Sensora baxın." - "Üz aşkarlanmadı." - "Cihazı sabit saxlayın." + + + + + + + + + + + + + + + + + + + + + + + + "Üzünüzü yenidən qeydiyyatdan keçirin." - "Fərqli üz aşkar edildi." + + "Digəri ilə oxşardır, pozanızı dəyişin." - "Birbaşa kameraya baxın." - "Birbaşa kameraya baxın." + + + + "Başınızı şaquli istiqamətdə qaldırın." - "Üzünüzü açıq saxlayın." + + + + "Üz proqramı əlçatan deyil." - "Üz proqramı taymerinin vaxtı bitdi. Yenidən cəhd edin." + + "Üz bərpa edilmədi." "Üz əməliyyatı ləğv edildi." "Üz dorğulaması istifadəçi tərəfindən ləğv edildi." "Həddindən çox cəhd. Sonraya saxlayın." "Həddindən çox cəhd. Üz identifikasiyası deaktiv edildi." - "Yenidən cəhd edin." - "Üz qeydə alınmayıb." - "Bu cihazda üz identifikasiyası sensoru yoxdur." + + + + + + "Üz %d" @@ -1213,9 +1246,16 @@ "%1$s tətbiqini açın" "%1$s yadda saxlamadan bağlanacaq" "%1$s yaddaş limitini keçdi" + + "Şəkil çəkildi. Paylaşmaq üçün klikləyin." "Yığın paylaşılsın?" - "%1$s prosesi %2$s ölçüsünün yaddaş limitini prosesini keçib. Yığın tərtibatçısı ilə paylaşmaq üçün sizə istifadəsi mümkündür. Ehtiyatlı olun: bu yığında proqramın giriş icazəsi olduğu şəxsi məlumatlarınız ola bilər." + + + + + + "Mətn üçün əməliyyat seçin" "Zəngin səs gücü" "Media həcmi" @@ -1254,8 +1294,10 @@ "Bütün şəbəkələri görmək üçün klikləyin" "Qoşulun" "Bütün şəbəkələr" - "%s tərəfindən təklif edilən Wi‑Fi şəbəkəsi əlçatandır" - "%s tərəfindən təklif edilən şəbəkələrə qoşulmaq istəyirsiniz?" + + + + "Bəli" "Xeyr" "Wi‑Fi avtomatik olaraq aktiv ediləcək" @@ -1267,9 +1309,14 @@ "Şəbəkəyə daxil olun" - "Wi-Fi şəbəkəsinin internetə girişi yoxdur" + + "Seçimlər üçün tıklayın" "Qoşuldu" + + + + "Hotspot ayarlarınızda dəyişiklik" "Hotspot qrupu dəyişdi." "Bu cihaz yalnız 5GHz üçün tərcihinizi dəstəkləmir. Əvəzinə əlçatan olduqda bu cihaz 5GHz qrupundan istifadə edəcək." @@ -1354,6 +1401,10 @@ "USB sazlama qoşuludur" "USB sazlamanı deaktiv etmək üçün klikləyin" "USb debaqı deaktivasiya etməyi seçin." + + + + "USB portuna maye sızıb və ya qırılıb" "USB portu avtomatik deaktiv edildi. Ətraflı məlumat üçün klikləyin." "USB portundan istifadə təhlükəsizdir" @@ -1905,8 +1956,6 @@ "İş profilinin kilidini açmaq üçün tıklayın" "%1$s məhsuluna bağlandı" "Faylları görmək üçün basın" - "Pin kod" - "Çıxarın" "Tətbiq məlumatı" "−%1$s" "Demo başlayır…" @@ -1997,6 +2046,22 @@ "Rejim üçün məlumat bildirişi" "Batareya həmişəki vaxtdan əvvəl bitə bilər" "Enerjiyə Qənaət rejimi batareya istifadəsinin müddətini artırmaq üçün aktiv edilir" + + + + + + + + + + + + + + + + "Qovluq" "Android tətbiqi" "Fayl" diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 788b5d61aba45c882b97d46145a7d3e0cd25913c..363b5ea6e79d38f2c2e8499586039d6048b93ca1 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -142,8 +142,10 @@ "Pozivanje preko Wi-Fi-ja" "VoWifi" "Isključeno" - "Prednost ima Wi-Fi" - "Želim mobilne podatke" + + + + "Samo Wi-Fi" "{0}: Nije prosleđeno" "{0}: {1}" @@ -230,7 +232,8 @@ "Izveštaj o grešci" "Završi sesiju" "Snimak ekrana" - "Napravi izveštaj o grešci" + + "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." "Interaktiv. izveštaj" "Koristite ovo u većini slučajeva. To vam omogućava da pratite napredak izveštaja, da unosite dodatne detalje o problemu i da snimate snimke ekrana. Verovatno će izostaviti neke manje korišćene odeljke za koje pravljenje izveštaja dugo traje." @@ -284,9 +287,12 @@ "Lokacija" "pristupi lokaciji ovog uređaja" "Želite li da omogućite da <b>%1$s</b> pristupa lokaciji ovog uređaja?" - "Aplikacija će imati pristup lokaciji samo dok koristite aplikaciju." - "Želite li da omogućite da <b>%1$s</b> pristupa lokaciji ovog uređaja?" - "Aplikacija će uvek imati pristup lokaciji, čak i kada ne koristite aplikaciju." + + + + + + "Kalendar" "pristupi kalendaru" "Želite li da omogućite da <b>%1$s</b> pristupa kalendaru?" @@ -319,7 +325,10 @@ "Želite li da omogućite da <b>%1$s</b> pristupa muzici?" "Slike i video snimci" "pristup slikama i video snimcima" - "Želite li da dozvolite da „%1$s“ pristupa slikama i video snimcima, uključujući označene lokacije?" + + + + "da preuzima sadržaj prozora" "Proverava sadržaj prozora sa kojim ostvarujete interakciju." "da uključi Istraživanja dodirom" @@ -512,8 +521,10 @@ "Dozvoljava aplikaciji da komunicira sa oznakama, karticama i čitačima komunikacije kratkog dometa (NFC)." "onemogućavanje zaključavanja ekrana" "Dozvoljava aplikaciji da onemogući zaključavanje tastature i sve povezane bezbednosne mere sa lozinkama. Na primer, telefon onemogućava zaključavanje tastature pri prijemu dolaznog telefonskog poziva, a zatim ga ponovo omogućava po završetku poziva." - "traženje složenosti zaključavanja ekrana" - "Dozvoljava aplikaciji da sazna nivo složenosti zaključavanja ekrana (visoka, srednja, niska ili nijedna), što ukazuje na mogući opseg trajanja i tip zaključavanja ekrana. Aplikacija može i da predlaže korisnicima da ažuriraju zaključavanje ekrana na određeni nivo, ali korisnici slobodno mogu da zanemare to i da idu na druge stranice. Imajte na umu da se podaci za zaključavanje ekrana ne čuvaju kao običan tekst, pa aplikacija ne zna tačnu lozinku." + + + + "koristi biometrijski hardver" "Dozvoljava aplikaciji da koristi biometrijski hardver za potvrdu identiteta" "upravljaj hardverom za otiske prstiju" @@ -568,37 +579,59 @@ "Dozvoljava da aplikacija aktivira metode za dodavanje i brisanje šablona lica radi korišćenja." "korišćenje hardv. za potvrdu identiteta pomoću lica" "Dozvoljava da aplikacija koristi hardver za potvrdu identiteta pomoću lica" - "Obrada lica nije uspela. Probajte ponovo." - "Lice je presvetlo. Probajte sa slabijim osvetljenjem." - "Lice je isuviše tamno. Otkrijte izvor svetla." - "Udaljite senzor od lica." - "Približite senzor licu." - "Pomerite senzor naviše." - "Pomerite senzor naniže." - "Pomerite senzor udesno." - "Pomerite senzor ulevo." - "Gledajte u senzor." - "Nije otkriveno nijedno lice." - "Mnogo se pomerate." + + + + + + + + + + + + + + + + + + + + + + + + "Ponovo registrujte lice." - "Otkriveno je drugo lice." + + "Previše je slično, promenite pozu." - "Gledajte pravo u kameru." - "Gledajte pravo u kameru." + + + + "Ispravite glavu." - "Otkrijte lice." + + + + "Harvder za lice nije dostupan." - "Isteklo je vreme za proveru lica. Probajte ponovo." + + "Nije moguće sačuvati lice." "Obrada lica je otkazana." "Korisnik je otkazao potvrdu lica." "Previše pokušaja. Probajte ponovo kasnije." "Više pokušaja. Potvrda identiteta je onemogućena." - "Probajte ponovo." - "Nije registrovano nijedno lice." - "Ovaj uređaj nema senzor za potvrdu identiteta pomoću lica." + + + + + + "Lice %d" @@ -1233,9 +1266,16 @@ "Otvori %1$s" "%1$s će se zatvoriti bez čuvanja" "%1$s premašuje ograničenje memorije" + + "Snimak dinamičkog dela memorije je napravljen. Dodirnite za deljenje." "Želite li da delite snimak dinamičkog dela memorije?" - "Proces %1$s je premašio ograničenje memorije za proces od %2$s. Snimak dinamičkog dela memorije je dostupan i možete da ga delite sa programerom. Budite oprezni: ovaj snimak dinamičkog dela memorije može da sadrži neke lične podatke kojima aplikacija može da pristupa." + + + + + + "Izaberite radnju za tekst" "Jačina zvuka zvona" "Jačina zvuka medija" @@ -1276,8 +1316,10 @@ "Dodirnite da biste videli sve mreže" "Poveži" "Sve mreže" - "Wi‑Fi mreža koju predlaže %s je dostupna" - "Želite li da se povežete na mreže koje predlaže %s?" + + + + "Da" "Ne" "Wi‑Fi će se automatski uključiti" @@ -1289,9 +1331,14 @@ "Prijavite se na mrežu" - "Wi-Fi nema pristup internetu" + + "Dodirnite za opcije" "Povezano je" + + + + "Promene podešavanja za hotspot" "Opseg hotspota je promenjen." "Ovaj uređaj ne podržava podešavanje samo za 5 GHz. Uređaj će koristiti opseg od 5 GHz kada bude dostupan." @@ -1376,6 +1423,10 @@ "Otklanjanje grešaka sa USB-a je omogućeno" "Dodirnite da biste isključili otklanjanje grešaka sa USB-a" "Izaberite da biste onemogućili otklanjanja grešaka sa USB-a." + + + + "Tečnost ili nečistoća u USB portu" "USB port je automatski isključen. Dodirnite da biste saznali više." "Korišćenje USB porta je bezbedno" @@ -1939,8 +1990,6 @@ "Dodirom otklj. profil za Work" "Povezano je sa proizvodom %1$s" "Dodirnite za pregled datoteka" - "Zakači" - "Otkači" "Informacije o aplikaciji" "−%1$s" "Pokrećemo demonstraciju..." @@ -2032,6 +2081,22 @@ "Obaveštenje o informacijama Rutinskog režima" "Baterija će se možda isprazniti pre uobičajenog punjenja" "Ušteda baterije je aktivirana da bi se produžilo trajanje baterije" + + + + + + + + + + + + + + + + "Direktorijum" "Android aplikacija" "Datoteka" diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 83b268a5f383a50c422f6ca6de37351e6cc5c324..18e345ac70ecde4fb2c836e94be41126e103fea4 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -143,8 +143,10 @@ "Wi-Fi-тэлефанія" "VoWi-Fi" "Выкл." - "Прыярытэт Wi-Fi" - "Прыярытэт мабільнай сеткі" + + + + "Толькі Wi-Fi" "{0}: не пераадрасоўваецца" "{0}: {1}" @@ -232,7 +234,8 @@ "Справаздача пра памылкі" "Скончыць сеанс" "Здымак экрана" - "Справаздача пра памылку" + + "Будзе збiрацца iнфармацыя пра бягучы стан прылады, якая будзе адпраўляцца на электронную пошту. Стварэнне справаздачы пра памылкi зойме некаторы час." "Інтэрактыўная справаздача" "Выкарыстоўвайце ў большасці выпадкаў. Гэта дазваляе сачыць за ходам справаздачы, уводзіць дадатковыя звесткі аб праблеме і рабіць здымкі экрана. Могуць быць прапушчаны некаторыя раздзелы, якія выкарыстоўваюцца менш і паведамленне пра якія займае шмат часу." @@ -287,9 +290,12 @@ "Месцазнаходжанне" "атрымліваць доступ да месцазнаходжання гэтай прылады" "Дазволіць праграме <b>%1$s</b> доступ да звестак аб месцазнаходжанні гэтай прылады?" - "Праграма будзе мець доступ да звестак пра месцазнаходжанне, толькі калі яна выкарыстоўваецца." - "Дазволіць праграме <b>%1$s</b> мець доступ да месцазнаходжання?" - "Праграма заўсёды будзе мець доступ да звестак пра месцазнаходжанне, нават калі яна не выкарыстоўваецца." + + + + + + "Каляндар" "атрымліваць доступ да вашага календара" "Дазволіць праграме <b>%1$s</b> доступ да вашага календара?" @@ -322,7 +328,10 @@ "Дазволіць праграме <b>%1$s</b> доступ да музыкі?" "Фота і відэа" "доступ да фота і відэа" - "Дазволіць <b>%1$s</b> доступ да фота і відэа, у тым ліку з пазначаным месцам?" + + + + "Атрымліваць змесціва вакна" "Аналізаваць змесціва актыўнага вакна." "Уключаць Азнаямленне дотыкам" @@ -515,8 +524,10 @@ "Дазваляе прыкладаннzv спалучацца з тэгамі, картамі і счытваючымі прыладамі Near Field Communication (NFC)." "адключэнне блакiроўкi экрана" "Дазваляе прыкладанням адключаць блакiроўку клавіятуры і любыя сродкі абароны, звязаныя з паролем. Прыкладам гэтага з\'яўляецца адключэнне тэлефонам блакiроўкi клавіятуры пры атрыманні ўваходнага выкліку і паўторнае ўключэнне блакiроўкi клавіятуры, калі выклік завершаны." - "запытваць узровень складанасці блакіроўкі экрана" - "Дазваляе праграме вызначаць узровень складанасці блакіроўкі экрана (высокі, сярэдні, нізкі ці нулявы), які залежыць ад даўжыні пароля і ад тыпу блакіроўкі экрана. Праграма можа прапанаваць карыстальнікам ускладніць блакіроўку экрана, аднак гэту прапанову можна ігнараваць. Заўважце, што праграма не можа ведаць тып і пароль блакіроўкі экрана, таму што яны захоўваюцца ў зашыфраваным выглядзе." + + + + "выкарыстоўваць біяметрычнае абсталяванне" "Дазваляе праграме выкарыстоўваць для аўтэнтыфікацыі біяметрычнае абсталяванне" "кіраваць апаратнымі сродкамі для адбіткаў пальцаў" @@ -571,37 +582,59 @@ "Праграма зможа дадаваць і выдаляць шаблоны твару." "карыстацца абсталяваннем для распазнавання твару" "Праграма зможа выкарыстоўваць абсталяванне распазнавання твару для аўтэнтыфікацыі" - "Не ўдалося распазнаць твар. Паўтарыце спробу." - "Твар занадта светлы. Паспрабуйце зменшыць святло." - "Твар занадта цёмны. Павялічце асвятленне." - "Перамясціце датчык далей ад твару." - "Наблізьце датчык да твару." - "Падыміце датчык вышэй." - "Апусціце датчык ніжэй." - "Перамясціце датчык управа." - "Перамясціце датчык улева." - "Глядзіце на датчык." - "Твар не знойдзены." - "Трымайце прыладу нерухома." + + + + + + + + + + + + + + + + + + + + + + + + "Паўтарыце рэгістрацыю твару." - "Выяўлены іншы твар." + + "Не бачна розніцы. Памяняйце позу." - "Глядзіце прама ў камеру." - "Глядзіце прама ў камеру." + + + + "Выраўнуйце галаву." - "Твар павінен быць добра бачны." + + + + "Абсталяванне для распазнавання твару недаступнае." - "Час чакання твару выйшаў. Паўтарыце спробу." + + "Не ўдалося захаваць твар." "Распазнаванне твару скасавана." "Распазнаванне твару скасавана карыстальнікам." "Занадта шмат спроб. Паўтарыце спробу пазней." "Занадта шмат спроб. Аўтэнтыфікацыя твару адключана" - "Паўтарыце спробу." - "Твар не зарэгістраваны." - "На гэтай прыладзе адсутнічае датчык аўтэнтыфікацыі твару." + + + + + + "Твар %d" @@ -1253,9 +1286,16 @@ "Адкрыць праграму \"%1$s\"" "Праграма \"%1$s\" будзе закрыта. Даныя не будуць захаваны" "Працэс %1$s перавысіў ліміт памяці" + + "Быў сабраны дамп кучы; абагульце дотыкам." "Абагуліць дамп дынамічнай вобласці?" - "Працэс %1$s перавысіў ліміт памяці працэсу %2$s. Дамп дынамічнай вобласці даступны для вас, вы можаце абагуліць яго з распрацоўшчыкам. Будзьце асцярожныя: гэты дамп дынамічнай вобласці можа ўтрымліваць асабістую інфармацыю, да якой маюць доступ праграмы." + + + + + + "Выберыце дзеянне для тэкста" "Гучнасць званка" "Гучнасць прайгравальніка" @@ -1298,8 +1338,10 @@ "Дакраніцеся, каб убачыць усе сеткі" "Падключыцца" "Усе сеткі" - "Даступная сетка Wi‑Fi, прапанаваная праграмай %s" - "Падключыцца да сетак, прапанаваных праграмай %s?" + + + + "Так" "Не" "Wi‑Fi уключыцца аўтаматычна" @@ -1311,9 +1353,14 @@ "Увайдзіце ў сетку" - "У Wi-Fi няма доступу да інтэрнэту" + + "Дакраніцеся, каб убачыць параметры" "Падключана" + + + + "Змяненні ў наладах хот-спота" "Частата хот-спота змянілася." "Прылада не можа працаваць толькі на частаце 5 ГГц. Гэта частата будзе выкарыстоўвацца, калі гэта магчыма." @@ -1398,6 +1445,10 @@ "Адладка па USB падключана" "Націсніце, каб адключыць адладку па USB" "Выберыце, каб адключыць адладку USB." + + + + "Вадкасць або смецце ў порце USB" "Порт USB аўтаматычна адключаны. Каб даведацца больш, націсніце тут." "Можна бяспечна выкарыстоўваць порт USB" @@ -1973,8 +2024,6 @@ "Кран., каб разбл. раб. профіль" "Падлучана да %1$s" "Краніце для прагляду файлаў" - "Замацаваць" - "Адмацаваць" "Інфармацыя пра праграму" "−%1$s" "Ідзе запуск дэманстрацыі…" @@ -2067,6 +2116,22 @@ "Апавяшчэнне з інфармацыяй пра ўсталяваны рэжым" "Акумулятар можа разрадзіцца хутчэй, чым прыйдзе час звычайнай зарадкі" "Каб павялічыць тэрмін работы акумулятара, уключаны рэжым эканоміі зараду" + + + + + + + + + + + + + + + + "Папка" "Праграма Android" "Файл" diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index da29c0eacad11d5dcc3992f95f9686eb66b012e6..23113228832545df6c50ceb62b9344e3775bf0a4 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -141,8 +141,10 @@ "Обаждания през Wi-Fi" "VoWifi" "Изключено" - "Предпочита се Wi-Fi" - "Предпочитат се мобилни данни" + + + + "Само Wi-Fi" "{0}: Не е пренасочено" "{0}: {1}" @@ -228,7 +230,8 @@ "Сигнал за програмна грешка" "Прекратяване на сесията" "Екранна снимка" - "Сигнал за програмна грешка" + + "По този начин ще се събере информация за текущото състояние на устройството ви, която да се изпрати като имейл съобщение. След стартирането на процеса ще мине известно време, докато сигналът за програмна грешка бъде готов за подаване. Моля, имайте търпение." "Интерактивен сигнал" "Използвайте тази опция в повечето случаи. Тя ви позволява да следите напредъка на сигнала, да въвеждате допълнителни подробности за проблема и да правите екранни снимки. Възможно е да бъдат пропуснати някои по-рядко използвани секции, за които подаването на сигнал отнема дълго време." @@ -281,9 +284,12 @@ "Местоположение" "получи достъп до местоположението на това устройство" "Да се разреши ли на <b>%1$s</b> да осъществява достъп до местоположението на това устройство?" - "Само когато използвате приложението, то ще има достъп до местоположението." - "Да се разреши ли на <b>%1$s</b> достъп до местопол. на у-вото?" - "Дори когато не използвате приложението, то винаги ще има достъп до местоположението." + + + + + + "Календар" "има достъп до календара ви" "Да се разреши ли на <b>%1$s</b> да осъществява достъп до календара ви?" @@ -316,7 +322,10 @@ "Да се разреши ли на <b>%1$s</b> да осъществява достъп до музиката ви?" "Снимки и видеоклипове" "достъп до снимките и видеоклиповете ви" - "Да се разреши ли на <b>%1$s</b> достъп до снимките и видеоклиповете ви, включително маркерите за местоположение?" + + + + "Извличане на съдържанието от прозореца" "Инспектиране на съдържанието на прозорец, с който взаимодействате." "Включване на изследването чрез докосване" @@ -509,8 +518,10 @@ "Разрешава на приложението да комуникира с маркери, карти и четци, ползващи комуникация в близкото поле (NFC)." "деактивиране на заключването на екрана ви" "Разрешава на приложението да деактивира заключването на клавиатурата и свързаната защита с парола. Например телефонът деактивира заключването при получаване на входящо обаждане и после го активира отново, когато обаждането завърши." - "заявяване на сложност на опцията за заключване на екрана" - "Разрешава на приложението да разбере нивото на сложност на опцията за заключване на екрана (високо, средно, ниско или липса на такова), което указва възможния диапазон на дължината и типа на опцията. Приложението може също да предложи на потребителите да актуализират опцията за заключване на екрана до определено ниво, но те могат да пренебрегнат това и да излязат от него. Обърнете внимание, че опцията за заключване на екрана не се съхранява като обикновен текст, така че приложението не знае точната парола." + + + + "използване на хардуера за биометрични данни" "Разрешава на приложението да използва хардуера за биометрични данни с цел удостоверяване" "управление на хардуера за отпечатъци" @@ -565,37 +576,59 @@ "Разрешава на прил. да извиква методи за добавяне и изтриване на лицеви шаблони за ползване" "използване на хардуера за удостоверяване с лице" "Разрешава на приложението при необходимост да използва хардуера за удостоверяване с лице" - "Лицето не можа да се обработи. Опитайте отново." - "Лицето е твърде осветено. Oпитайте на по-тъмно." - "Лицето е твърде тъмно. Моля, осветете го по-добре." - "Моля, отдалечете сензора от лицето си." - "Моля, приближете сензора към лицето си." - "Моля, повдигнете сензора." - "Моля, преместете сензора надолу." - "Моля, преместете сензора надясно." - "Моля, преместете сензора наляво." - "Моля, гледайте към сензора." - "Не е открито лице." - "Стабилизирайте устройството." + + + + + + + + + + + + + + + + + + + + + + + + "Моля, регистрирайте лицето си отново." - "Разпознато е различно лице." + + "Позата ви е сходна с предишна. Моля, променете я." - "Моля, гледайте по-директно към камерата." - "Моля, гледайте по-директно към камерата." + + + + "Моля, изправете главата си." - "Моля, открийте лицето си." + + + + "Няма достъп до хардуера за лице." - "Времето за изчакване изтече. Опитайте отново." + + "Лицето не може да бъде съхранено." "Операцията с лице е анулирана." "Удостоверяв. на лицето е анулирано от потребителя." "Твърде много опити. Опитайте отново по-късно." "Твърде много опити. Удост. с лице е деактивирано." - "Опитайте отново." - "Няма регистрирано лице." - "Това устройство няма сензор за удостоверяване с лице." + + + + + + "Лице %d" @@ -1213,9 +1246,16 @@ "Отваряне на %1$s" "%1$s ще се затвори без запазване" "%1$s надхвърли ограничението за памет" + + "Извлечена е моментна снимка на паметта. Докоснете, за да я споделите." "Да се сподели ли моментната снимка на паметта?" - "Процесът %1$s надхвърли ограничението си от %2$s. Налице е моментна снимка на паметта, която да споделите със съответния програмист. Бъдете внимателни, защото тя може да съдържа ваши лични данни, до които приложението има достъп." + + + + + + "Избиране на действие за текст" "Сила на звука при звънене" "Сила на звука" @@ -1254,8 +1294,10 @@ "Докоснете, за да видите всички мрежи" "Свързване" "Всички мрежи" - "Налице е Wi‑Fi мрежа, предложена от %s" - "Искате ли да се свържете с мрежите, предложени от %s?" + + + + "Да" "Не" "Wi‑Fi ще се включи автоматично" @@ -1267,9 +1309,14 @@ "Вход в мрежата" - "Wi-Fi мрежата няма достъп до интернет" + + "Докоснете за опции" "Установена е връзка" + + + + "Промени в настройките ви за точка за достъп" "Честотната лента на точката ви за достъп е променена." "Това устройство не поддържа предпочитанието ви за използване само на честотната лента от 5 ГХц. Вместо това то ще я ползва, когато е възможно." @@ -1354,6 +1401,10 @@ "Отстраняване на грешки през USB" "Докоснете, за да изключите отстраняването на грешки през USB" "Изберете, за да деактивирате отстраняването на грешки през USB." + + + + "Течност или замърсяване в USB порта" "USB портът е деактивиран автоматично. Докоснете, за да научите повече." "Безопасно е да използвате USB порта" @@ -1905,8 +1956,6 @@ "Докоснете за откл. на служ. потр. профил" "Установена е връзка с %1$s" "Докоснете, за да прегледате файловете" - "Фиксиране" - "Освобождаване" "Информация за приложението" "-%1$s" "Демонстрацията се стартира…" @@ -1997,6 +2046,22 @@ "Известие с информация за режима на поредица" "Батерията може да се изтощи преди обичайното зареждане" "Режимът за запазване на батерията е активиран с цел удължаване на живота на батерията" + + + + + + + + + + + + + + + + "Папка" "Приложение за Android" "Файл" diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index e0f866d64aaba95fbabad5be344a583d1a37146d..fc7022dc0ff1c316c860a999446834a2d476991f 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -141,8 +141,10 @@ "ওয়াই-ফাই কলিং" "VoWifi" "বন্ধ আছে" - "পছন্দের ওয়াই-ফাই" - "পছন্দের মোবাইল" + + + + "শুধুমাত্র ওয়াই-ফাই" "{0}: ফরওয়ার্ড করা হয়নি" "{0}: {1}" @@ -228,7 +230,8 @@ "ত্রুটির প্রতিবেদন" "সেশন শেষ করুন" "স্ক্রিনশট নিন" - "ত্রুটির অভিযোগ করুন" + + "এটি একটি ই-মেল মেসেজ পাঠানোর জন্য আপনার ডিভাইসের বর্তমান অবস্থা সম্পর্কে তথ্য সংগ্রহ করবে৷ ত্রুটির প্রতিবেদন শুরুর সময় থেকে এটি পাঠানোর জন্য প্রস্তুত হতে কিছুটা সময় নেবে; অনুগ্রহ করে ধৈর্য রাখুন৷" "ইন্টারেক্টিভ প্রতিবেদন" "বেশিরভাগ পরিস্থিতিতে এটিকে ব্যবহার করুন৷ এটি আপনাকে প্রতিবেদনের কাজ কতটা হয়েছে তার উপর নজর রাখতে দেয়, সমস্যাটির সম্পর্কে আরও অনেক কিছু লিখতে দেয় এবং স্ক্রীনশটগুলি নিতে দেয়৷ এটি হয়ত প্রতিবেদন করতে খুব বেশি সময় নেয় এমনকি কম-ব্যবহৃত বিভাগগুলি সরিয়ে দিতে পারে৷" @@ -281,9 +284,12 @@ "লোকেশন" "এই ডিভাইসের লোকেশন অ্যাক্সেস" "<b>%1$s</b>-কে এই ডিভাইসের লোকেশন অ্যাক্সেস করতে দেবেন?" - "আপনি এই অ্যাপটি ব্যবহার করার সময়েই সেটি আপনার লোকেশন অ্যাক্সেস করতে পারবে।" - "<b>%1$s</b>কে সবসময় এই ডিভাইসের লোকেশন অ্যাক্সেস করতে দেবেন?" - "এই অ্যাপটি সবসময় আপনার লোকেশন অ্যাক্সেস করতে পারবে, এমনকি আপনি অ্যাপটি ব্যবহার না করার সময়েও।" + + + + + + "ক্যালেন্ডার" "আপনার ক্যালেন্ডারে অ্যাক্সেস" "<b>%1$s</b>-কে আপনার ক্যালেন্ডারে অ্যাক্সেস দেবেন?" @@ -316,7 +322,10 @@ "<b>%1$s</b>-কে আপনার মিউজিকে অ্যাক্সেস দেবেন?" "ফটো ও ভিডিও" "আপনার ফটো ও ভিডিও অ্যাক্সেস করুন" - "<b>%1$s</b>-কে ট্যাগ করা লোকেশন সহ ফটো এবং ভিডিও অ্যাক্সেস করার অনুমতি দেবেন?" + + + + "উইন্ডোর কন্টেন্ট পুনরুদ্ধার করে" "ব্যবহার করছেন এমন একটি উইন্ডোর কন্টেন্ট নিরীক্ষণ করে৷" "স্পর্শের মাধ্যমে অন্বেষণ করা চালু করুন" @@ -509,8 +518,10 @@ "অ্যাপ্লিকেশানকে নিয়ার ফিল্ড কমিউনিকেশন (NFC) ট্যাগ, কার্ড এবং রিডারগুলির সাথে যোগাযোগ করতে দেয়৷" "আপনার স্ক্রিন লক অক্ষম করুন" "কী-লক এবং যেকোনো সংশ্লিষ্ট পাসওয়ার্ড সুরক্ষা অক্ষম করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ উদাহরণস্বরূপ, একটি ইনকামিং ফোন কল গ্রহণ করার সময়ে ফোনটি কী-লক অক্ষম করে, তারপরে কল শেষ হয়ে গেলে কী-লকটিকে আবার সক্ষম করে৷" - "স্ক্রিন লকের জটিলতা জানার অনুরোধ করুন" - "এটি অ্যাপটিকে স্ক্রিন লকের জটিলতার লেভেল বুঝতে সাহায্য করে (খুব জটিল, মাঝারি ধরনের জটিল, অল্প জটিল বা কোনও জটিলতা নেই), যা স্ক্রিন লকটি সম্ভত কত দীর্ঘ ও সেটির ধরন কীরকম, তার ইঙ্গিত দেয়। একটি নির্দিষ্ট লেভেল পর্যন্ত স্ক্রিন লক আপডেট করা যাবে তাও এই অ্যাপটি সাজেস্ট করতে পারে, তবে ব্যবহারকারীর তা উপেক্ষা করার এবং অন্য কোথাও চলে যাওয়ার স্বাধীনতা আছে। লক্ষ্য করবেন, স্ক্রিন লকটি যেহেতু প্লেন টেক্সট ফর্ম্যাটে স্টোর করা হয় না, তাই সঠিক পাসওয়ার্ডটি অ্যাপের পক্ষে জানা সম্ভব নয়।" + + + + "বায়োমেট্রিক হার্ডওয়্যার ব্যবহার করুন" "অ্যাপটিকে যাচাইকরণের জন্য বায়োমেট্রিক হার্ডওয়্যার ব্যবহার করার অনুমতি দেয়" "আঙ্গুলের ছাপ নেওয়ার হার্ডওয়্যার পরিচালনা করুন" @@ -565,37 +576,59 @@ "ব্যবহার করার জন্য ফেস টেম্পলেট যোগ করা এবং মোছার পদ্ধতি গ্রহণ করতে অ্যাপটিকে অনুমতি দেয়৷" "ফেস যাচাইকরণ হার্ডওয়্যার ব্যবহার করুন" "প্রমাণীকরণের জন্য ফেস যাচাইকরণ হার্ডওয়্যার ব্যবহার করার অনুমতি অ্যাপটিকে দেয়" - "ফেস প্রক্রিয়া করা যায়নি৷ আবার চেষ্টা করুন৷" - "ফেসটি খুব উজ্জ্বল লাগছে। কম আলোতে চেষ্টা করুন।" - "ফেসটি খুব অন্ধকার লাগছে। বেশি আলোতে চেষ্টা করুন।" - "অনুগ্রহ করে ফেস থেকে সেন্সরটি দূরে সরান।" - "অনুগ্রহ করে ফেসের কাছাকাছি সেন্সরটি আনুন।" - "অনুগ্রহ করে সেন্সরটি উঁচুতে নিয়ে যান।" - "অনুগ্রহ করে সেন্সরটি নিচে নিয়ে যান।" - "অনুগ্রহ করে সেন্সরটি ডান দিকে নিয়ে যান।" - "অনুগ্রহ করে সেন্সরটি বাঁ দিকে নিয়ে যান।" - "অনুগ্রহ করে সেন্সরের দিকে তাকান।" - "কোনও ফেস শনাক্ত করা যায়নি।" - "খুব বেশি নড়ছে।" + + + + + + + + + + + + + + + + + + + + + + + + "আপনার মুখের ছবি আবার নথিভুক্ত করুন।" - "বিভিন্ন ব্যক্তির মুখের ছবি শনাক্ত করা হয়েছে।" + + "একই ধরনের দেখতে, একটু অন্যদিকে ঘুরে দাঁড়ান।" - "সরাসরি ক্যামেরার দিকে তাকান।" - "সরাসরি ক্যামেরার দিকে তাকান।" + + + + "মাথা সোজা করে রাখুন।" - "আপনার মুখটা আলোর দিকে নিয়ে আসুন।" + + + + "ফেসের হার্ডওয়্যার উপলভ্য নয়৷" - "ফেসের ছাপ নেওয়ার সময়সীমা শেষ৷ আবার চেষ্টা করুন৷" + + "ফেস স্টোর করা যাবে না।" "ফেস অপারেশন বাতিল করা হয়েছে৷" "ফেস যাচাইকরণ ব্যবহারকারীর দ্বারা বাতিল করা হয়েছে।" "অনেকবার চেষ্টা করা হয়েছে। পরে আবার চেষ্টা করুন।" "অনেকবার চেষ্টা করা হয়েছে৷ ফেস যাচাইকরণ বন্ধ আছে।" - "আবার চেষ্টা করুন।" - "কোনও ফেস নথিভুক্ত করা হয়নি।" - "এই ডিভাইসের ফেস যাচাইকরণ সেন্সর নেই।" + + + + + + "%d ফেস" @@ -1213,9 +1246,16 @@ "%1$s খুলুন" "সেভ না করেই %1$s বন্ধ হবে" "%1$s মেমরি সীমা অতিক্রম করেছে" + + "হিপ ডাম্প সংগ্রহ করা হয়েছে। শেয়ার করতে ট্যাপ করুন।" "হিপ ডাম্প শেয়ার করবেন?" - "%1$s প্রক্রিয়াটি তার %2$s এর মেমরি সীমা অতিক্রম করেছে৷ তার ডেভেলপারের সাথে শেয়ার করার জন্য একটি হিপ ডাম্প উপলব্ধ৷ সতর্কতা অবলম্বন করুন: এই হিপ ডাম্পে অ্যাপ্লিকেশানটির অ্যাক্সেস আছে এমন আপনার যেকোন ব্যক্তিগত তথ্য থাকতে পারে৷" + + + + + + "পাঠ্যের জন্য একটি কাজ বেছে নিন" "রিং ভলিউম" "মিডিয়ার ভলিউম" @@ -1254,8 +1294,10 @@ "সমস্ত নেটওয়ার্ক দেখতে ট্যাপ করুন" "সংযুক্ত করুন" "সব নেটওয়ার্ক" - "%s-এর সাজেস্ট করা ওয়াই-ফাই নেটওয়ার্ক এখন উপলভ্য" - "আপনি কি %s-এর সাজেস্ট করা নেটওয়ার্ক দিয়ে কানেক্ট করতে চান?" + + + + "হ্যাঁ" "না" "ওয়াই-ফাই অটোমেটিক চালু হবে" @@ -1267,9 +1309,14 @@ "নেটওয়ার্কে সাইন-ইন করুন" - "ওয়াই-ফাই এ ইন্টারনেট অ্যাক্সেস নেই" + + "বিকল্পগুলির জন্য আলতো চাপুন" "কানেক্ট করা হয়েছে" + + + + "আপনার হটস্পট সেটিংসে পরিবর্তনগুলি" "আপনার হটস্পট ব্যান্ড পরিবর্তন করা হয়েছে।" "এই ডিভাইসটি শুধুমাত্র 5GHz এর জন্য আপনার পছন্দ সমর্থন করে না। পরিবর্তে, এই ডিভাইসটি 5GHz ব্যান্ড ব্যবহার করবে।" @@ -1355,6 +1402,10 @@ "USB ডিবাগিং সংযুক্ত হয়েছে" "ইউএসবি ডিবাগিং বন্ধ করতে ট্যাপ করুন" "USB ডিবাগিং অক্ষম করতে বেছে নিন।" + + + + "ইউএসবি পোর্টে তরল পদার্থ অথবা ধুলো কণা" "ইউএসবি পোর্ট নিজে থেকে বন্ধ করা হবে। আরও জানতে ট্যাপ করুন।" "ইউএসবি পোর্ট এখন ব্যবহার করতে পারবেন" @@ -1397,8 +1448,7 @@ "সেট আপ করুন" "বের করে নিন" "ঘুরে দেখুন" - - + "আউটপুট পাল্টান" "%s অনুপস্থিত" "ডিভাইসটি আবার ঢোকান" "%s সরানো হচ্ছে" @@ -1907,8 +1957,6 @@ "কর্মস্থলের প্রোফাইল আনলক করতে আলতো চাপ দিন" "%1$s এর সাথে সংযুক্ত হয়েছে" "ফাইলগুলি দেখতে আলতো চাপ দিন" - "পিন করুন" - "আনপিন করুন" "অ্যাপের তথ্য" "−%1$s" "ডেমো শুরু করা হচ্ছে…" @@ -1999,6 +2047,22 @@ "রুটিন মোডের তথ্য সংক্রান্ত বিজ্ঞপ্তি" "সাধারণত যখন চার্জ দেন, তার আগে চার্জ শেষ হয়ে যেতে পারে" "ডিভাইস বেশিক্ষণ চালু রাখতে ব্যাটারি সেভার চালু করা হয়েছে" + + + + + + + + + + + + + + + + "ফোল্ডার" "Android অ্যাপ্লিকেশন" "ফাইল" diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index a78796fb2a17f1cb5668e4fee87a503b6fe32df0..1d5a7d8fc52477104a14db15c8851da6bd7271ab 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -142,8 +142,10 @@ "Pozivanje putem WIFi-ja" "VoWifi" "Isključeno" - "Preferira se WiFi" - "Preferira se mobilna mreža" + + + + "Samo WiFi" "{0}: Nije proslijeđen" "{0}: {1}" @@ -230,7 +232,8 @@ "Izvještaj o greškama" "Završi sesiju" "Snimak ekrana" - "Kreiranje izvještaja o greškama" + + "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." "Interaktivni izvještaj" "Koristite ovu opciju u većini slučajeva. Ova opcija vam omogućava praćenje napretka izvještaja, unos dodatnih detalja o problemu i pravljenje snimaka ekrana. Moglo bi doći do izostavljanja nekih manje korištenih dijelova za čije prijavljivanje je potrebno dugo vremena." @@ -284,9 +287,12 @@ "Lokacija" "pristupa lokaciji ovog uređaja" "Dozvoliti aplikaciji <b>%1$s</b> pristup lokaciji ovog uređaja?" - "Ova aplikacija će moći pristupiti lokaciji uređaja samo kada je koristite." - "Uvijek dozvoliti da <b>%1$s</b> pristupi lokaciji uređaja?" - "Ova aplikacija će uvijek moći pristupiti lokaciji uređaja, čak i kada je ne koristite." + + + + + + "Kalendar" "pristupa vašem kalendaru" "Dozvoliti aplikaciji <b>%1$s</b> da pristupi vašem kalendaru?" @@ -319,7 +325,10 @@ "Dozvoliti aplikaciji <b>%1$s</b>da pristupi vašoj muzici?" "Fotografije i videozapisi" "pristup fotografijama i videozapisima" - "Dozvoliti da <b>%1$s</b> pristupa fotografijama i videozapisima, uključujući označene lokacije?" + + + + "Preuzima sadržaj prozora" "Pregleda sadržaj prozora koji trenutno koristite." "Uključi opciju Istraživanje dodirom" @@ -512,8 +521,10 @@ "Dozvoljava aplikaciji komuniciranje sa NFC (komunikacija bliskog polja) oznakama, karticama i čitačima." "deaktivacija zaključavanja ekrana" "Omogućava aplikaciji deaktivaciju zaključane tastature i svih povezanih zaštita. Naprimjer, telefon deaktivira zaključavanje tastature kod dolaznog telefonskog poziva, a zatim ponovo aktivira zaključavanje tastature kada je poziv završen." - "zahtjev za kompleksnost zaključavanja ekrana" - "Omogućava aplikaciji da sazna nivo kompleksnosti zaključavanja ekrana (visoki, srednji, niski ili bez zaključavanja), što naznačava mogući raspon trajanja i vrste zaključavanja ekrana. Aplikacija također može korisnicima predložiti da ažuriraju zaključavanje ekrana do određenog nivoa ali korisnici slobodno mogu ignorirati prijedlog i napustiti stranicu. Važno je napomenuti da se zaključavanje ekrana ne pohranjuje kao obični tekst tako da aplikacija ne zna tačnu lozinku." + + + + "koristi biometrijski hardver za otiske prstij" "Omogućava aplikaciji da za autentifikaciju koristi biometrijski hardver" "upravljanje hardverom za otiske prstiju" @@ -568,37 +579,59 @@ "Omogućava aplikaciji korištenje metoda za dodavanje i brisanje šablona lica za upotrebu." "upotreba hardvera za autentifikaciju licem" "Omogućava aplikaciji da za autentifikaciju koristi hardver za autentifikaciju licem" - "Obrada lica nije uspjela. Pokušajte ponovo." - "Lice je presvijetlo. Pokušajte s manje svjetla." - "Lice je pretamno. Pojačajte izvor svjetlosti." - "Odmaknite senzor od lica." - "Približite senzor licu." - "Pomjerite senzor prema gore." - "Pomjerite senzor prema dolje." - "Pomjerite senzor udesno." - "Pomjerite senzor ulijevo." - "Gledajte u senzor." - "Nije pronađeno nijedno lice." - "Previše pokreta." + + + + + + + + + + + + + + + + + + + + + + + + "Ponovo registrirajte lice." - "Otkriveno je lice koje nije vaše." + + "Previše slično, promijenite položaj." - "Gledajte direktno u kameru." - "Gledajte direktno u kameru." + + + + "Poravnajte položaj glave vertikalno." - "Otkrijte lice." + + + + "Hardver za prepoznavanje lica nije dostupan." - "Vrijeme za prepoznavanje lica je isteklo. Pokušajte ponovo." + + "Nije moguće pohraniti lice." "Prepoznavanje lica je otkazano." "Korisnik je otkazao provjeru lica." "Previše pokušaja. Pokušajte ponovo kasnije." "Previše pokušaja. Autentifikacija lica onemogućena." - "Pokušajte ponovo." - "Nije prijavljeno nijedno lice." - "Ovaj uređaj nema senzor za autentifikaciju lica." + + + + + + "Lice %d" @@ -1235,9 +1268,16 @@ "Otvori aplikaciju %1$s" "Aplikacija %1$s će se zatvoriti bez pohranjivanja" "%1$s premašuje ograničenje memorije" + + "Snimak dinamičkog stanja memorije je napravljen. Dodirnite za dijeljenje." "Želite li dijeliti snimak dinamičkog dijela memorije?" - "Proces %1$s je premašio ograničenje procesne memorije od %2$s. Snimak dinamičkog dijela memorije vam je dostupan i možete ga dijeliti sa njegovim programerom. Budite oprezni: ovaj snimak dinamičkog dijela memorije može sadržavati vaše lične podatke kojima aplikacija ima pristup." + + + + + + "Biranje akcije za tekst" "Jačina zvuka zvona" "Jačina zvuka medija" @@ -1278,8 +1318,10 @@ "Dodirnite da vidite sve mreže" "Povežite se" "Sve mreže" - "WiFi mreža koju je predložila aplikacija %s je dostupna" - "Želite li se povezati na mreže koje je predložila aplikacija %s?" + + + + "Da" "Ne" "Wi‑Fi veza će se automatski uključiti" @@ -1291,9 +1333,14 @@ "Prijava na mrežu" - "WiFi nema pristup internetu" + + "Dodirnite za opcije" "Povezano" + + + + "Promjene postavki vaše pristupne tačke" "Opseg vaše pristupne tačke je promijenjen." "Ovaj uređaj ne podržava vašu postavku za mreže od isključivo 5GHz. Uređaj će koristiti opseg of 5GHz kada je dostupan." @@ -1378,6 +1425,10 @@ "Otklanjanje grešaka putem uređaja spojenog na USB je uspostavljeno" "Dodirnite da isključite otklanjanje grešaka putem USB-a" "Odaberite da onemogućite ispravljanje grešaka koristeći USB" + + + + "Tečnost ili nečistoće u USB priključku" "USB priključak je automatski onemogućen. Dodirnite da saznate više." "USB priključak je sada sigurno koristiti" @@ -1941,8 +1992,6 @@ "Dodirnite da biste otključali radni profil" "Povezan na uređaj %1$s" "Dodirnite za prikaz fajlova" - "Zakači" - "Otkači" "Informacije o aplikaciji" "−%1$s" "Pokretanje demonstracije…" @@ -2034,6 +2083,22 @@ "Obavještenje za informacije Rutinskog načina" "Moguće je da će se baterija isprazniti prije uobičajenog punjenja" "Ušteda baterije je aktivirana da bi se produžio vijek trajanja baterije" + + + + + + + + + + + + + + + + "Folder" "Android aplikacija" "Fajl" diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index be2c0de0de144ac615729d663c0399b924e0cef7..a0e3e21174400ce54cb949457d3572be36fec15e 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -143,8 +143,10 @@ "Volání přes WiFi" "VoWifi" "Vypnuto" - "Preferována síť W-Fi" - "Preferována mobilní data" + + + + "Pouze Wi-Fi" "{0}: Nepřesměrováno" "{0}: {1}" @@ -232,7 +234,8 @@ "Hlášení chyb" "Ukončit relaci" "Snímek obrazovky" - "Vytvořit chybové hlášení" + + "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í." "Interaktivní přehled" "Tato možnost se používá ve většině případů. Umožňuje sledovat průběh přehledu, zadat další podrobnosti o problému a pořizovat snímky obrazovky. Mohou být vynechány některé méně používané sekce, jejichž kontrola trvá dlouho." @@ -287,9 +290,12 @@ "Poloha" "přístup k poloze tohoto zařízení" "Povolit aplikaci <b>%1$s</b> přístup k poloze tohoto zařízení?" - "Aplikace bude mít přístup k poloze, pouze když ji budete používat." - "Povolit aplikaci <b>%1$s</b> přístup k poloze tohoto zařízení?" - "Aplikace bude mít přístup k poloze vždy, i když ji nebudete používat." + + + + + + "Kalendář" "přístup ke kalendáři" "Povolit aplikaci <b>%1$s</b> přístup ke kalendáři?" @@ -322,7 +328,10 @@ "Povolit aplikaci <b>%1$s</b> přístup k hudbě?" "Fotky a videa" "přístup k fotkám a videím" - "Povolit aplikaci <b>%1$s</b> přístup k fotkám a videím včetně označených míst?" + + + + "Načítat obsah oken" "Může prozkoumávat obsah oken, se kterými pracujete." "Zapnout funkci Prozkoumání dotykem" @@ -400,13 +409,13 @@ "Umožňuje aplikaci odesílat trvalá vysílání, která přetrvávají i po skončení vysílání. Nadměrné používání může televizi zpomalit či způsobit její nestabilitu, protože bude používat příliš mnoho paměti." "Umožňuje aplikaci odesílat trvalá vysílání, která přetrvávají i po skončení vysílání. Nadměrné používání může telefon zpomalit či způsobit jeho nestabilitu, protože bude používat příliš mnoho paměti." "čtení kontaktů" - "Umožňuje aplikaci číst údaje o kontaktech uložených v tabletu, včetně toho, jak často voláte, posíláte e-maily nebo jinak komunikujete s konkrétními osobami. Toto oprávnění umožňuje aplikacím ukládat údaje o kontaktech. Škodlivé aplikace mohou tyto údaje bez vašeho vědomí sdílet." - "Umožňuje aplikaci číst údaje o kontaktech uložených v televizi včetně toho, jak často voláte, posíláte e-maily nebo jinými způsoby komunikujete s konkrétními kontakty. Toto oprávnění umožňuje aplikacím ukládat údaje o vašich kontaktech a škodlivé aplikace mohou sdílet údaje o kontaktech bez vašeho vědomí." - "Umožňuje aplikaci číst údaje o kontaktech uložených v telefonu, včetně toho, jak často voláte, posíláte e-maily nebo komunikujete jinými způsoby s konkrétními osobami. Toto oprávnění umožňuje aplikacím ukládat údaje o kontaktech. Škodlivé aplikace mohou tyto údaje bez vašeho vědomí sdílet." + "Umožňuje aplikaci číst údaje o kontaktech uložených v tabletu, včetně toho, jak často voláte, posíláte e‑maily nebo jinak komunikujete s konkrétními osobami. Toto oprávnění umožňuje aplikacím ukládat údaje o kontaktech. Škodlivé aplikace mohou tyto údaje bez vašeho vědomí sdílet." + "Umožňuje aplikaci číst údaje o kontaktech uložených v televizi včetně toho, jak často voláte, posíláte e‑maily nebo jinými způsoby komunikujete s konkrétními kontakty. Toto oprávnění umožňuje aplikacím ukládat údaje o vašich kontaktech a škodlivé aplikace mohou sdílet údaje o kontaktech bez vašeho vědomí." + "Umožňuje aplikaci číst údaje o kontaktech uložených v telefonu, včetně toho, jak často voláte, posíláte e‑maily nebo komunikujete jinými způsoby s konkrétními osobami. Toto oprávnění umožňuje aplikacím ukládat údaje o kontaktech. Škodlivé aplikace mohou tyto údaje bez vašeho vědomí sdílet." "úprava kontaktů" - "Umožňuje aplikaci upravit údaje o kontaktech uložených v tabletu včetně toho, jak často voláte, posíláte e-maily nebo komunikujete jinými způsoby s konkrétními kontakty. Toto oprávnění aplikacím umožňuje mazat údaje o kontaktech." - "Umožňuje aplikaci upravit údaje o kontaktech uložených v televizi včetně toho, jak často voláte, posíláte e-maily nebo jinými způsoby komunikujete s konkrétními kontakty. Toto oprávnění aplikacím umožňuje mazat údaje o kontaktech." - "Umožňuje aplikaci upravit údaje o kontaktech uložených v telefonu včetně toho, jak často voláte, posíláte e-maily nebo komunikujete jinými způsoby s konkrétními kontakty. Toto oprávnění aplikacím umožňuje mazat údaje o kontaktech." + "Umožňuje aplikaci upravit údaje o kontaktech uložených v tabletu včetně toho, jak často voláte, posíláte e‑maily nebo komunikujete jinými způsoby s konkrétními kontakty. Toto oprávnění aplikacím umožňuje mazat údaje o kontaktech." + "Umožňuje aplikaci upravit údaje o kontaktech uložených v televizi včetně toho, jak často voláte, posíláte e‑maily nebo jinými způsoby komunikujete s konkrétními kontakty. Toto oprávnění aplikacím umožňuje mazat údaje o kontaktech." + "Umožňuje aplikaci upravit údaje o kontaktech uložených v telefonu včetně toho, jak často voláte, posíláte e‑maily nebo komunikujete jinými způsoby s konkrétními kontakty. Toto oprávnění aplikacím umožňuje mazat údaje o kontaktech." "čtení seznamu hovorů" "Tato aplikace může číst historii volání." "zápis do seznamu hovorů" @@ -515,8 +524,10 @@ "Umožňuje aplikaci komunikovat se štítky, kartami a čtečkami s podporou technologie NFC." "vypnutí zámku obrazovky" "Umožňuje aplikaci vypnout zámek kláves a související zabezpečení heslem. Telefon například vypne zámek klávesnice při příchozím hovoru a po skončení hovoru jej zase zapne." - "zjištění složitosti zámku obrazovky" - "Umožňuje aplikaci zjistit úroveň složitosti zámku obrazovky (vysoká, střední, nízká nebo žádná), která ukazuje možnou délku a typ zámku obrazovky. Aplikace také může uživatelům navrhovat, aby zámek obrazovky upravili na určitou úroveň, ale uživatelé mohou návrhy klidně ignorovat a odejít. Zámek obrazovky není uložen jako prostý text, a tak aplikace přesné heslo nezná." + + + + "použití biometrického hardwaru" "Umožňuje aplikaci použít k ověření biometrický hardware" "správa hardwaru na čtení otisků prstů" @@ -571,37 +582,59 @@ "Umožňuje aplikaci volat metody k přidání a smazání šablon obličeje, které budou použity." "použití hardwaru k ověření obličeje" "Umožňuje aplikaci provést ověření pomocí hardwaru k ověření obličeje" - "Zpracování obličeje se nezdařilo. Zkuste to znovu." - "Obličej je moc jasný. Zkuste to v menším světle." - "Obličej je moc tmavý. Odkryjte zdroj světla." - "Oddalte senzor od obličeje." - "Posuňte senzor blíž k obličeji." - "Posuňte senzor výš." - "Posuňte senzor níž." - "Posuňte senzor doprava." - "Posuňte senzor doleva." - "Podívejte se do senzoru." - "Nebyl rozpoznán žádný obličej." - "Příliš mnoho pohybu." + + + + + + + + + + + + + + + + + + + + + + + + "Zaznamenejte obličej znovu." - "Byl rozpoznán jiný obličej." + + "Příliš podobné, změňte výraz." - "Dívejte se přímo na fotoaparát." - "Dívejte se přímo na fotoaparát." + + + + "Narovnejte hlavu." - "Odhalte obličej." + + + + "Není k dispozici hardware ke snímání obličeje." - "Limit ověření obličeje vypršel. Zkuste to znovu." + + "Obličej nelze uložit." "Operace snímání obličeje byla zrušena." "Ověření obličeje bylo zrušeno uživatelem." "Příliš mnoho pokusů. Zkuste to později." "Příliš mnoho pokusů. Ověření obličeje je zakázáno." - "Zkuste to znovu." - "Není zaregistrován žádný obličej." - "Toto zařízení nemá snímač ověření obličeje." + + + + + + "Obličej %d" @@ -1253,9 +1286,16 @@ "Otevřít aplikaci %1$s" "Aplikace %1$s se zavře bez uložení" "Proces %1$s překročil limit paměti" + + "Byl shromážděn výpis haldy. Klepnutím jej můžete sdílet." "Sdílet výpis haldy?" - "Proces %1$s překročil limit paměti procesu %2$s. Je k dispozici výpis haldy, který můžete sdílet s vývojářem. Buďte opatrní, výpis haldy může obsahovat osobní údaje, ke kterým má aplikace přístup." + + + + + + "Vyberte akci pro text" "Hlasitost vyzvánění" "Hlasitost médií" @@ -1298,8 +1338,10 @@ "Klepnutím zobrazíte všechny sítě" "Připojit" "Všechny sítě" - "Je k dispozici síť Wi‑Fi navrhovaná aplikací %s" - "Chcete se připojit k sítím, které navrhuje aplikace %s?" + + + + "Ano" "Ne" "Wi‑Fi se zapne automaticky" @@ -1311,9 +1353,14 @@ "Přihlásit se k síti" - "Wi-Fi nemá přístup k internetu" + + "Klepnutím zobrazíte možnosti" "Připojeno" + + + + "Změny nastavení hotspotu" "Pásmo hotspotu se změnilo." "Toto zařízení nepodporuje vaše nastavení jen 5GHz pásma. Zařízení použije pásmo 5 GHz, jen když bude dostupné." @@ -1398,6 +1445,10 @@ "Ladění přes USB připojeno" "Klepnutím vypnete ladění přes USB" "Vyberte, chcete-li zakázat ladění přes USB." + + + + "Kapalina nebo nečistota v portu USB" "Port USB byl automaticky deaktivován. Klepnutím zobrazíte další informace." "Port USB lze bezpečně použít" @@ -1973,8 +2024,6 @@ "Klepnutím jej odemknete" "Připojeno k zařízení %1$s" "Klepnutím zobrazíte soubory" - "Připnout" - "Odepnout" "O aplikaci" "−%1$s" "Spouštění ukázky…" @@ -2067,6 +2116,22 @@ "Informační oznámení režimu sledu činností" "Baterie se možná vybije před obvyklým časem nabití" "Byl aktivován spořič baterie za účelem prodloužení výdrže" + + + + + + + + + + + + + + + + "Složka" "Aplikace pro Android" "Soubor" diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 4a30a4d58289dbf73cad7596bdc0b7318f41b369..80256eebdf980a1d40e7527cbe089d6f6918519f 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -141,8 +141,10 @@ "Wi-Fi-opkald" "VoWifi" "Fra" - "WiFi-netværk er foretrukket" - "Mobildata foretrækkes" + + + + "Kun Wi-Fi" "{0}: Ikke viderestillet" "{0}: {1}" @@ -228,7 +230,8 @@ "Fejlrapport" "Afslut sessionen" "Screenshot" - "Lav fejlrapport" + + "Der indsamles oplysninger om din enheds aktuelle status, der efterfølgende sendes i en mail. Der går lidt tid, fra fejlrapporten påbegyndes, til den er klar til at blive sendt. Tak for tålmodigheden." "Interaktiv rapport" "Brug dette workflow under de fleste omstændigheder. Det giver dig mulighed for at se status på rapporten, angive flere oplysninger om problemet og tage screenshots. Nogle mindre brugte sektioner, der tager lang tid at rapportere, udelades muligvis." @@ -281,9 +284,12 @@ "Placering" "få adgang til enhedens placering" "Vil du give <b>%1$s</b> adgang til enhedens placering?" - "Appen har kun adgang til placeringen, når du bruger appen." - "Skal <b>%1$s</b> altid have adgang til enhedens placering?" - "Appen har altid adgang til placeringen, også selvom du ikke bruger appen." + + + + + + "Kalender" "have adgang til din kalender" "Vil du give <b>%1$s</b> adgang til din kalender?" @@ -316,7 +322,10 @@ "Vil du give <b>%1$s</b> adgang til din musik?" "Billeder og videoer" "adgang til dine billeder og videoer" - "Vil du give <b>%1$s</b> adgang til dine billeder og videoer, herunder taggede placeringer?" + + + + "Hente indholdet i vinduet" "Undersøge indholdet i et vindue, du interagerer med." "Aktivere Udforsk ved berøring" @@ -509,8 +518,10 @@ "Tillader, at appen kan kommunikere med NFC-tags (Near Field Communication), -kort og -læsere." "deaktivere din skærmlås" "Tillader, at appen kan deaktivere tastaturlåsen og anden form for tilknyttet adgangskodesikkerhed. Telefonen deaktiverer f.eks. tastaturlåsen ved indgående telefonopkald og aktiverer tastaturlåsen igen, når opkaldet er afsluttet." - "anmode om skærmlåsens kompleksitet" - "Giver appen tilladelse til at kende skærmlåsens kompleksitet (høj, medium, lav eller ingen), hvilket kan afsløre oplysninger om skærmlåsens længde og type. Appen kan også foreslå brugerne at opdatere deres skærmlås til et bestemt niveau, men brugerne kan frit ignorere det og gå videre. Bemærk! Skærmlåsen gemmes ikke som almindelig tekst, så appen kender ikke adgangskoden." + + + + "brug biometrisk hardware" "Tillader, at appen kan bruge biometrisk hardware til godkendelse" "administrer fingeraftrykhardware" @@ -565,37 +576,59 @@ "Tillader, at appen kan bruge metoder til at tilføje og slette ansigtsskabeloner." "brug hardware til ansigtsgenkendelse" "Tillader, at appen bruger ansigtsgenkendelseshardware til godkendelse" - "Ansigtet kunne ikke behandles. Prøv igen." - "Ansigtet er for lyst. Prøv i svagere belysning." - "Ansigtet er for mørkt. Ryk tættere på en lyskilde." - "Flyt sensoren længere væk fra ansigtet." - "Ryk sensoren tættere på ansigtet." - "Flyt sensoren højere op." - "Flyt sensoren længere ned." - "Flyt sensoren til højre." - "Flyt sensoren til venstre." - "Kig på sensoren." - "Der er ikke registreret noget ansigt." - "Der er for meget bevægelse." + + + + + + + + + + + + + + + + + + + + + + + + "Registrer dit ansigt igen." - "Der blev registreret et andet ansigt." + + "Det minder for meget om et andet. Skift stilling." - "Kig mere direkte ind i kameraet." - "Kig mere direkte ind i kameraet." + + + + "Hold hovedet helt lodret." - "Sørg for, at ansigtet ikke er dækket til." + + + + "Hardwaren til ansigtsregistrering er ikke klar." - "Ansigtsgenkendelse fik timeout. Prøv igen." + + "Ansigtet kan ikke gemmes." "Ansigtshandlingen blev annulleret." "Ansigtsgodkendelsen blev annulleret af brugeren." "Du har prøvet for mange gange. Prøv igen senere." "For mange forsøg – Ansigtsgenkendelse deaktiveret." - "Prøv igen." - "Der er ikke registreret nogen ansigter." - "Denne enhed har ingen sensor til ansigtsgenkendelse." + + + + + + "Ansigt %d" @@ -1213,9 +1246,16 @@ "Åbn %1$s" "%1$s lukkes uden at gemme" "%1$s har overskredet sin hukommelsesgrænse" + + "Der er indsamlet en heap dump. Tryk for at dele." "Vil du dele en heap dump?" - "Processen %1$s har overskredet sin proceshukommelsesgrænse på %2$s. En heap dump er tilgængelig og kan deles med udvikleren. Vær forsigtig: Denne heap dump kan indeholde dine personlige oplysninger, som appen har adgang til." + + + + + + "Vælg en handling for teksten" "Lydstyrke for opkald" "Lydstyrke for medier" @@ -1254,8 +1294,10 @@ "Tryk for at se alle netværk" "Opret forbindelse" "Alle netværk" - "%s har foreslået et tilgængeligt Wi-Fi-netværk" - "Vil du oprette forbindelse til netværk, som %s foreslår?" + + + + "Ja" "Nej" "Wi‑Fi aktiveres automatisk" @@ -1267,9 +1309,14 @@ "Log ind på netværk" - "Wi-Fi-netværket har ikke internetadgang" + + "Tryk for at se valgmuligheder" "Der er oprettet forbindelse" + + + + "Ændringer af dine indstillinger for hotspot" "Dit hotspotbånd er ændret." "Denne enhed understøtter ikke din præference om kun 5 GHz. Denne enhed vil i stedet bruge 5 GHz-båndet, når det er muligt." @@ -1354,6 +1401,10 @@ "USB-fejlretning er tilsluttet" "Tryk for at deaktivere USB-fejlretning" "Vælg for at deaktivere USB-fejlretning." + + + + "Væske eller snavs i USB-porten" "USB-porten deaktiveres automatisk. Tryk for at få flere oplysninger." "Det er sikkert at bruge USB-porten" @@ -1905,8 +1956,6 @@ "Tryk for at låse profilen op" "Tilsluttet %1$s" "Tryk for at se filer" - "Fastgør" - "Frigør" "Appinfo" "−%1$s" "Starter demoen…" @@ -1997,6 +2046,22 @@ "Notifikation med oplysninger om rutinetilstand" "Enheden løber muligvis tør for batteri, inden du normalt oplader den" "Batterisparefunktion er aktiveret for at forlænge batteritiden" + + + + + + + + + + + + + + + + "Mappe" "Android-app" "Fil" diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index f4f7918a1435c4607e46bdb5c5e1342f7ed38d8e..538151159ce9087c0b2c1a05141cfd867fedbb48 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -141,8 +141,10 @@ "Κλήση Wi-Fi" "VoWifi" "Ανενεργό" - "Προτίμηση Wi-Fi" - "Προτίμηση δικτύου κινητής τηλεφωνίας" + + + + "Μόνο Wi-Fi" "{0}: Δεν προωθήθηκε" "{0}: {1}" @@ -228,7 +230,8 @@ "Αναφορά σφαλμάτων" "Λήξη περιόδου σύνδεσης" "Στιγμιότυπο οθόνης" - "Λήψη αναφοράς σφάλματος" + + "Θα συλλέξει πληροφορίες σχετικά με την τρέχουσα κατάσταση της συσκευής σας και θα τις στείλει μέσω μηνύματος ηλεκτρονικού ταχυδρομείου. Απαιτείται λίγος χρόνος για τη σύνταξη της αναφοράς σφάλματος και την αποστολή της. Κάντε λίγη υπομονή." "Διαδραστική αναφορά" "Χρησιμοποιήστε αυτήν την επιλογή στις περισσότερες περιπτώσεις. Σας επιτρέπει να παρακολουθείτε την πρόοδο της αναφοράς, να εισάγετε περισσότερες λεπτομέρειες σχετικά με το πρόβλημα που αντιμετωπίζετε και να τραβήξετε στιγμιότυπα οθόνης. Ενδέχεται να παραλείψει ορισμένες ενότητες που δεν χρησιμοποιούνται συχνά και για τις οποίες απαιτείται μεγάλο χρονικό διάστημα για τη δημιουργία αναφορών." @@ -281,9 +284,12 @@ "Τοποθεσία" "έχει πρόσβαση στην τοποθεσία της συσκευής" "Να επιτρέπεται στην εφαρμογή <b>%1$s</b> να έχει πρόσβαση στην τοποθεσία αυτής της συσκευής;" - "Η εφαρμογή θα έχει πρόσβαση στην τοποθεσία μόνο κατά τη διάρκεια χρήσης της εφαρμογής." - "Να επιτρέπεται πάντα στην εφαρμογή <b>%1$s</b> να έχει πρόσβαση στην τοποθεσία αυτής της συσκευής;" - "Η εφαρμογή θα έχει πάντα πρόσβαση στην τοποθεσία, ακόμα και όταν δεν χρησιμοποιείτε την εφαρμογή." + + + + + + "Ημερολόγιο" "έχει πρόσβαση στο ημερολόγιό σας" "Να επιτρέπεται στην εφαρμογή <b>%1$s</b> να έχει πρόσβαση στο ημερολόγιό σας;" @@ -316,7 +322,10 @@ "Να επιτρέπεται στην εφαρμογή <b>%1$s</b> να έχει πρόσβαση στη μουσική σας;" "Φωτογραφίες και βίντεο" "πρόσβαση στις φωτογραφίες και στα βίντεό σας" - "Να επιτρέπεται η πρόσβαση του <b>%1$s</b> σε φωτογραφίες και βίντεο, μεταξύ άλλων σε επισημασμένες τοποθεσίες;" + + + + "Ανάκτηση του περιεχομένου του παραθύρου" "Έλεγχος του περιεχομένου ενός παραθύρου με το οποίο αλληλεπιδράτε." "Ενεργοποίηση της \"Εξερεύνησης με άγγιγμα\"" @@ -509,8 +518,10 @@ "Επιτρέπει στην εφαρμογή την επικοινωνία με ετικέτες, κάρτες και αναγνώστες της Επικοινωνίας κοντινού πεδίου (NFC)." "απενεργοποιεί το κλείδωμα οθόνης" "Επιτρέπει στην εφαρμογή την απενεργοποίηση του κλειδώματος πληκτρολογίου και άλλης σχετικής ασφάλειας με κωδικό πρόσβασης. Για παράδειγμα, το κλείδωμα πληκτρολογίου στο τηλέφωνο απενεργοποιείται όταν λαμβάνεται εισερχόμενη τηλεφωνική κλήση και ενεργοποιείται ξανά όταν η κλήση τερματιστεί." - "υποβολή αιτήματος για πολυπλοκότητα οθόνης κλειδώματος" - "Επιτρέπει στην εφαρμογή να μάθει το επίπεδο πολυπλοκότητας του κλειδώματος οθόνης (υψηλό, μέσο, χαμηλό ή κανένα), το οποίο υποδεικνύει το πιθανό εύρος του μήκους και του τύπου κλειδώματος οθόνης. Η εφαρμογή μπορεί επίσης να προτείνει στους χρήστες να ενημερώσουν το κλείδωμα οθόνης σε ένα συγκεκριμένο επίπεδο, όμως οι χρήστες μπορούν να την αγνοήσουν και να συνεχίσουν. Λάβετε υπόψη ότι το κλείδωμα οθόνης δεν αποθηκεύεται σε απλό κείμενο. Συνεπώς, η εφαρμογή δεν γνωρίζει τον κωδικό." + + + + "χρήση βιομετρικού εξοπλισμού" "Επιτρέπει στην εφαρμογή να χρησιμοποιεί βιομετρικό εξοπλισμό για έλεγχο ταυτότητας" "διαχειρίζεται τον εξοπλισμό δακτυλικού αποτυπώματος" @@ -565,37 +576,59 @@ "Επιτρέπει στην εφαρμογή να επικαλείται μεθόδους προσθήκης/διαγραφής προτύπων για χρήση." "χρήση υλικολογισμικού ελέγχου ταυτότητας προσώπου" "Επιτρέπει στην εφαρμογή να χρησιμοποιεί υλικολογισμικό για έλεγχο ταυτότητας" - "Αδυναμία επεξεργασίας προσώπου. Δοκιμάστε ξανά." - "Υψηλή φωτεινότητα. Δοκιμάστε χαμηλότερο φωτισμό." - "Πολύ σκοτεινό πρόσωπο. Ξεσκεπάστε την πηγή φωτός." - "Μετακινήστε τον αισθητήρα πιο μακριά." - "Τοποθετήστε τον αισθητήρα πιο κοντά στο πρόσωπο." - "Μετακινήστε τον αισθητήρα ψηλότερα." - "Μετακινήστε τον αισθητήρα χαμηλότερα." - "Μετακινήστε τον αισθητήρα προς τα δεξιά." - "Μετακινήστε τον αισθητήρα προς τα αριστερά." - "Κοιτάξτε στον αισθητήρα." - "Δεν εντοπίστηκε κάποιο πρόσωπο." - "Υπερβολικά πολλή κίνηση." + + + + + + + + + + + + + + + + + + + + + + + + "Καταχωρίστε ξανά το πρόσωπό σας." - "Ανιχνεύτηκε διαφορετικό πρόσωπο." + + "Πολύ παρόμοιο, αλλάξτε την πόζα σας." - "Κοιτάξτε απευθείας στην κάμερα." - "Κοιτάξτε απευθείας στην κάμερα." + + + + "Ευθυγραμμίστε το κεφάλι σας στον κατακόρυφο άξονα." - "Μην καλύπτετε το πρόσωπό σας." + + + + "Το υλικολογισμικό προσώπου δεν είναι διαθέσιμο." - "Λήξη χρονικού ορίου προσώπου. Δοκιμάστε ξανά." + + "Δεν είναι δυνατή η αποθήκευση του προσώπου." "Η ενέργεια προσώπου ακυρώθηκε." "Ο έλεγχος ταυτότητας προσώπου ακυρώθηκε από τον χρήστη." "Πάρα πολλές προσπάθειες. Δοκιμάστε ξανά αργότερα." "Πολλές προσπάθειες. Αποτυχία ελέγ. ταυτ. προσώπου." - "Δοκιμάστε ξανά." - "Δεν έχει καταχωριστεί κάποιο πρόσωπο." - "Η συσκευή δεν διαθέτει αισθητήρα ελέγχου ταυτότ. προσώπου." + + + + + + "Πρόσωπο %d" @@ -1213,9 +1246,16 @@ "Ανοίξτε την εφαρμογή %1$s" "Η εφαρμογή %1$s θα κλείσει χωρίς αποθήκευση" "Η διαδικασία %1$s υπερβαίνει το όριο μνήμης" + + "Το στιγμιότυπο οθόνης λήφθηκε. Πατήστε για κοινοποίηση." "Κοινή χρήση στιγμιότυπου μνήμης;" - "Η διαδικασία %1$s υπερβαίνει το όριο μνήμης %2$s. Είναι διαθέσιμο ένα στιγμιότυπο μνήμης για να μοιραστείτε με τον προγραμματιστή. Να είστε προσεκτικοί: αυτό το στιγμιότυπο μνήμης μπορεί να περιέχει οποιοδήποτε από τα προσωπικά σας στοιχεία στα οποία έχει πρόσβαση η εφαρμογή." + + + + + + "Επιλέξτε μια ενέργεια για το κείμενο" "Ένταση ήχου ειδοποίησης" "Ένταση ήχου πολυμέσων" @@ -1254,8 +1294,10 @@ "Πατήστε για να δείτε όλα τα δίκτυα" "Σύνδεση" "Όλα τα δίκτυα" - "Είναι διαθέσιμο ένα δίκτυο Wi‑Fi που προτείνεται από %s" - "Θέλετε να συνδέσετε τα δίκτυα που προτείνονται από %s;" + + + + "Ναι" "Όχι" "Το Wi‑Fi θα ενεργοποιηθεί αυτόματα" @@ -1267,9 +1309,14 @@ "Σύνδεση στο δίκτυο" - "Το Wi-Fi δεν έχει πρόσβαση στο διαδίκτυο" + + "Πατήστε για να δείτε τις επιλογές" "Συνδέθηκε" + + + + "Αλλαγές στις ρυθμίσεις σημείου πρόσβασης Wi-Fi" "Το εύρος σημείου πρόσβασης Wi-Fi άλλαξε." "Αυτή η συσκευή δεν υποστηρίζει την προτίμησή σας για τη ζώνη 5 GHz μόνο. Αντ\' αυτού, αυτή η συσκευή θα χρησιμοποιεί τη ζώνη 5 GHz όταν είναι διαθέσιμη." @@ -1354,6 +1401,10 @@ "Συνδέθηκε ο εντοπισμός σφαλμάτων USB" "Πατήστε για να απενεργοποιήσετε τον εντοπισμό και τη διόρθωση σφαλμάτων USB" "Επιλογή για απενεργοποίηση του εντοπισμού σφαλμάτων USB." + + + + "Υγρασία ή ακαθαρσίες στη θύρα USB" "Η θύρα USB απενεργοποιείται αυτόματα. Πατήστε για να μάθετε περισσότερα." "Μπορείτε να χρησιμοποιήσετε με ασφάλεια τη θύρα USB" @@ -1905,8 +1956,6 @@ "Πατήστε για ξεκλ. προφίλ εργ." "Συνδέθηκε με το %1$s" "Πατήστε για να δείτε τα αρχεία" - "Καρφίτσωμα" - "Ξεκαρφίτσωμα" "Πληροφορίες εφαρμογής" "−%1$s" "Έναρξη επίδειξης…" @@ -1997,6 +2046,22 @@ "Ειδοποίηση πληροφοριών λειτουργίας Ρουτίνας" "Η μπαταρία μπορεί να εξαντληθεί πριν από τη συνηθισμένη φόρτιση" "Η Εξοικονόμηση μπαταρίας ενεργοποιήθηκε για την επέκταση της διάρκειας ζωής της μπαταρίας" + + + + + + + + + + + + + + + + "Φάκελος" "Εφαρμογή Android" "Αρχείο" diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 8ed7bd8863801be8c814caa43d964d5aa333a682..a90fef7db74f01b4c13f7cb78c83b8ef581d0479 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -141,8 +141,10 @@ "Wi-Fi Calling" "VoWifi" "Off" - "Wi-Fi preferred" - "Mobile preferred" + + + + "Wi-Fi only" "{0}: Not forwarded" "{0}: {1}" @@ -228,7 +230,8 @@ "Bug report" "End session" "Screenshot" - "Take bug report" + + "This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient." "Interactive report" "Use this under most circumstances. It allows you to track progress of the report, enter more details about the problem and take screenshots. It might omit some less-used sections that take a long time to report." @@ -281,9 +284,12 @@ "Location" "access this device\'s location" "Allow <b>%1$s</b> to access this device\'s location?" - "The app will only have access to the location while you’re using the app." - "Always allow <b>%1$s</b> to access this device’s location?" - "The app will always have access to the location, even when you’re not using the app." + + + + + + "Calendar" "access your calendar" "Allow <b>%1$s</b> to access your calendar?" @@ -316,7 +322,10 @@ "Allow <b>%1$s</b> to access your music?" "Photos & videos" "access your photos & videos" - "Allow <b>%1$s</b> to access your photos and videos, including tagged locations?" + + + + "Retrieve window content" "Inspect the content of a window that you\'re interacting with." "Turn on Explore by Touch" @@ -509,8 +518,10 @@ "Allows the app to communicate with Near Field Communication (NFC) tags, cards and readers." "disable your screen lock" "Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished." - "request screen lock complexity" - "Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plain text so the app does not know the exact password." + + + + "use biometric hardware" "Allows the app to use biometric hardware for authentication" "manage fingerprint hardware" @@ -565,37 +576,59 @@ "Allows the app to invoke methods to add and delete facial templates for use." "use face authentication hardware" "Allows the app to use face authentication hardware for authentication" - "Couldn’t process face. Please try again." - "Face is too bright. Please try in lower light." - "Face is too dark. Please uncover light source." - "Please move sensor farther away from face." - "Please bring sensor closer to face." - "Please move sensor higher." - "Please move sensor lower." - "Please move sensor to the right." - "Please move sensor to the left." - "Please look at the sensor." - "No face detected." - "Too much motion." + + + + + + + + + + + + + + + + + + + + + + + + "Please re-enroll your face." - "Different face detected." + + "Too similar, please change your pose." - "Please look more directly at the camera." - "Please look more directly at the camera." + + + + "Please straighten your head vertically." - "Please uncover your face." + + + + "Face hardware not available." - "Face time out reached. Try again." + + "Face can’t be stored." "Face operation cancelled." "Face authentication cancelled by user." "Too many attempts. Try again later." "Too many attempts. Facial authentication disabled." - "Try again." - "No face enrolled." - "This device does not have a face authentication sensor." + + + + + + "Face %d" @@ -1213,9 +1246,16 @@ "Open %1$s" "%1$s will close without saving" "%1$s exceeded memory limit" + + "Heap dump collected. Tap to share." "Share heap dump?" - "The process %1$s has exceeded its process memory limit of %2$s. A heap dump is available for you to share with its developer. Be careful: this heap dump can contain any of your personal information that the application has access to." + + + + + + "Choose an action for text" "Ringer volume" "Media volume" @@ -1254,8 +1294,10 @@ "Tap to see all networks" "Connect" "All networks" - "A Wi‑Fi network proposed by %s is available" - "Do you want to connect to networks proposed by %s?" + + + + "Yes" "No" "Wi‑Fi will turn on automatically" @@ -1267,9 +1309,14 @@ "Sign in to network" - "Wi-Fi has no Internet access" + + "Tap for options" "Connected" + + + + "Changes to your hotspot settings" "Your hotspot band has changed." "This device doesn’t support your preference for 5 GHz only. Instead, this device will use the 5 GHz band when available." @@ -1354,6 +1401,10 @@ "USB debugging connected" "Tap to turn off USB debugging" "Select to disable USB debugging." + + + + "Liquid or debris in USB port" "USB port is automatically disabled. Tap to learn more." "Safe to use USB port" @@ -1905,8 +1956,6 @@ "Tap to unlock work profile" "Connected to %1$s" "Tap to view files" - "Pin" - "Unpin" "App info" "−%1$s" "Starting demo…" @@ -1997,6 +2046,22 @@ "Routine Mode info notification" "Battery may run out before usual charge" "Battery Saver activated to extend battery life" + + + + + + + + + + + + + + + + "Folder" "Android application" "File" diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 722d1326f39efb41d726c9e4e5b612569dc69f70..a10300e8f9fd7fad7c1be4edaf82f68d75f6d07e 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -141,8 +141,10 @@ "Wi-Fi Calling" "VoWifi" "Off" - "Wi-Fi preferred" - "Mobile preferred" + + + + "Wi-Fi only" "{0}: Not forwarded" "{0}: {1}" @@ -228,7 +230,8 @@ "Bug report" "End session" "Screenshot" - "Take bug report" + + "This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient." "Interactive report" "Use this under most circumstances. It allows you to track progress of the report, enter more details about the problem and take screenshots. It might omit some less-used sections that take a long time to report." @@ -281,9 +284,12 @@ "Location" "access this device\'s location" "Allow <b>%1$s</b> to access this device\'s location?" - "The app will only have access to the location while you’re using the app." - "Always allow <b>%1$s</b> to access this device’s location?" - "The app will always have access to the location, even when you’re not using the app." + + + + + + "Calendar" "access your calendar" "Allow <b>%1$s</b> to access your calendar?" @@ -316,7 +322,10 @@ "Allow <b>%1$s</b> to access your music?" "Photos & videos" "access your photos & videos" - "Allow <b>%1$s</b> to access your photos and videos, including tagged locations?" + + + + "Retrieve window content" "Inspect the content of a window that you\'re interacting with." "Turn on Explore by Touch" @@ -509,8 +518,10 @@ "Allows the app to communicate with Near Field Communication (NFC) tags, cards and readers." "disable your screen lock" "Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished." - "request screen lock complexity" - "Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plain text so the app does not know the exact password." + + + + "use biometric hardware" "Allows the app to use biometric hardware for authentication" "manage fingerprint hardware" @@ -565,37 +576,59 @@ "Allows the app to invoke methods to add and delete facial templates for use." "use face authentication hardware" "Allows the app to use face authentication hardware for authentication" - "Couldn’t process face. Please try again." - "Face is too bright. Please try in lower light." - "Face is too dark. Please uncover light source." - "Please move sensor farther away from face." - "Please bring sensor closer to face." - "Please move sensor higher." - "Please move sensor lower." - "Please move sensor to the right." - "Please move sensor to the left." - "Please look at the sensor." - "No face detected." - "Too much motion." + + + + + + + + + + + + + + + + + + + + + + + + "Please re-enroll your face." - "Different face detected." + + "Too similar, please change your pose." - "Please look more directly at the camera." - "Please look more directly at the camera." + + + + "Please straighten your head vertically." - "Please uncover your face." + + + + "Face hardware not available." - "Face time out reached. Try again." + + "Face can’t be stored." "Face operation cancelled." "Face authentication cancelled by user." "Too many attempts. Try again later." "Too many attempts. Facial authentication disabled." - "Try again." - "No face enrolled." - "This device does not have a face authentication sensor." + + + + + + "Face %d" @@ -1213,9 +1246,16 @@ "Open %1$s" "%1$s will close without saving" "%1$s exceeded memory limit" + + "Heap dump collected. Tap to share." "Share heap dump?" - "The process %1$s has exceeded its process memory limit of %2$s. A heap dump is available for you to share with its developer. Be careful: this heap dump can contain any of your personal information that the application has access to." + + + + + + "Choose an action for text" "Ringer volume" "Media volume" @@ -1254,8 +1294,10 @@ "Tap to see all networks" "Connect" "All networks" - "A Wi‑Fi network proposed by %s is available" - "Do you want to connect to networks proposed by %s?" + + + + "Yes" "No" "Wi‑Fi will turn on automatically" @@ -1267,9 +1309,14 @@ "Sign in to network" - "Wi-Fi has no Internet access" + + "Tap for options" "Connected" + + + + "Changes to your hotspot settings" "Your hotspot band has changed." "This device doesn’t support your preference for 5 GHz only. Instead, this device will use the 5 GHz band when available." @@ -1354,6 +1401,10 @@ "USB debugging connected" "Tap to turn off USB debugging" "Select to disable USB debugging." + + + + "Liquid or debris in USB port" "USB port is automatically disabled. Tap to learn more." "Safe to use USB port" @@ -1905,8 +1956,6 @@ "Tap to unlock work profile" "Connected to %1$s" "Tap to view files" - "Pin" - "Unpin" "App info" "−%1$s" "Starting demo…" @@ -1997,6 +2046,22 @@ "Routine Mode info notification" "Battery may run out before usual charge" "Battery Saver activated to extend battery life" + + + + + + + + + + + + + + + + "Folder" "Android application" "File" diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 8ed7bd8863801be8c814caa43d964d5aa333a682..a90fef7db74f01b4c13f7cb78c83b8ef581d0479 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -141,8 +141,10 @@ "Wi-Fi Calling" "VoWifi" "Off" - "Wi-Fi preferred" - "Mobile preferred" + + + + "Wi-Fi only" "{0}: Not forwarded" "{0}: {1}" @@ -228,7 +230,8 @@ "Bug report" "End session" "Screenshot" - "Take bug report" + + "This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient." "Interactive report" "Use this under most circumstances. It allows you to track progress of the report, enter more details about the problem and take screenshots. It might omit some less-used sections that take a long time to report." @@ -281,9 +284,12 @@ "Location" "access this device\'s location" "Allow <b>%1$s</b> to access this device\'s location?" - "The app will only have access to the location while you’re using the app." - "Always allow <b>%1$s</b> to access this device’s location?" - "The app will always have access to the location, even when you’re not using the app." + + + + + + "Calendar" "access your calendar" "Allow <b>%1$s</b> to access your calendar?" @@ -316,7 +322,10 @@ "Allow <b>%1$s</b> to access your music?" "Photos & videos" "access your photos & videos" - "Allow <b>%1$s</b> to access your photos and videos, including tagged locations?" + + + + "Retrieve window content" "Inspect the content of a window that you\'re interacting with." "Turn on Explore by Touch" @@ -509,8 +518,10 @@ "Allows the app to communicate with Near Field Communication (NFC) tags, cards and readers." "disable your screen lock" "Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished." - "request screen lock complexity" - "Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plain text so the app does not know the exact password." + + + + "use biometric hardware" "Allows the app to use biometric hardware for authentication" "manage fingerprint hardware" @@ -565,37 +576,59 @@ "Allows the app to invoke methods to add and delete facial templates for use." "use face authentication hardware" "Allows the app to use face authentication hardware for authentication" - "Couldn’t process face. Please try again." - "Face is too bright. Please try in lower light." - "Face is too dark. Please uncover light source." - "Please move sensor farther away from face." - "Please bring sensor closer to face." - "Please move sensor higher." - "Please move sensor lower." - "Please move sensor to the right." - "Please move sensor to the left." - "Please look at the sensor." - "No face detected." - "Too much motion." + + + + + + + + + + + + + + + + + + + + + + + + "Please re-enroll your face." - "Different face detected." + + "Too similar, please change your pose." - "Please look more directly at the camera." - "Please look more directly at the camera." + + + + "Please straighten your head vertically." - "Please uncover your face." + + + + "Face hardware not available." - "Face time out reached. Try again." + + "Face can’t be stored." "Face operation cancelled." "Face authentication cancelled by user." "Too many attempts. Try again later." "Too many attempts. Facial authentication disabled." - "Try again." - "No face enrolled." - "This device does not have a face authentication sensor." + + + + + + "Face %d" @@ -1213,9 +1246,16 @@ "Open %1$s" "%1$s will close without saving" "%1$s exceeded memory limit" + + "Heap dump collected. Tap to share." "Share heap dump?" - "The process %1$s has exceeded its process memory limit of %2$s. A heap dump is available for you to share with its developer. Be careful: this heap dump can contain any of your personal information that the application has access to." + + + + + + "Choose an action for text" "Ringer volume" "Media volume" @@ -1254,8 +1294,10 @@ "Tap to see all networks" "Connect" "All networks" - "A Wi‑Fi network proposed by %s is available" - "Do you want to connect to networks proposed by %s?" + + + + "Yes" "No" "Wi‑Fi will turn on automatically" @@ -1267,9 +1309,14 @@ "Sign in to network" - "Wi-Fi has no Internet access" + + "Tap for options" "Connected" + + + + "Changes to your hotspot settings" "Your hotspot band has changed." "This device doesn’t support your preference for 5 GHz only. Instead, this device will use the 5 GHz band when available." @@ -1354,6 +1401,10 @@ "USB debugging connected" "Tap to turn off USB debugging" "Select to disable USB debugging." + + + + "Liquid or debris in USB port" "USB port is automatically disabled. Tap to learn more." "Safe to use USB port" @@ -1905,8 +1956,6 @@ "Tap to unlock work profile" "Connected to %1$s" "Tap to view files" - "Pin" - "Unpin" "App info" "−%1$s" "Starting demo…" @@ -1997,6 +2046,22 @@ "Routine Mode info notification" "Battery may run out before usual charge" "Battery Saver activated to extend battery life" + + + + + + + + + + + + + + + + "Folder" "Android application" "File" diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 8ed7bd8863801be8c814caa43d964d5aa333a682..a90fef7db74f01b4c13f7cb78c83b8ef581d0479 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -141,8 +141,10 @@ "Wi-Fi Calling" "VoWifi" "Off" - "Wi-Fi preferred" - "Mobile preferred" + + + + "Wi-Fi only" "{0}: Not forwarded" "{0}: {1}" @@ -228,7 +230,8 @@ "Bug report" "End session" "Screenshot" - "Take bug report" + + "This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient." "Interactive report" "Use this under most circumstances. It allows you to track progress of the report, enter more details about the problem and take screenshots. It might omit some less-used sections that take a long time to report." @@ -281,9 +284,12 @@ "Location" "access this device\'s location" "Allow <b>%1$s</b> to access this device\'s location?" - "The app will only have access to the location while you’re using the app." - "Always allow <b>%1$s</b> to access this device’s location?" - "The app will always have access to the location, even when you’re not using the app." + + + + + + "Calendar" "access your calendar" "Allow <b>%1$s</b> to access your calendar?" @@ -316,7 +322,10 @@ "Allow <b>%1$s</b> to access your music?" "Photos & videos" "access your photos & videos" - "Allow <b>%1$s</b> to access your photos and videos, including tagged locations?" + + + + "Retrieve window content" "Inspect the content of a window that you\'re interacting with." "Turn on Explore by Touch" @@ -509,8 +518,10 @@ "Allows the app to communicate with Near Field Communication (NFC) tags, cards and readers." "disable your screen lock" "Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished." - "request screen lock complexity" - "Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plain text so the app does not know the exact password." + + + + "use biometric hardware" "Allows the app to use biometric hardware for authentication" "manage fingerprint hardware" @@ -565,37 +576,59 @@ "Allows the app to invoke methods to add and delete facial templates for use." "use face authentication hardware" "Allows the app to use face authentication hardware for authentication" - "Couldn’t process face. Please try again." - "Face is too bright. Please try in lower light." - "Face is too dark. Please uncover light source." - "Please move sensor farther away from face." - "Please bring sensor closer to face." - "Please move sensor higher." - "Please move sensor lower." - "Please move sensor to the right." - "Please move sensor to the left." - "Please look at the sensor." - "No face detected." - "Too much motion." + + + + + + + + + + + + + + + + + + + + + + + + "Please re-enroll your face." - "Different face detected." + + "Too similar, please change your pose." - "Please look more directly at the camera." - "Please look more directly at the camera." + + + + "Please straighten your head vertically." - "Please uncover your face." + + + + "Face hardware not available." - "Face time out reached. Try again." + + "Face can’t be stored." "Face operation cancelled." "Face authentication cancelled by user." "Too many attempts. Try again later." "Too many attempts. Facial authentication disabled." - "Try again." - "No face enrolled." - "This device does not have a face authentication sensor." + + + + + + "Face %d" @@ -1213,9 +1246,16 @@ "Open %1$s" "%1$s will close without saving" "%1$s exceeded memory limit" + + "Heap dump collected. Tap to share." "Share heap dump?" - "The process %1$s has exceeded its process memory limit of %2$s. A heap dump is available for you to share with its developer. Be careful: this heap dump can contain any of your personal information that the application has access to." + + + + + + "Choose an action for text" "Ringer volume" "Media volume" @@ -1254,8 +1294,10 @@ "Tap to see all networks" "Connect" "All networks" - "A Wi‑Fi network proposed by %s is available" - "Do you want to connect to networks proposed by %s?" + + + + "Yes" "No" "Wi‑Fi will turn on automatically" @@ -1267,9 +1309,14 @@ "Sign in to network" - "Wi-Fi has no Internet access" + + "Tap for options" "Connected" + + + + "Changes to your hotspot settings" "Your hotspot band has changed." "This device doesn’t support your preference for 5 GHz only. Instead, this device will use the 5 GHz band when available." @@ -1354,6 +1401,10 @@ "USB debugging connected" "Tap to turn off USB debugging" "Select to disable USB debugging." + + + + "Liquid or debris in USB port" "USB port is automatically disabled. Tap to learn more." "Safe to use USB port" @@ -1905,8 +1956,6 @@ "Tap to unlock work profile" "Connected to %1$s" "Tap to view files" - "Pin" - "Unpin" "App info" "−%1$s" "Starting demo…" @@ -1997,6 +2046,22 @@ "Routine Mode info notification" "Battery may run out before usual charge" "Battery Saver activated to extend battery life" + + + + + + + + + + + + + + + + "Folder" "Android application" "File" diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index a8173dd486d021e90e3111365815bd11699da0ab..a53135592443b019856ae2832760fccf7f6210bc 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -141,8 +141,10 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‏‎‎‎‏‎‏‏‎‏‏‏‎‏‎‎‏‏‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‏‎WiFi Calling‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‏‎VoWifi‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‎‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‏‎‎‏‎‎‎‏‎‎Off‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‎‏‎‏‏‎‎‏‎‎‎‎‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‏‏‎Wi-Fi preferred‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‎‎‏‎‏‏‏‏‎‏‎‎‎Mobile preferred‎‏‎‎‏‎" + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‎‎‎‏‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‎‏‎‎‎‎‏‏‎‎‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎‎‎‎Wi-Fi only‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‏‏‎‏‏‎‏‎‎‏‏‏‎‏‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‎‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎{0}‎‏‎‎‏‏‏‎: Not forwarded‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‏‎‎‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‎‏‎‏‎‏‎‎‏‏‏‎‎‏‎‎‏‎‎‏‏‎{0}‎‏‎‎‏‏‏‎: ‎‏‎‎‏‏‎{1}‎‏‎‎‏‏‏‎‎‏‎‎‏‎" @@ -228,7 +230,8 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‎‏‏‎‏‎‎‎‏‎‎‏‎‎‏‎‏‎‏‏‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‏‎Bug report‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‎‏‏‎‎‎‏‏‏‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎‎‏‎‎End session‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‏‎‏‎‎‎‎‎‎‎‎‏‎‎‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‎Screenshot‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‏‎‎‏‏‎‏‏‎‎‎‎‏‏‎‎‏‎‎‎‎‎‏‏‎‎‏‎‏‎‎‎‏‏‎‎‏‏‎‎‎‏‎‎‎‏‏‎‏‎‏‎Take bug report‎‏‎‎‏‎" + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‎‎‎‏‏‏‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‎‎‏‏‏‏‎‎‎‎This will collect information about your current device state, to send as an e-mail message. It will take a little time from starting the bug report until it is ready to be sent; please be patient.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‎‎‏‎‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‎‎‏‏‏‎‎‎‎‏‏‏‏‎Interactive report‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‎‏‎‏‎‏‏‏‎‎‎‎‎‎‏‎‏‎‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‎‎‎‎‎‎‎Use this under most circumstances. It allows you to track progress of the report, enter more details about the problem, and take screenshots. It might omit some less-used sections that take a long time to report.‎‏‎‎‏‎" @@ -281,9 +284,12 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‎‎‎‎‎‏‏‏‏‎‏‎‎‎‎‎‏‎‏‎‎‎‎‎‎‎‎‎‏‏‎‏‎‎‎‎‏‎‏‎‎‎Location‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‎‎‎‎‎‎‏‎‎‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎‎‎‏‏‎‎‎‏‏‏‎‏‏‏‎‎‏‎access this device\'s location‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‎‏‎‎‏‎‏‎‏‎‏‎‎‎‏‎‏‏‎‏‎‏‏‎‏‏‎‏‎‏‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‏‏‎Allow <b>‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎</b> to access this device\'s location?‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‏‏‏‎‎‏‎‎‎‎The app will only have access to the location while you’re using the app.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‎‏‏‎‎Always allow <b>‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎</b> to access this device’s location?‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‏‏‏‎‎‏‏‎‏‎‏‎‎‎‏‎‏‎‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎The app will always have access to the location, even when you’re not using the app.‎‏‎‎‏‎" + + + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‏‎‏‏‏‎‏‎‎‎‏‏‏‎‎‏‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‏‏‏‏‎‎Calendar‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‎‏‎‎access your calendar‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‎‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‎Allow <b>‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎</b> to access your calendar?‎‏‎‎‏‎" @@ -316,7 +322,10 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‎‏‏‏‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‎‏‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‎‎Allow <b>‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎</b> to access your music?‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‏‎‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‎‏‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‎Photos & videos‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‎‏‎‎‏‎‏‎‏‎‎‏‎‏‏‎‏‎‏‎‎‎‎‏‎‏‎‏‏‏‏‎‏‎‎access your photos & videos‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‏‏‎‎‏‏‏‎‎‏‏‎‎‏‎‎Allow <b>‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎</b> to access your photos and videos, including tagged locations?‎‏‎‎‏‎" + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎‏‎‏‏‎‏‎‏‏‏‏‏‏‎‎‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‏‏‏‏‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‎‎‎Retrieve window content‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‎‎‏‏‎‏‎‎‎‏‎‏‎‏‎‏‎‎‎‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‏‏‎‏‎‏‏‏‎‏‏‎‏‎‎‎‎‎Inspect the content of a window you\'re interacting with.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‏‎‎‏‎‎‎‏‏‎‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‎‎‎‎‎‏‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎Turn on Explore by Touch‎‏‎‎‏‎" @@ -509,8 +518,10 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‏‎Allows the app to communicate with Near Field Communication (NFC) tags, cards, and readers.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‎‎disable your screen lock‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‎‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‏‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‏‏‎‎‎‎‎Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‏‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎request screen lock complexity‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‏‎‎‏‎‎‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‎‎‎‎‏‎‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‎‎‏‏‎‏‎Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plaintext so the app does not know the exact password.‎‏‎‎‏‎" + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‎‎‏‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‎‎‏‎‏‎‏‏‎‎use biometric hardware‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‏‏‎‏‏‎‎‎‎‎‎‏‏‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‎‎Allows the app to use biometric hardware for authentication‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‎‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‎‎manage fingerprint hardware‎‏‎‎‏‎" @@ -565,37 +576,59 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‎‏‎‏‎‏‏‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‏‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‎‎‏‎‎Allows the app to invoke methods to add and delete facial templates for use.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‏‎‏‏‎‎‎‎‏‎‏‎‎‎‎‎‏‏‎‏‏‎‎‎‏‎‎‎‏‎‏‎‏‏‏‎‏‎‏‏‎‎‏‏‎‏‏‏‎use face authentication hardware‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‎‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‎‏‏‎‏‏‎‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‎‎‏‎Allows the app to use face authentication hardware for authentication‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‎‏‎‎‏‎‎‎‏‎‏‎‎‏‎‎‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‎‎Couldn’t process face. Please try again.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‏‏‎‎Face is too bright. Please try in lower light.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‏‎‎‏‏‎‎‏‎‎‏‎‏‎‎‏‏‎‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‎‎‏‏‎‎‏‎‎‎‎‎‏‏‎Face is too dark. Please uncover light source.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‏‏‏‎‏‎Please move sensor farther from face.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‎‏‏‏‎Please bring sensor closer to face.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‎‏‎‏‎‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‎‏‎‎‎Please move sensor higher.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‎‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‏‏‎‏‎‏‎Please move sensor lower.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‏‏‎‏‎‏‎‎‏‏‎‏‎‎‎‎Please move sensor to the right.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‎‏‎‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‏‎‎‏‎Please move sensor to the left.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎Please look at the sensor.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎‏‏‎‏‏‏‏‏‎No face detected.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‏‏‏‎‎‏‎‎‎‎‏‎‎‏‏‏‎‎‎‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‎‎‏‎‎‎‎‏‏‏‎‎Too much motion.‎‏‎‎‏‎" + + + + + + + + + + + + + + + + + + + + + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‎‏‎‏‎‏‎‎‏‎‎‎‏‎‎‎‏‏‎‎‎‏‎‏‎‎‏‏‎‎‎‎‏‎‏‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‏‎Please re-enroll your face.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‎‎‎‎‏‎‎‎‎‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‎‎‏‎‏‎‏‎‎‎Different face detected.‎‏‎‎‏‎" + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‏‎‎‎‎‎‏‎‎‎‎‎‎‏‎‎‎‏‏‏‏‎‎‎‏‎‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎Too similar, please change your pose.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎‏‎‏‏‎‎‏‎‎‎‎‏‏‎‏‏‎‎‏‏‎‎‎‎‏‏‎‎‏‎‎‎‎‎‎‎Please look more directly at the camera.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‏‎‎‎‏‏‏‎‏‎‎‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‎‎Please look more directly at the camera.‎‏‎‎‏‎" + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‏‎‎‎‏‎‎‎‎‏‎‎‎‏‎‎‏‎‏‏‎‎‏‎‎‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎‏‏‏‏‎‏‏‎Please straighten your head vertically.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‎‎‏‎‏‏‏‎‏‎‎‎‎‏‏‏‏‏‎‏‎‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‎‏‎Please uncover your face.‎‏‎‎‏‎" + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎‏‎‏‏‎‎‏‎‎‎‏‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎‎‎‎‏‎‏‏‎‏‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎Face hardware not available.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‎‎Face time out reached. Try again.‎‏‎‎‏‎" + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‎‏‎‏‎‎‎‎‏‏‎‎‎‎‏‏‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‎‎‏‎‎Face can’t be stored.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‏‎‎‎‏‏‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‎‏‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎Face operation canceled.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎‏‏‎‏‎‎‏‎‏‎‏‏‎‎‎‏‏‏‎‏‏‎Face authentication canceled by user.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‎‎‎‎Too many attempts. Try again later.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‏‎‎‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‎‎‎‎‏‎‏‎‎‏‎‏‎‎‏‏‎‏‏‏‎‏‎‎Too many attempts. Facial authentication disabled.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‎‏‏‎‏‎‎‎‏‏‎‎‏‏‏‎‎‏‏‎‎Try again.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‎‏‏‎‏‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎No face enrolled.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‎‏‏‎‏‏‎‏‎‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎‏‎‎‎‎‏‎‏‏‎‏‏‎This device does not have a face authentication sensor.‎‏‎‎‏‎" + + + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎‏‎‏‎‎‏‏‎‏‎‎‎‎‏‎‏‎‎‎‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎Face ‎‏‎‎‏‏‎%d‎‏‎‎‏‏‏‎‎‏‎‎‏‎" @@ -1213,9 +1246,16 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‏‎‏‎‎‎‎‎‏‏‎Open ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‎‏‏‏‎‏‎‏‏‏‎‎‏‎‏‏‏‏‎‎‎‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ will close without saving‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‏‏‎‎‎‏‏‎‏‏‎‎‏‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ exceeded memory limit‎‏‎‎‏‎" + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‏‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‎‎‎‏‎‏‎‏‎‏‏‏‏‎‏‎Heap dump collected. Tap to share.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‎‎‏‎‎‎‏‎‎‏‏‎‏‎‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‎‎‎‏‎‏‏‎‎‏‎Share heap dump?‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‏‏‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‎‎‏‏‏‎‏‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‏‎‎‏‎‏‏‏‎‏‎The process ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ has exceeded its process memory limit of ‎‏‎‎‏‏‎%2$s‎‏‎‎‏‏‏‎. A heap dump is available for you to share with its developer. Be careful: this heap dump can contain any of your personal information that the application has access to.‎‏‎‎‏‎" + + + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‏‏‎‏‎‎‏‏‎‎‎‏‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‏‎‏‏‎‎Choose an action for text‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‎Ringer volume‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‏‏‎‎‏‎‎‏‏‎‎‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‏‏‏‎‎‏‎‎‏‎‏‏‎Media volume‎‏‎‎‏‎" @@ -1254,8 +1294,10 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‏‎‎‏‏‎‏‎‏‎‏‎Tap to see all networks‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‏‎‎‎‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‏‎‎‎Connect‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‏‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎All networks‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‎‏‏‏‏‎‏‏‎‏‎‎‏‎‏‎A Wi‑Fi network proposed by ‎‏‎‎‏‏‎%s‎‏‎‎‏‏‏‎ is available‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‏‎‎‎‎‎Do you want to connect to networks proposed by ‎‏‎‎‏‏‎%s‎‏‎‎‏‏‏‎?‎‏‎‎‏‎" + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‎‏‎‏‎‏‎‏‎‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‎‏‎‏‎Yes‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‏‏‎‏‎‎‎‎‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‎‏‎No‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‏‎‎‎Wi‑Fi will turn on automatically‎‏‎‎‏‎" @@ -1267,9 +1309,14 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‎‎‏‎‎‎‏‎‎‎‎‎‎‏‏‎‏‏‎‎‏‎‏‏‏‎‏‏‎‎‎‎‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‎‏‎Sign in to network‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‏‎‎‎‏‏‎‏‎‏‎‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎Wi-Fi has no internet access‎‏‎‎‏‎" + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‏‎‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‏‎‎‎‏‏‎‏‎‏‏‎‏‎Tap for options‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‏‎‏‎‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎Connected‎‏‎‎‏‎" + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎Changes to your hotspot settings‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‎‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‎‎‏‎‏‎‎‎‎‏‏‎‏‏‎Your hotspot band has changed.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‏‎‎‎‎‎‎‎‏‏‎‎‎‎‎‎‏‏‏‎‎‏‎‎‎‏‎This device doesn’t support your preference for 5GHz only. Instead, this device will use the 5GHz band when available.‎‏‎‎‏‎" @@ -1354,6 +1401,10 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‏‎‎‏‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‎‎‎‎‏‎‎‎‏‏‏‏‎‎USB debugging connected‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‎‎‏‎‎‎‏‎‎‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‎‎‎‎Tap to turn off USB debugging‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‏‎‏‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‎‎‎Select to disable USB debugging.‎‏‎‎‏‎" + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‎‏‏‎‎‏‎‏‏‎‏‎‎‏‎‎‎‎‏‏‏‎‏‎‏‎‏‏‎‎‏‏‎‏‏‎‎‎‏‏‎‏‏‏‏‎‏‏‎‏‎Liquid or debris in USB port‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‎‎‏‎‏‏‎‏‎‏‎‎‏‏‏‎‏‎‏‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‎‏‎‎USB port is automatically disabled. Tap to learn more.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‎‏‏‎‏‎‎‎‏‎‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎Safe to use USB port‎‏‎‎‏‎" @@ -1905,8 +1956,6 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‏‎‎‎‏‎‎Tap to unlock work profile‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‎‏‏‎‎‎‏‎‏‎‎‎‏‎‎‏‎‎‎Connected to ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‎‏‏‎‎‎‏‏‏‏‎‎Tap to view files‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‎‎‎‎‎‎‏‎‎‏‏‎‎‎‎‎‎‎‎‎‎‎‎‏‎‎‎‏‎‎Pin‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‏‎‏‏‎‏‏‎‎‎‏‏‎‏‏‎‎‎‎‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‎‎‎‏‎‎‏‎‎Unpin‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‏‏‏‎‎‎‏‏‏‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‎‎‎‎App info‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎‎‏‎‎‏‎‏‏‎‎‎‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‏‎‎‎‏‏‎‏‏‏‏‎‏‏‎‎‏‎‎‎‏‏‎‏‏‎−‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‏‎‏‏‎‏‎‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‏‏‎‎‎‎‏‏‏‏‎‎‎‏‎‏‏‎Starting demo…‎‏‎‎‏‎" @@ -1997,6 +2046,22 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‎Routine Mode info notification‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‏‏‎‎‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‏‎‎‏‏‎Battery may run out before usual charge‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎Battery Saver activated to extend battery life‎‏‎‎‏‎" + + + + + + + + + + + + + + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‎‏‎‏‎‏‏‏‎‏‎‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‎‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‏‎‎‎Folder‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‏‎‎‏‏‏‏‏‎‎‎‏‏‎‎‎‏‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎Android application‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‎‏‎‏‎‎‎‎‎‎‎‏‎‏‎‎‎‏‎‏‎‏‎‏‎‎‎‎‏‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎File‎‏‎‎‏‎" diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 387eea309e787e9b6289bbe0f43e9280bf34b3a7..d058728a7833d96eb33671905a714bd3e09d328b 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -141,8 +141,10 @@ "Llamada por Wi-Fi" "VoWifi" "Desactivada" - "Red Wi-Fi preferida" - "Red móvil preferida" + + + + "Solo Wi-Fi" "{0}: no se ha remitido" "{0}: {1}" @@ -228,7 +230,8 @@ "Informe de errores" "Finalizar sesión" "Captura de pantalla" - "Iniciar informe de errores" + + "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." "Informe interactivo" "Usa esta opción en la mayoría de los casos. Te permite realizar un seguimiento del progreso del informe, ingresar más detalles acerca del problema y tomar capturas de pantalla. Es posible que se omitan secciones menos usadas cuyos informes demoran más en completarse." @@ -281,9 +284,12 @@ "Ubicación" "acceder a la ubicación de este dispositivo" "¿Permitir que <b>%1$s</b> acceda a la ubicación de este dispositivo?" - "La app solo tendrá acceso a la ubicación cuando la uses." - "¿Permitir que <b>%1$s</b> acceda a la ubicación?" - "La app siempre tendrá acceso a la ubicación, incluso cuando no la uses." + + + + + + "Calendario" "acceder al calendario" "¿Permitir que <b>%1$s</b> acceda a tu calendario?" @@ -316,7 +322,10 @@ "¿Quieres permitir que <b>%1$s</b> acceda a tu música?" "Fotos y videos" "acceder a tus fotos y videos" - "¿Permitir que <b>%1$s</b> acceda a tus fotos y videos, incluidas las ubicaciones etiquetadas?" + + + + "Recuperar el contenido de las ventanas" "Inspecciona el contenido de la ventana con la que estés interactuando." "Activar la Exploración táctil" @@ -509,8 +518,10 @@ "Permite que la aplicación se comunique con lectores, tarjetas y etiquetas de Comunicación de campo cercano (NFC)." "desactivar el bloqueo de pantalla" "Permite que la aplicación desactive el bloqueo del teclado y cualquier protección con contraseña asociada. Por ejemplo, el dispositivo puede desactivar el bloqueo del teclado cuando recibe una llamada telefónica y volver a activarlo cuando finaliza la llamada." - "solicitar complejidad del bloqueo de pantalla" - "Permite que la app conozca el nivel de complejidad del bloqueo de pantalla (alta, media, baja o ninguna), lo que indica el rango de duración posible y el tipo de bloqueo. La app también puede sugerirles a los usuarios que actualicen el bloqueo de pantalla a un determinado nivel, aunque ellos pueden ignorar esta sugerencia y seguir navegando. Ten en cuenta que el bloqueo de pantalla no se almacena como texto sin formato, por lo que la app no conoce la contraseña exacta." + + + + "usar hardware biométrico" "Permite que la app use hardware biométrico para realizar la autenticación" "Administrar el hardware de huellas digitales" @@ -565,37 +576,59 @@ "Permite que la app emplee métodos para agregar y borrar plantillas de rostros para su uso." "usar el hardware de autenticación facial" "Permite que la app use el hardware de autenticación facial para reconocerte" - "Error al procesar el rostro. Vuelve a intentarlo." - "El rostro se ve muy claro. Prueba con menos luz." - "El rostro se ve muy oscuro. Prueba con más luz." - "Aleja el sensor del rostro." - "Acerca el sensor al rostro." - "Coloca el sensor más arriba." - "Coloca el sensor más abajo." - "Mueve el sensor hacia la derecha." - "Mueve el sensor hacia la izquierda." - "Mira al sensor." - "No se detectó ningún rostro." - "Te estás moviendo demasiado." + + + + + + + + + + + + + + + + + + + + + + + + "Vuelve a registrar tu cara." - "Se detectó otra cara." + + "Es muy similar a la anterior. Haz otra pose." - "Mira directamente a la cámara." - "Mira directamente a la cámara." + + + + "Mantén la cabeza en posición vertical." - "No te tapes la cara." + + + + "Hardware de reconocimiento facial no disponible" - "Se agotó el tiempo. Vuelve a intentarlo." + + "No se puede almacenar el rostro." "Se canceló el reconocimiento facial." "El usuario canceló la autenticación de rostro." "Demasiados intentos. Inténtalo de nuevo más tarde." "Demasiados intentos. Autent. facial inhabilitada." - "Vuelve a intentarlo." - "No registraste ningún rostro." - "Este dispositivo no tiene sensor de autenticación facial." + + + + + + "Rostro %d" @@ -1213,9 +1246,16 @@ "Abrir %1$s" "%1$s se cerrará sin guardar" "%1$s superó el límite de memoria." + + "Se recopiló el volcado de pila. Toca para compartir." "¿Compartir volcado de pila?" - "El proceso %1$s superó el límite de memoria de proceso de %2$s. Hay un volcado de pila disponible para que puedas compartirlo con el programador. Ten cuidado, este volcado de pila puede contener información personal a la que la aplicación tiene acceso." + + + + + + "Seleccionar una acción para el texto" "Volumen del timbre" "Volumen de los medios" @@ -1254,8 +1294,10 @@ "Presiona para ver todas las redes" "Conectar" "Todas las redes" - "Está disponible una red Wi‑Fi sugerida por %s" - "¿Deseas conectarte a las redes sugeridas por %s?" + + + + "Sí" "No" "Se activará la conexión Wi-Fi automáticamente" @@ -1267,9 +1309,14 @@ "Acceder a la red" - "La red Wi-Fi no tiene acceso a Internet" + + "Presiona para ver opciones" "Se estableció conexión" + + + + "Cambios en la configuración de tu hotspot" "Cambió la banda de tu hotspot." "Si bien este dispositivo no admite la opción para conectarse exclusivamente a bandas de 5 GHz, las usará cuando estén disponibles." @@ -1354,6 +1401,10 @@ "Depuración por USB conectada" "Presiona para desactivar la depuración USB" "Seleccionar para desactivar la depuración por USB" + + + + "Hay líquido o suciedad en el puerto USB" "El puerto USB se inhabilitó automáticamente. Presiona para obtener más información." "Es seguro usar el puerto USB" @@ -1905,8 +1956,6 @@ "Presiona para desbloquear" "Conectado a %1$s" "Presiona para ver archivos" - "Fijar" - "No fijar" "Información de apps" "−%1$s" "Iniciando demostración…" @@ -1997,6 +2046,22 @@ "Notificación de información del modo de Rutinas" "Es posible que la batería se agote antes de la carga habitual" "Se activó el Ahorro de batería para extender la duración de la batería" + + + + + + + + + + + + + + + + "Carpeta" "Aplicación de Android" "Archivo" diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index f87743aa685d90839da6ea4333503b2422f9dd02..6e212d75cc8fcd1708448e1a9b0307fe95bd42ca 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -141,8 +141,10 @@ "WiFi-kõned" "VoWifi" "Väljas" - "WiFi eelistusega" - "Eelistatud on mobiilne andmeside" + + + + "Ainult WiFi" "{0}: pole suunatud" "{0}: {1}" @@ -228,7 +230,8 @@ "Veaaruanne" "Seansi lõpp" "Ekraanipilt" - "Veaaruande võtmine" + + "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." "Interakt. aruanne" "Kasutage seda enamikul juhtudel. See võimaldab jälgida aruande edenemist, sisestada probleemi kohta täpsemat teavet ja jäädvustada ekraanipilte. Vahele võivad jääda mõned vähem kasutatud jaotised, millest teavitamine võtab rohkem aega." @@ -281,9 +284,12 @@ "Asukoht" "pääseda juurde selle seadme asukohale" "Kas lubada rakendusele <b>%1$s</b> juurdepääs selle seadme asukohale?" - "Rakendusel on juurdepääs asukohale vaid siis, kui rakendust kasutate." - "Kas lubada rak. <b>%1$s</b> alati juurdepääs seadme asukohale?" - "Rakendusel on alati juurdepääs asukohale isegi siis, kui te rakendust ei kasuta." + + + + + + "Kalender" "juurdepääs kalendrile" "Kas lubada rakendusele <b>%1$s</b> juurdepääs teie kalendrile?" @@ -316,7 +322,10 @@ "Kas lubada rakendusele <b>%1$s</b> juurdepääs teie muusikale?" "Fotod ja videod" "Pääseda juurde teie fotodele ja videotele" - "Kas lubada rakendusel <b>%1$s</b> teie fotodele ja videotele, sh märgistatud asukohtadele, juurde pääseda?" + + + + "Akna sisu toomine" "Kasutatava akna sisu kontrollimine." "Puudutusega sirvimise sisselülitamine" @@ -509,8 +518,10 @@ "Võimaldab rakendusel suhelda lähiväljaside (NFC) märgendite, kaartide ja lugeritega." "keelake ekraanilukk" "Võimaldab rakendusel keelata klahviluku ja muu seotud parooli turvalisuse. Näiteks keelab telefon klahviluku sissetuleva kõne vastuvõtmisel ja lubab klahviluku uuesti, kui kõne on lõpetatud." - "taotleda ekraaniluku keerukust" - "Lubab rakendusel vaadata ekraaniluku keerukuse taset (kõrge, keskmine, madal või puudub), mis näitab ekraaniluku võimalikku pikkust ja tüüpi. Rakendus võib kasutajatele soovitada ka ekraaniluku viimist teatud tasemele, kuid kasutajad võivad seda eirata ja kuvalt lahkuda. Pange tähele, et ekraanilukku ei salvestata lihttekstina, seega ei tea rakendus täpset parooli." + + + + "kasutada biomeetrilist riistvara" "Võimaldab rakendusel autentimiseks kasutada biomeetrilist riistvara" "sõrmejälje riistvara haldamine" @@ -565,37 +576,59 @@ "Lubab rakendusel tühistada meetodid kasutatavate näomallide lisamiseks ja kustutamiseks." "kasutada näo autentimise riistvara" "Võimaldab rakendusel autentimiseks kasutada näo autentimise riistvara" - "Nägu ei õnnestunud töödelda. Proovige uuesti." - "Nägu on liiga hele. Proovige hämaramas." - "Nägu on liiga tume. Kasutage valgusallikat." - "Liigutage andur näost kaugemale." - "Liigutage andur näole lähemale." - "Liigutage andurit kõrgemale." - "Liigutage andurit madalamale." - "Liigutage andurit paremale." - "Liigutage andurit vasakule." - "Vaadake andurit." - "Nägu ei tuvastatud." - "Liiga palju liikumist." + + + + + + + + + + + + + + + + + + + + + + + + "Registreerige oma nägu uuesti." - "Tuvastati teine nägu." + + "Liiga sarnane, palun muutke oma asendit." - "Vaadake otse kaamerasse." - "Vaadake otse kaamerasse." + + + + "Pange pea vertikaalselt otseks." - "Tooge oma nägu esile." + + + + "Näotuvastuse riistvara pole saadaval." - "Näotuvastuse taimeri ajalõpp. Proovige uuesti." + + "Nägu ei saa salvestada." "Näotuvastuse toiming tühistati." "Kasutaja tühistas näo autentimise." "Liiga palju katseid. Proovige hiljem uuesti." "Liiga palju katseid. Näotuvastus on keelatud." - "Proovige uuesti." - "Nägu pole registreeritud." - "Sellel seadmel pole näotuvastuse andurit." + + + + + + "Nägu %d" @@ -1213,9 +1246,16 @@ "Ava rakendus %1$s" "Rakendus %1$s suletakse salvestamata" "Protsess %1$s ületas mälupiirangu" + + "Mälutõmmis salvestati. Puudutage jagamiseks." "Kas jagada mälutõmmist?" - "Protsess %1$s ületas protsessi mälupiirangu %2$s. Saate mälutõmmist jagada selle arendajaga. Olge ettevaatlik: see mälutõmmis võib sisaldada teie isiklikke andmeid, millele rakendusel on juurdepääs." + + + + + + "Valige teksti jaoks toiming" "Helina helitugevus" "Meediumi helitugevus" @@ -1254,8 +1294,10 @@ "Puudutage kõikide võrkude nägemiseks" "Ühenda" "Kõik võrgud" - "Rakenduse %s soovitatud WiFi-võrk on saadaval" - "Kas soovite ühenduse luua rakenduse %s soovitatud võrkudega?" + + + + "Jah" "Ei" "WiFi lülitub sisse automaatselt" @@ -1267,9 +1309,14 @@ "Võrku sisselogimine" - "WiFi-võrgul pole juurdepääsu Internetile" + + "Puudutage valikute nägemiseks" "Ühendatud" + + + + "Muudatused teie leviala seadetes" "Teie leviala riba on muutunud." "See seade ei toeta teie eelistatud ainult 5 GHz riba. Seade kasutab 5 GHz riba ainult siis, kui see on saadaval." @@ -1354,6 +1401,10 @@ "USB-silumine ühendatud" "Puudutage USB silumise väljalülitamiseks" "Valige USB silumise keelamiseks" + + + + "USB-pordis on vedelik või mustus" "USB-port on automaatselt keelatud. Puudutage lisateabe saamiseks." "USB-porti on ohutu kasutada" @@ -1905,8 +1956,6 @@ "Puudut. tööprofiili avamiseks" "Ühendatud seadmega %1$s" "Failide vaatamiseks puudutage" - "Kinnita" - "Vabasta" "Rakenduse teave" "−%1$s" "Demo käivitamine …" @@ -1997,6 +2046,22 @@ "Rutiinirežiimi teabe märguanne" "Aku võib enne tavapärast laadimist tühjaks saada" "Akusäästja aktiveeriti aku tööea pikendamiseks" + + + + + + + + + + + + + + + + "Kaust" "Androidi rakendus" "Fail" diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 7835cfe2481253a96295e46d4e8686d2a7e972a5..43c9b983090a91d9784d49b15906d2959092883d 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -141,8 +141,10 @@ "‏تماس ازطریق WiFi" "VoWifi" "خاموش" - "‏Wi-Fi ترجیحی" - "داده شبکه تلفن همراه ارجح است" + + + + "‏فقط Wi-Fi" "{0}: هدایت نشده" "{0}: {1}" @@ -228,7 +230,8 @@ "گزارش اشکال" "پایان جلسه" "عکس صفحه‌نمایش" - "گرفتن گزارش اشکال" + + "این گزارش اطلاعات مربوط به وضعیت دستگاه کنونی شما را جمع‌آوری می‌کند تا به صورت یک پیام ایمیل ارسال شود. از زمان شروع گزارش اشکال تا آماده شدن برای ارسال اندکی زمان می‌برد؛ لطفاً شکیبا باشید." "گزارش تعاملی" "در بیشتر شرایط از این گزینه استفاده کنید. به شما امکان ردیابی پیشرفت گزارش و وارد کردن جزئیات بیشتری درباره مشکل را می‌دهد. ممکن است برخی از بخش‌هایی را که کمتر استفاده شده و باعث افزایش طول زمان گزارش می‌شود حذف کند." @@ -281,9 +284,12 @@ "مکان" "دسترسی به موقعیت مکانی این دستگاه" "‏به <b>%1$s</b> اجازه داده شود به مکان این دستگاه دسترسی پیدا کند؟" - "این برنامه تنها هنگامی که از آن استفاده می‌کنید، به مکان دسترسی خواهد داشت." - "‏همیشه به <b>%1$s</b> اجازه داده شود به مکان این دستگاه دسترسی داشته باشد؟" - "این برنامه همیشه به مکان دسترسی خواهد داشت، حتی اگر از آن استفاده نکنید." + + + + + + "تقویم" "دسترسی به تقویم شما" "‏به <b>%1$s</b> اجازه داده شود به تقویم شما دسترسی پیدا کند؟" @@ -316,7 +322,10 @@ "‏به <b>%1$s</b> اجازه داده شود به موسیقی شما دسترسی پیدا کند؟" "عکس و ویدیو" "دسترسی به عکس‌ها و ویدیوهایتان" - "‏به <b>%1$s</b> اجازه می‌دهید به عکس‌ها و ویدیوهاتان، ازجمله مکان‌های برچسب‌گذاری‌شده، دسترسی داشته باشد؟" + + + + "محتوای پنجره را بازیابی کند" "محتوای پنجره‌ای را که درحال تعامل با آن هستید بررسی می‌کند." "فعال‌سازی کاوش لمسی" @@ -509,8 +518,10 @@ "‏به برنامه اجازه می‎دهد تا با تگهای ارتباط میدان نزدیک (NFC)، کارتها و فایل خوان ارتباط برقرار کند." "غیرفعال کردن قفل صفحه شما" "به برنامه امکان می‌دهد قفل کلید و هر گونه امنیت گذرواژه مرتبط را غیرفعال کند. به‌عنوان مثال تلفن هنگام دریافت یک تماس تلفنی ورودی قفل کلید را غیرفعال می‌کند و بعد از پایان تماس، قفل کلید را دوباره فعال می‌کند." - "درخواست پیچیدگی قفل صفحه" - "به برنامه اجازه می‌دهد سطح پیچیدگی قفل صفحه (بالا، متوسط، پایین یا هیچ‌کدام) را بیاموزد که نشانگر طیف طول مدت و نوع قفل صفحه است. همچنین برنامه می‌تواند به کاربران پیشنهاد دهد قفل صفحه را به سطح خاصی به‌روزرسانی کنند، اما کاربران می‌توانند آزادانه این پیشنهاد را نادیده بگیرند و به سطح دیگری بروند. توجه داشته باشید که قفل صفحه در قالب نوشتار ساده ذخیره نمی‌شود، بنابراین برنامه گذرواژه دقیق را نمی‌داند." + + + + "استفاده از سخت‌افزار بیومتریک" "به برنامه امکان می‌دهد از سخت‌افزار بیومتریک برای احراز هویت استفاده کند" "مدیریت سخت‌افزار اثر انگشت" @@ -565,37 +576,59 @@ "به برنامه امکان می‌دهد روش‌هایی را برای افزودن و حذف الگوهای چهره جهت استفاده فرابخواند." "استفاده از سخت‌افزار احراز هویت با چهره" "به برنامه امکان می‌دهد از سخت‌افزار احراز هویت با چهره برای احراز هویت استفاده کند" - "چهره پردازش نشد. لطفاً دوباره امتحان کنید." - "چهره خیلی روشن است. لطفاً در نور کمتری دوباره امتحان کنید." - "چهره خیلی تاریک است. لطفاً منبع نور را نپوشانید." - "لطفاً حسگر را از صورتتان دورتر کنید." - "لطفاً حسگر را به صورتتان نزدیک‌تر کنید." - "لطفاً حسگر را بالاتر ببرید." - "لطفاً حسگر را پایین‌تر بیاورید." - "لطفاً حسگر را به راست حرکت دهید." - "لطفاً حسگر را به چپ حرکت دهید." - "لطفاً به حسگر نگاه کنید." - "چهره‌ای شناسایی نشد." - "حرکت خیلی زیاد است." + + + + + + + + + + + + + + + + + + + + + + + + "لطفاً چهره‌تان را مجدداً ثبت کنید." - "چهره دیگری شناسایی شد." + + "بسیار شبیه قبلی است، لطفاً قیافه دیگری بگیرید." - "لطفاً مستقیم به دوربین نگاه کنید." - "لطفاً مستقیم به دوربین نگاه کنید." + + + + "لطفاً سرتان را به‌صورت عمود نگه دارید." - "لطفاً چهره‌تان را نپوشانید." + + + + "سخت‌افزار چهره دردسترس نیست." - "مهلت زمانی شناسایی چهره تمام شد. دوباره امتحان کنید" + + "نمی‌توان چهره را ذخیره کرد." "عملیات شناسایی چهره لغو شد." "احراز هویت چهره توسط کاربر لغو شد." "تعداد زیادی تلاش ناموفق. بعداً دوباره امتحان کنید." "چندین تلاش ناموفق. احراز هویت با چهره غیرفعال شد." - "دوباره امتحان کنید." - "هیچ چهره‌ای ثبت نشده است." - "این دستگاه حسگر احراز هویت با چهره ندارد." + + + + + + "چهره %d" @@ -1213,9 +1246,16 @@ "%1$s را باز کنید" "%1$s بدون ذخیره شدن بسته می‌شود" "%1$s از حد مجاز حافظه فراتر رفت" + + "رونوشت حافظه جمع‌آوری شد. برای هم‌رسانی ضربه بزنید." "رونوشت حافظه آزاد به اشتراک گذاشته شود؟" - "فرآیند %1$s از حد مجاز حافظه پردازش خود %2$s فراتر رفته است. یک رونوشت حافظه آزاد برای شما در دسترس است که با برنامه‌نویس به اشتراک بگذارید. مواظب باشید: این رونوشت حافظه آزاد می‌تواند حاوی هر نوع اطلاعات شخصی شما باشد که برنامه به آن دسترسی دارد." + + + + + + "انتخاب یک عملکرد برای نوشتار" "میزان صدای زنگ" "میزان صدای رسانه" @@ -1254,8 +1294,10 @@ "برای دیدن همه شبکه‌ها ضربه بزنید" "اتصال" "همه شبکه‌ها" - "‏شبکه Wi-Fi پیشنهادی %s دردسترس است" - "مایلید به شبکه‌های پیشنهادی %s متصل شوید؟" + + + + "بله" "خیر" "‏Wi‑Fi به‌طور خودکار روشن خواهد شد" @@ -1267,9 +1309,14 @@ "ورود به سیستم شبکه" - "‏Wi-Fi به اینترنت دسترسی ندارد" + + "برای گزینه‌ها ضربه بزنید" "متصل" + + + + "تغییرات در تنظیمات نقطه اتصال" "نوار نقطه اتصال شما تغییر کرد." "این دستگاه از اولویت فقط ۵ گیگاهرتز شما پشتیبانی نمی‌کند. هرزمان نوار ۵ گیگاهرتزی دردسترس باشد این دستگاه از آن استفاده خواهد کرد." @@ -1354,6 +1401,10 @@ "‏اشکال‌زدایی USB متصل شد" "‏برای خاموش کردن اشکال‌زدایی USB ضربه بزنید" "‏انتخاب کنید تا رفع عیب USB غیرفعال شود." + + + + "‏مایعات یا خاکروبه در درگاه USB" "‏درگاه USB به‌طور خودکار غیرفعال شده است. برای اطلاعات بیشتر، ضربه بزنید." "‏ایمن برای استفاده از درگاه USB" @@ -1905,8 +1956,6 @@ "برای باز کردن قفل ضربه بزنید" "به %1$s متصل شد" "برای دیدن فایل‌ها، ضربه بزنید" - "پین کردن" - "برداشتن پین" "اطلاعات برنامه" "−%1$s" "در حال شروع نسخه نمایشی…" @@ -1997,6 +2046,22 @@ "اعلان اطلاعات حالت روال معمول" "ممکن است شارژ باتری قبل از شارژ معمول تمام شود" "جهت افزایش عمر باتری، بهینه‌سازی باتری فعال شد" + + + + + + + + + + + + + + + + "پوشه" "‏برنامه Android" "فایل" diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 6efc8befcc28aacd0b33e8fbcda3789fccc878a5..1b2d5331cce50e6da136c29ac9f78726a6289e6a 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -141,8 +141,10 @@ "Wi-Fi-puhelut" "VoWifi" "Ei käytössä" - "Wi-Fi ensisijainen" - "Mobiiliverkko ensisijainen" + + + + "Vain Wi-Fi" "{0}: ei siirretty" "{0}: {1}" @@ -228,7 +230,8 @@ "Virheraportti" "Lopeta käyttökerta" "Kuvakaappaus" - "Luo virheraportti" + + "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." "Interaktiivinen" "Valitse tämä vaihtoehto useimmissa tapauksissa. Voit seurata raportin etenemistä, antaa lisätietoja ongelmasta ja tallentaa kuvakaappauksia. Tämä vaihtoehto saattaa ohittaa joitakin harvoin käytettyjä osioita, joiden käsittely raportissa kestää kauan." @@ -281,9 +284,12 @@ "Sijainti" "käyttää laitteen sijaintia" "Saako <b>%1$s</b> tämän laitteen sijainnin käyttöoikeuden?" - "Sovellus saa sijainnin käyttöoikeuden vain jos käytät sovellusta." - "Saako <b>%1$s</b> tämän laitteen sijainnin käyttöoikeuden?" - "Sovellus saa sijainnin käyttöoikeuden aina, vaikka et käyttäisi sovellusta." + + + + + + "Kalenteri" "käyttää kalenteria" "Saako <b>%1$s</b> kalenterisi käyttöoikeuden?" @@ -316,7 +322,10 @@ "Saako <b>%1$s</b> käyttää musiikkiasi?" "Kuvat ja videot" "käyttää kuvia ja videoita" - "Saako <b>%1$s</b> pääsyn kuviin ja videoihin, mukaan lukien tagattuihin sijainteihin?" + + + + "Noutaa ikkunan sisältöä" "Tarkistaa käyttämäsi ikkunan sisältö." "Ottaa kosketuksella tutkimisen käyttöön" @@ -509,8 +518,10 @@ "Antaa sovelluksen kommunikoida NFC (Near Field Communication) -tagien, -korttien ja -lukijoiden kanssa." "poista näytön lukitus käytöstä" "Antaa sovelluksen ottaa näppäinlukon ja siihen liittyvän salasanasuojauksen pois käytöstä. Esimerkki: puhelin poistaa näppäinlukon käytöstä puhelun saapuessa ja asettaa lukon takaisin käyttöön puhelun päättyessä." - "pyytää näytön lukituksen monimutkaisuutta" - "Sovellus saa luvan selvittää näytön lukituksen monimutkaisuuden (korkea, keskitaso tai matala) eli sen pituusluokan ja tyypin. Sovellus voi myös ehdottaa käyttäjille näytön lukituksen vaihtamista tietylle tasolle, mutta he voivat ohittaa tämän ja poistua. Itse näytön lukitusta ei säilytetä tekstimuodossa, joten sovellus ei tiedä sitä tarkalleen." + + + + "käytä biometristä laitteistoa" "Sallii sovelluksen käyttää biometristä laitteistoa todennukseen" "sormenjälkilaitteiston hallinnointi" @@ -565,37 +576,59 @@ "Sallii sovelluksen käyttää menetelmiä, joilla voidaan lisätä tai poistaa kasvomalleja." "käyttää kasvojentodennuslaitteistoa" "Sallii sovelluksen käyttää todennuslaitteistoa todennukseen" - "Kasvojen käsittely epäonnistui. Yritä uudelleen." - "Kasvokuva on liian kirkas. Kokeile hämärää valoa." - "Kasvokuva on liian tumma. Älä peitä valonlähdettä." - "Siirrä anturia kauemmas kasvoista." - "Tuo anturi lähemmäs kasvoja." - "Siirrä anturia korkeammalle." - "Siirrä anturia alemmas." - "Siirrä anturia oikealle." - "Siirrä anturia vasemmalle." - "Katso anturia." - "Kasvoja ei havaittu." - "Laite liikkui liikaa." + + + + + + + + + + + + + + + + + + + + + + + + "Rekisteröi kasvot uudelleen." - "Toiset kasvot havaittu." + + "Liian samanlainen, vaihda asentoa." - "Katso suoremmin kameraan." - "Katso suoremmin kameraan." + + + + "Suorista pää pystysuunnassa." - "Älä peitä kasvojasi." + + + + "Kasvolaitteisto ei ole käytettävissä." - "Kasvotoiminto aikakatkaistiin. Yritä uudelleen." + + "Kasvoja ei voi tallentaa." "Kasvotoiminto peruutettu" "Käyttäjä peruutti kasvojentunnistuksen." "Liian monta yritystä. Yritä myöhemmin uudelleen." "Liikaa yrityksiä. Kasvojentodennus ei käytössä." - "Yritä uudelleen." - "Kasvoja ei ole otettu käyttöön." - "Laitteessa ei ole kasvojentunnistusanturia." + + + + + + "Kasvot %d" @@ -1213,9 +1246,16 @@ "Avaa %1$s" "%1$s suljetaan tallentamatta tietoja" "%1$s ylitti muistirajan." + + "Keon vedos on kerätty, jaa se napauttamalla." "Jaetaanko keon vedos?" - "Prosessi %1$s on ylittänyt muistirajan (%2$s). Keon vedos on jaettavissa kehittäjän kanssa. Ole varovainen: tämä keon vedos voi sisältää sellaisia henkilötietojasi, joihin sovelluksella on käyttöoikeus." + + + + + + "Valitse tekstille toiminto" "Soittoäänen voimakkuus" "Median äänenvoimakkuus" @@ -1254,8 +1294,10 @@ "Napauta, niin näet kaikki verkot." "Yhdistä" "Kaikki verkot" - "Wi-Fi-verkko, jota %s ehdotti, on käytettävissä" - "Haluatko muodostaa yhteyden verkkoihin, joita %s ehdottaa?" + + + + "Kyllä" "Ei" "Wi-Fi käynnistyy automaattisesti" @@ -1267,9 +1309,14 @@ "Kirjaudu verkkoon" - "Wi-Fi ei ole yhteydessä internetiin" + + "Näytä vaihtoehdot napauttamalla." "Yhdistetty" + + + + "Hotspot-asetustesi muutokset" "Hotspot-taajuutesi on muuttunut." "Tämä laite ei tue asetustasi (vain 5 GHz). Sen sijaan laite käyttää 5 GHz:n taajuutta sen ollessa käytettävissä." @@ -1354,6 +1401,10 @@ "USB-vianetsintä yhdistetty" "Poista USB-virheenkorjaus käytöstä napauttamalla." "Poista USB-vianetsintä käytöstä valitsemalla tämä." + + + + "Nestettä tai likaa USB-portissa" "USB-portti poistetaan käytöstä automaattisesti. Napauta nähdäksesi lisätietoja." "USB-portin käyttö on turvallista" @@ -1905,8 +1956,6 @@ "Avaa profiili koskettamalla." "%1$s yhdistetty" "Näytä tiedostot koskettamalla" - "Kiinnitä" - "Irrota" "Sovelluksen tiedot" "−%1$s" "Aloitetaan esittelyä…" @@ -1997,6 +2046,22 @@ "Ohjelmatilan tietoilmoitus" "Akku saattaa loppua ennen normaalia latausaikaa" "Virransäästö otettu käyttöön akunkeston pidentämiseksi" + + + + + + + + + + + + + + + + "Kansio" "Android-sovellus" "Tiedosto" diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 78f6eccdda6da2e57127cd00235ea55cfe3cb6a3..36a47fb8f23a9200f0a93434cfea2fb8201531c8 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -141,8 +141,10 @@ "Chamadas por wifi" "VoWifi" "Desactivado" - "Wifi preferida" - "Datos móbiles preferidos" + + + + "Só por wifi" "{0}: non desviada" "{0}: {1}" @@ -228,7 +230,8 @@ "Informe de erros" "Finalizar a sesión" "Captura de pantalla" - "Crear informe de erros" + + "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." "Informe interactivo" "Usa esta opción na maioría das circunstancias. Permíteche realizar un seguimento do progreso do informe, introducir máis detalles sobre o problema e facer capturas de pantalla. É posible que omita algunhas seccións menos usadas para as que se tarda máis en facer o informe." @@ -281,9 +284,12 @@ "Localización" "acceder á localización deste dispositivo" "Queres permitir que a aplicación <b>%1$s</b> acceda á localización deste dispositivo?" - "A aplicación só terá acceso á localización mentres a esteas utilizando." - "Queres permitir que <b>%1$s</b> acceda á localización?" - "A aplicación sempre terá acceso á localización, aínda que non a esteas utilizando." + + + + + + "Calendario" "acceder ao teu calendario" "Queres permitir que a aplicación <b>%1$s</b> acceda ao teu calendario?" @@ -316,7 +322,10 @@ "Queres permitir que a aplicación <b>%1$s</b> acceda á túa música?" "Fotos e vídeos" "acceder ás fotos e aos vídeos" - "Queres permitir que <b>%1$s</b> acceda ás túas fotos e vídeos, incluídas as localizacións etiquetadas?" + + + + "Recuperar contido da ventá" "Inspecciona o contido dunha ventá coa que estás interactuando." "Activar a exploración táctil" @@ -509,8 +518,10 @@ "Permite á aplicación comunicarse con etiquetas, tarxetas e lectores Near Field Communication (NFC)." "desactivar o bloqueo da pantalla" "Permite á aplicación desactivar o bloqueo do teclado e calquera seguranza dos contrasinais asociada. Por exemplo, o teléfono desactiva o bloqueo do teclado ao recibir unha chamada telefónica entrante e, a continuación, volve activar o bloqueo do teclado unha vez finalizada a chamada." - "solicitar o nivel de complexidade do bloqueo de pantalla" - "Permite que a aplicación consulte o nivel (alto, medio, baixo ou inexistente) de complexidade do bloqueo de pantalla para saber a lonxitude aproximada do contrasinal e o tipo de bloqueo de pantalla. A aplicación tamén pode suxerirlles aos usuarios que aumenten o nivel de complexidade do bloqueo de pantalla, aínda que poden ignorar a suxestión e seguir navegando. Ten en conta que o bloqueo de pantalla non se almacena como texto sen formato, polo que a aplicación non pode consultar o contrasinal exacto." + + + + "utilizar hardware biométrico" "Permite que a aplicación utilice hardware biométrico para a autenticación" "xestionar hardware de impresión dixital" @@ -565,37 +576,59 @@ "Permite que a aplicación invoque métodos para engadir e eliminar modelos faciais de uso." "usar hardware de autenticación facial" "Permite que a aplicación utilice hardware facial para a autenticación" - "Non se puido procesar a cara. Téntao de novo." - "A cara vese demasiado brillante. Proba con menos luz." - "A cara vese demasiado escura. Proba con máis luz." - "Afasta o sensor da cara." - "Achega o sensor á cara." - "Sube máis o sensor." - "Baixa o sensor." - "Move o sensor cara á dereita." - "Move o sensor cara á esquerda." - "Mira ao sensor." - "Non se detectou ningunha cara." - "Demasiado movemento." + + + + + + + + + + + + + + + + + + + + + + + + "Volve rexistrar a túa cara." - "Detectouse unha cara diferente." + + "É moi similar. Cambia a pose." - "Mira máis directamente á cámara." - "Mira máis directamente á cámara." + + + + "Endereita a cabeza." - "Descubre a cara." + + + + "O hardware facial non está dispoñible." - "Esgotouse o tempo de espera. Téntao de novo." + + "Non se puido almacenar a cara." "Cancelouse a operación relacionada coa cara" "O usuario cancelou a autenticación da cara." "Demasiados intentos. Téntao de novo máis tarde." "Demasiados intentos. Autenticación desactivada." - "Téntao de novo." - "Non se rexistrou ningunha cara." - "Este dispositivo non ten sensor de autenticación de caras." + + + + + + "Cara %d" @@ -1213,9 +1246,16 @@ "Abrir a aplicación %1$s" "A aplicación %1$s pecharase sen gardar o contido" "%1$s superou o límite de memoria" + + "Recompilouse un baleirado de montóns. Toca para compartilo." "Queres compartir o baleirado de montóns?" - "O proceso %1$s superou o seu límite de memoria de proceso de %2$s. Tes dispoñible un baleirado de montóns para compartir co seu programador. Debes ter coidado, pois este baleirado de montóns pode conter información persoal á que ten acceso a aplicación." + + + + + + "Seleccionar unha acción para o texto" "Volume do timbre" "Volume dos elementos multimedia" @@ -1254,8 +1294,10 @@ "Toca para ver todas as redes" "Conectarse" "Todas as redes" - "Hai unha rede wifi dispoñible proposta por %s" - "Queres conectarte ás redes propostas por %s?" + + + + "Si" "Non" "A wifi activarase automaticamente" @@ -1267,9 +1309,14 @@ "Inicia sesión na rede" - "A wifi non ten acceso a Internet" + + "Toca para ver opcións." "Estableceuse conexión" + + + + "Cambios na configuración da zona wifi" "Modificouse a banda da zona wifi." "Este dispositivo non admite a opción de conectarse só a bandas de 5 GHz, pero usaraas se están dispoñibles." @@ -1355,6 +1402,10 @@ "Depuración por USB conectada" "Toca para desactivar a depuración por USB" "Selecciona a opción para desactivar a depuración por USB." + + + + "Hai líquido ou residuos no porto USB" "O porto USB desactivouse automaticamente. Toca para obter máis información." "É seguro usar o porto USB" @@ -1906,8 +1957,6 @@ "Toca para desbloquear o perfil" "Conectado a %1$s" "Toca para ver os ficheiros" - "Fixar" - "Soltar" "Info. da aplicación" "−%1$s" "Iniciando demostración…" @@ -1998,6 +2047,22 @@ "Notificación da información do modo de rutina" "A batería pode esgotarse antes do habitual" "Para ampliar a duración da batería activouse a función Aforro de batería" + + + + + + + + + + + + + + + + "Cartafol" "Aplicación Android" "Ficheiro" diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 8131c0dceee6480aa3e132288e03e745ddb6ed76..b8e26122c63d0f7e2c8cad519ad7f28c2504bf96 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -141,8 +141,10 @@ "વાઇ-ફાઇ કૉલિંગ" "VoWifi" "બંધ" - "વાઇ-ફાઇ પસંદ કર્યું" - "મોબાઇલને પસંદગી" + + + + "ફક્ત વાઇ-ફાઇ" "{0}: ફોરવર્ડ કર્યો નથી" "{0}: {1}" @@ -228,7 +230,8 @@ "બગ રિપોર્ટ" "સત્ર સમાપ્ત કરો" "સ્ક્રીનશૉટ" - "બગ રિપોર્ટ લો" + + "આ, એક ઇ-મેઇલ સંદેશ તરીકે મોકલવા માટે, તમારા વર્તમાન ઉપકરણ સ્થિતિ વિશેની માહિતી એકત્રિત કરશે. એક બગ રિપોર્ટ પ્રારંભ કરીને તે મોકલવા માટે તૈયાર ન થઈ જાય ત્યાં સુધી તેમાં થોડો સમય લાગશે; કૃપા કરીને ધીરજ રાખો." "ક્રિયાપ્રતિક્રિયાત્મક રિપોર્ટ" "મોટાભાગના સંજોગોમાં આનો ઉપયોગ કરો. તે રિપોર્ટની પ્રગતિને ટ્રૅક કરવા, સમસ્યા વિશે વધુ વિગતો દાખલ કરવાની અને સ્ક્રીનશૉટ્સ લેવાની મંજૂરી આપે છે. તે કેટલાક ઓછા ઉપયોગમાં આવતાં વિભાગો કે જે જાણ કરવામાં વધુ સમય લેતાં હોય તેને છોડી દઈ શકે છે." @@ -281,9 +284,12 @@ "સ્થાન" "આ ઉપકરણના સ્થાનને ઍક્સેસ કરવાની" "<b>%1$s</b>ને આ ઉપકરણના સ્થાનને ઍક્સેસ કરવાની મંજૂરી આપીએ?" - "માત્ર જ્યારે તમે ઍપનો ઉપયોગ કરી રહ્યાં હોય ત્યારે જ ઍપ સ્થાનને ઍક્સેસ કરી શકશે." - "<b>%1$s</b>ને હંમેશાં આ ઉપકરણના સ્થાનને ઍક્સેસ કરવાની મંજૂરી આપીએ?" - "તમે ઍપનો ઉપયોગ કરી રહ્યાં ન હોય, તો પણ ઍપ હંમેશાં સ્થાનને ઍક્સેસ કરી શકશે." + + + + + + "કૅલેન્ડર" "તમારા કેલેન્ડરને ઍક્સેસ કરવાની" "<b>%1$s</b>ને તમારા કૅલેન્ડરને ઍક્સેસ કરવાની મંજૂરી આપીએ?" @@ -316,7 +322,10 @@ "<b>%1$s</b>ને તમારા સંગીતમાં ઍક્સેસ કરવાની મંજૂરી આપવી છે?" "ફોટા અને વીડિયો" "તમારા ફોટો & વિડિઓ ઍક્સેસ કરો" - "<b>%1$s</b>ને તમારા ટૅગ કરેલાં સ્થાનો સહિત ફોટા અને વીડિયો ઍક્સેસ કરવાની મંજૂરી આપવી છે?" + + + + "વિંડો કન્ટેન્ટ પુનઃપ્રાપ્ત કરો" "તમે જેની સાથે ક્રિયા-પ્રતિક્રિયા કરી રહ્યાં છો તે વિંડોનું કન્ટેન્ટ તપાસો." "સ્પર્શ કરીને શોધખોળ કરવું ચાલુ કરો" @@ -509,8 +518,10 @@ "ઍપ્લિકેશનને નિઅર ફીલ્ડ કમ્યુનિકેશન (NFC) ટૅગ, કાર્ડ અને રીડર સાથે સંચાર કરવાની મંજૂરી આપે છે." "તમારું સ્ક્રીન લૉક અક્ષમ કરો" "એપ્લિકેશનને કીલૉક અને કોઈપણ સંકળાયેલ પાસવર્ડ સુરક્ષા અક્ષમ કરવાની મંજૂરી આપે છે. ઉદાહરણ તરીકે, ઇનકમિંગ ફોન કૉલ પ્રાપ્ત કરતી વખતે ફોન, કીલૉકને અક્ષમ કરે છે, પછી કૉલ સમાપ્ત થઈ જવા પર કીલૉક ફરીથી સક્ષમ કરે છે." - "સ્ક્રીન લૉકની જટિલતા જાણવા માટે વિનંતી કરો" - "ઍપને સ્ક્રીન લૉકની જટિલતાનું લેવલ (ઊંચું, મધ્યમ, નીચું અથવા કોઈ નહીં) જાણવાની મંજૂરી આપે છે, જે સ્ક્રીન લૉકના પ્રકાર અને લંબાઈની સંભવિત શ્રેણી સૂચવે છે. ઍપ વપરાશકર્તાઓને સ્ક્રીન લૉકને ચોક્કસ લેવલ સુધી અપડેટ કરવાનું સૂચન પણ કરી શકે છે, પરંતુ વપરાશકર્તાઓ મુક્ત રીતે તેને અવગણીને નૅવિગેટ કરી શકે છે. એ વાતની નોંધ કરજો કે સ્ક્રીન લૉકનો સાદા ટેક્સ્ટમાં સંગ્રહ કરવામાં આવતો નથી, તેથી ઍપને ચોક્કસ પાસવર્ડની જાણ હોતી નથી." + + + + "બાયોમેટ્રિક હાર્ડવેરનો ઉપયોગ કરો" "ઍપને પ્રમાણીકરણ માટે બાયોમેટ્રિક હાર્ડવેરનો ઉપયોગ કરવાની મંજૂરી આપે છે" "ફિંગરપ્રિન્ટ હાર્ડવેરને સંચાલિત કરો" @@ -565,37 +576,59 @@ "ઍપને ઉપયોગ માટે ચહેરાના નમૂના ઉમેરવા અને ડિલીટ કરવાની પદ્ધતિને રદ કરવાની મંજૂરી આપે છે." "ચહેરા પ્રમાણીકરણના હાર્ડવેરનો ઉપયોગ કરો" "ઍપને પ્રમાણીકરણ માટે ચહેરા પ્રમાણીકરણના હાર્ડવેરનો ઉપયોગ કરવાની મંજૂરી આપે છે" - "ચહેરાની પ્રક્રિયા કરી શકાઈ નથી. ફરી પ્રયાસ કરો." - "ચહેરો ખૂબ ચળકે છે. કૃપા કરીને ઓછા પ્રકાશવાળા સ્થાનમાં પ્રયાસ કરો." - "ચહેરો ખૂબ શ્યામ છે. કૃપા કરીને પ્રકાશના સૉર્સ ઉઘાડો." - "કૃપા કરીને સેન્સરને ચહેરાથી દૂર ખસેડો." - "કૃપા કરીને સેન્સરને ચહેરાની નજીક ખસેડો." - "કૃપા કરીને સેન્સરને ઉપર ખસેડો." - "કૃપા કરીને સેન્સર નીચે ખસેડો." - "કૃપા કરીને સેન્સરને જમણી બાજુ ખસેડો." - "કૃપા કરીને સેન્સરને ડાબી બાજુ ખસેડો." - "કૃપા કરીને સેન્સરની સામે જુઓ." - "કોઈ ચહેરો મળ્યો નથી." - "ડિવાઇસ ઘણો હલી રહ્યો છે." + + + + + + + + + + + + + + + + + + + + + + + + "કૃપા કરીને તમારા ચહેરાની ફરી નોંધણી કરાવો." - "અલગ ચહેરાની ઓળખ થઈ." + + "ઘણી સમાનતા ધરાવે છે, કૃપા કરીને તમારો પોઝ બદલો." - "કૃપા કરીને કૅમેરા તરફ સીધું જુઓ." - "કૃપા કરીને કૅમેરા તરફ સીધું જુઓ." + + + + "કૃપા કરીને તમારું માથું સીધું ઊભું રાખો." - "કૃપા કરીને તમારો ચહેરો ઢાંકો નહીં." + + + + "ચહેરા માટેનું હાર્ડવેર ઉપલબ્ધ નથી." - "ચહેરા માટેનો સમય સમાપ્ત થયો. ફરી પ્રયાસ કરો." + + "ચહેરો સંગ્રહિત કરી શકાશે નહીં." "ચહેરા સંબંધિત કાર્યવાહી રદ કરવામાં આવી છે." "વપરાશકર્તાએ ચહેરા પ્રમાણીકરણ રદ કર્યુ." "ઘણા બધા પ્રયત્નો. થોડા સમય પછી ફરી પ્રયાસ કરો." "ઘણા બધા પ્રયત્નો. ચહેરાનું પ્રમાણીકરણ બંધ કરવામાં આવ્યું છે." - "ફરી પ્રયાસ કરો." - "કોઈ ચહેરાની નોંધણી કરવામાં આવી નથી." - "આ ડિવાઇસમાં ચહેરાના પ્રમાણીકરણ માટે કોઈ સેન્સર નથી." + + + + + + "ચહેરાનું %d" @@ -1213,9 +1246,16 @@ "%1$s ખોલો" "%1$s ફેરફારો સાચવ્યા વિના બંધ થશે" "%1$s એ મેમરી સીમા વટાવી" + + "હીપ ડમ્પ એકત્રિત કરવામાં આવ્યો છે. શેર કરવા માટે ટૅપ કરો." "હીપ ડમ્પ શેર કરીએ?" - "પ્રક્રિયા %1$s એ તેની %2$s ની પ્રક્રિયા મેમરી મર્યાદા ઓળંગી. તેના વિકાસકર્તા સાથે શેર કરવા તમારી માટે એક હીપ ડમ્પ ઉપલબ્ધ છે. સાવચેત રહો: આ હીપ ડમ્પમાં તમારી વ્યક્તિગત માહિતી શામેલ હોઈ શકે છે કે જેની એપ્લિકેશનને ઍક્સેસ છે." + + + + + + "ટેક્સ્ટ માટે ક્રિયા પસંદ કરો" "રિંગર વૉલ્યૂમ" "મીડિયા વૉલ્યૂમ" @@ -1254,8 +1294,10 @@ "બધા નેટવર્ક જોવા ટૅપ કરો" "કનેક્ટ કરો" "બધા નેટવર્કો" - "%s દ્વારા સૂચિત કરવામાં આવેલ વાઇ-ફાઇ નેટવર્ક ઉપલબ્ધ છે" - "તમે %s દ્વારા સૂચવવામાં આવેલાં નેટવર્ક સાથે કનેક્ટ કરવા માગો છો?" + + + + "હા" "ના" "વાઇ-ફાઇ આપમેળે ચાલુ થઈ જશે" @@ -1267,9 +1309,14 @@ "નેટવર્ક પર સાઇન ઇન કરો" - "વાઇ-ફાઇને કોઈ ઇન્ટરનેટ ઍક્સેસ નથી" + + "વિકલ્પો માટે ટૅપ કરો" "કનેક્ટેડ" + + + + "તમારી હૉટસ્પૉટ સેટિંગને બદલે છે" "તમારું હૉટસ્પૉટ બેન્ડ બદલાયેલ છે." "આ ઉપકરણ તમારી ફક્ત 5GHz માટેની પસંદગીને સમર્થન આપતું નથી. તેના બદલે, જ્યારે આ ઉપકરણ જ્યારે 5GHz બેન્ડ ઉપલબ્ધ હશે ત્યારે તેનો ઉપયોગ કરશે." @@ -1355,6 +1402,10 @@ "USB ડીબગિંગ કનેક્ટ થયું." "USB ડિબગીંગ બંધ કરવા માટે ટૅપ કરો" "USB ડિબગીંગને અક્ષમ કરવા માટે પસંદ કરો." + + + + "USB પોર્ટમાં પ્રવાહી કે ધૂળ" "USB પોર્ટ ઑટોમૅટિક રીતે બંધ કરવામાં આવ્યો છે. વધુ જાણવા માટે ટૅપ કરો." "USB પોર્ટનો ઉપયોગ કરવો સુરક્ષિત છે" @@ -1906,8 +1957,6 @@ "કાર્યાલયની પ્રોફાઇલ અનલૉક કરવા ટૅપ કરો" "%1$s થી કનેક્ટ કરેલું છે" "ફાઇલો જોવા માટે ટૅપ કરો" - "પિન" - "અનપિન કરો" "ઍપ્લિકેશન માહિતી" "−%1$s" "ડેમો પ્રારંભ કરી રહ્યાં છે…" @@ -1998,6 +2047,22 @@ "રૂટિન મોડની માહિતીનું નોટિફિકેશન" "સામાન્ય રીતે ચાર્જ કરવાના સમય પહેલાં બૅટરી સમાપ્ત થઈ શકે છે" "બૅટરી આવરદા વધારવા માટે બૅટરી સેવર ચાલુ કર્યું" + + + + + + + + + + + + + + + + "ફોલ્ડર" "Android ઍપ" "ફાઇલ" diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 2fd884e8c1e2693fa7a7a6af7b2f7a50b2a266be..dca9f86503ee964743d7bc2d8fca8422f6223239 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -141,8 +141,10 @@ "वाई-फ़ाई कॉलिंग" "VoWifi" "बंद" - "वाई-फ़ाई को प्राथमिकता" - "मोबाइल को प्राथमिकता" + + + + "केवल वाई-फ़ाई" "{0}: अग्रेषित नहीं किया गया" "{0}: {1}" @@ -228,7 +230,8 @@ "गड़बड़ी की रिपोर्ट" "सत्र खत्म करें" "स्क्रीनशॉट" - "गड़बड़ी की रिपोर्ट लें" + + "इससे ईमेल भेजने के लिए, आपके डिवाइस की मौजूदा स्थिति से जुड़ी जानकारी इकट्ठा की जाएगी. गड़बड़ी की रिपोर्ट बनना शुरू होने से लेकर भेजने के लिए तैयार होने तक कुछ समय लगेगा; कृपया इंतज़ार करें." "सहभागी रिपोर्ट" "अधिकांश परिस्थितियों में इसका उपयोग करें. यह आपको रिपोर्ट की प्रगति ट्रैक करने देता है, समस्या के बारे में अधिक विवरण डालने देता है और स्क्रीनशॉट लेने देता है. यह आपको ऐसे कम उपयोग किए गए अनुभाग मिटाने दे सकता है जिनकी रिपोर्ट करने में अधिक समय लगता है." @@ -281,9 +284,12 @@ "जगह" "इस डिवाइस की जगह तक पहुंचने दें" "<b>%1$s</b> को इस डिवाइस की \'जगह की जानकारी\' ऐक्सेस करने की अनुमति देना चाहते हैं?" - "ऐप्लिकेशन, डिवाइस की \'जगह की जानकारी\' सिर्फ़ तभी देख पाएगा जब आप इसका इस्तेमाल कर रहे हों." - "<b>%1$s</b> को हमेशा डिवाइस की \'जगह की जानकारी\' एक्सेस करने दें?" - "ऐप्लिकेशन के पास हमेशा डिवाइस की \'जगह की जानकारी\' देखने की मंज़ूरी होगी, तब भी जब आप इसका इस्तेमाल न कर रहे हों." + + + + + + "कैलेंडर" "अपने कैलेंडर को ऐक्सेस करने" "<b>%1$s</b> को अपना कैलेंडर देखने की अनुमति देना चाहते हैं?" @@ -308,17 +314,17 @@ "फ़ोन" "फ़ोन कॉल करने और उन्हें प्रबंधित करने की अनुमति दें" "<b>%1$s</b> को फ़ोन कॉल करने और उन्हें प्रबंधित करने की अनुमति दें?" - - + "बॉडी सेंसर" "अपने महत्वपूर्ण संकेतों के बारे में सेंसर डेटा को ऐक्सेस करें" "<b>%1$s</b> को अपने स्वास्थ्य से जुड़ी ज़रूरी जानकारी इस्तेमाल करने की अनुमति देना चाहते हैं?" "संगीत" "अपना संगीत संग्रह एक्सेस करने दें" "<b>%1$s</b> को अपना संगीत एक्सेस करने दें?" - - + "फ़ोटो और वीडियो" "अपने फ़ोटो और वीडियो एक्सेस करने दें" - + + + "विंडो की सामग्री वापस पाएं" "उस विंडो की सामग्री की जाँच करें, जिसका आप इस्तेमाल कर रहे हैं." @@ -512,8 +518,10 @@ "ऐप्स को नियर फ़ील्ड कम्यूनिकेशन (NFC) टैग, कार्ड, और रीडर के साथ संचार करने देता है." "अपना स्‍क्रीन लॉक अक्षम करें" "ऐप्स को कीलॉक और कोई भी संबद्ध पासवर्ड सुरक्षा अक्षम करने देता है. उदाहरण के लिए, इनकमिंग फ़ोन कॉल प्राप्त करते समय फ़ोन, कीलॉक को अक्षम कर देता है, फिर कॉल समाप्त होने पर कीलॉक को पुन: सक्षम कर देता है." - "जानें कि स्क्रीन लॉक कितना मुश्किल है" - "यह मंज़ूरी मिलने के बाद ऐप्लिकेशन जान पाता है कि स्क्रीन लॉक कितना मुश्किल (बहुत ज़्यादा, मध्यम, कम या बिल्कुल नहीं) है. इस स्तर से यह पता चलता है कि स्क्रीन लॉक कितना लंबा या किस तरह का है. ऐप्लिकेशन उपयोगकर्ताओं को यह सुझाव भी दे सकता है कि वे स्क्रीन लॉक को एक तय स्तर तक अपडेट करें. हालांकि, उपयोगकर्ता बेझिझक इसे अनदेखा करके आगे बढ़ सकते हैं. ध्यान दें कि स्क्रीन लॉक को सादे टेक्स्ट में सेव नहीं किया जाता है इसलिए ऐप्लिकेशन को बिल्कुल सही पासवर्ड पता नहीं होता." + + + + "बायोमीट्रिक हार्डवेयर इस्तेमाल करने दें" "पुष्टि के लिए, ऐप्लिकेशन को बायोमीट्रिक हार्डवेयर इस्तेमाल करने की मंज़ूरी दें" "उंगली की छाप के लिए हार्डवेयर को प्रबंधित करें" @@ -568,37 +576,59 @@ "ऐप्लिकेशन को चेहरे के टेम्पलेट इस्तेमाल के तरीके जोड़ने और मिटाने की मंज़ूरी मिलती है." "चेहरे की पुष्टि करने वाला हार्डवेयर इस्तेमाल करें" "ऐप्लिकेशन को चेहरे की पुष्टि करने वाले हार्डवेयर का इस्तेमाल करने की मंज़ूरी मिलती है" - "चेहरे की पहचान नहीं हो पाई. कृपया फिर कोशिश करें." - "चेहरे पर रोशनी ज़्यादा है. कृपया कम रोशनी में कोशिश करें." - "चेहरे पर रोशनी बहुत कम है. कृपया रोशनी बढ़ाएं." - "कृपया डिवाइस को चेहरे से दूर ले जाएं." - "कृपया डिवाइस को चेहरे के करीब लाएं." - "कृपया डिवाइस को ऊपर करें." - "कृपया डिवाइस को नीचे की ओर ले जाएं." - "कृपया डिवाइस को चेहरे की दाईं ओर ले जाएं." - "कृपया डिवाइस को चेहरे के बाईं ओर ले जाएं." - "कृपया सेंसर की ओर देखें." - "चेहरे की पहचान नहीं हो पाई." - "डिवाइस बहुत ज़्यादा हिल रहा है." + + + + + + + + + + + + + + + + + + + + + + + + "कृपया फिर से अपने चेहरे की पहचान कराएं." - "यह चेहरा किसी और का है." + + "चेहरा काफ़ी मिलता-जुलता है, कृपया अपना पोज़ बदलें." - "कृपया कैमरे की तरफ़ सीधा देखें." - "कृपया कैमरे की तरफ़ सीधा देखें." + + + + "कृपया अपना सिर सीधा करें, दाएं-बाएं न झुकाएं" - "चेहरा साफ़ नहीं दिख रहा. कृपया रोशनी बढ़ाएं." + + + + "चेहरे की पहचान करने वाला हार्डवेयर मौजूद नहीं है." - "चेहरे की पहचान का समय खत्म हुआ. फिर से कोशिश करें." + + "चेहरा सेव करने की सीमा पूरी हो गई है." "चेहरा पहचानने की कार्रवाई रद्द की गई." "उपयोगकर्ता ने चेहरे की पहचान रद्द कर दी." "कई बार कोशिश की गई. बाद में कोशिश करें." "कई बार कोशिश की. चेहरा पहचानने की सुविधा बंद हुई." - "फिर से कोशिश करें." - "कोई चेहरा रजिस्टर नहीं किया गया है." - "इस डिवाइस में चेहरे की पहचान करने वाला सेंसर नहीं है." + + + + + + "चेहरा %d" @@ -1216,9 +1246,16 @@ "%1$s खोलें" "%1$s बिना सेव किए बंद हो जाएगा" "%1$s मेमोरी सीमा को पार कर गई है" + + "हीप डंप (Java™ प्रोसेस मेमोरी का स्नैपशॉट) ले लिया गया है. शेयर करने के लिए टैप करें." "हीप डंप शेयर करें?" - "यह प्रक्रिया %1$s इसकी %2$s की मेमोरी की सीमा को पार कर गई है. एक हीप डंप मौजूद है जिसे आप इसके डेवलपर से शेयर कर सकते हैं. सावधान रहें: इस हीप डंप में आपकी ऐसी कोई भी निजी जानकारी हो सकती है जिस पर ऐप्लिकेशन की पहुंच हो." + + + + + + "मैसेज करने के लिए कोई कार्रवाई चुनें" "रिंगर वॉल्‍यूम" "मीडिया वॉल्‍यूम" @@ -1257,8 +1294,10 @@ "सभी नेटवर्क देखने के लिए यहां पर टैप करें" "कनेक्ट करें" "सभी नेटवर्क" - "%s का बताया हुआ वाई-फ़ाई नेटवर्क उपलब्ध नहीं है" - "क्या आप %s के बताए हुए नेटवर्क से कनेक्ट करना चाहते हैं?" + + + + "हां" "नहीं" "वाई-फ़ाई अपने आप चालू हो जाएगा" @@ -1270,9 +1309,14 @@ "नेटवर्क में साइन इन करें" - "वाई-फ़ाई के लिए इंटरनेट नहीं मिल रहा है" + + "विकल्पों के लिए टैप करें" "जुड़ गया है" + + + + "आपकी हॉटस्पॉट सेटिंग के हिसाब से बदलाव हो गए हैं" "आपका हॉटस्पॉट बैंड बदल गया है." "यह डिवाइस सिर्फ़ 5 गीगाहर्ट्ज़ की आपकी पसंद की सेटिंग पर काम नहीं करता, लेकिन जब भी 5 गीगाहर्ट्ज़ बैंड मौजूद होगा, डिवाइस उसका इस्तेमाल करने लगेगा." @@ -1357,6 +1401,10 @@ "डीबग करने के लिए एडीबी कनेक्ट किया गया" "यूएसबी को डीबग करने की सुविधा बंद करने के लिए टैप करें" "USB डीबग करना अक्षम करने के लिए चुनें." + + + + "यूएसबी पोर्ट में तरल चीज़ या कचरा है" "यूएसबी पोर्ट अपने आप बंद हो गया है. ज़्यादा जानने के लिए टैप करें." "यूएसबी पोर्ट का इस्तेमाल करना सुरक्षित है" @@ -1908,8 +1956,6 @@ "कार्य प्रोफाइल अनलॉक करने हेतु टैप करें" "%1$s से कनेक्ट किया गया" "फ़ाइलें देखने के लिए टैप करें" - "पिन करें" - "अनपिन करें" "ऐप्लिकेशन की जानकारी" "−%1$s" "डेमो प्रारंभ हो रहा है…" @@ -2000,6 +2046,22 @@ "रूटीन मोड जानकारी की सूचना" "बैटरी आम तौर पर जितने समय चलती है, उससे पहले खत्म हो सकती है" "बैटरी लाइफ़ बढ़ाने के लिए \'बैटरी सेवर\' चालू हो गया है" + + + + + + + + + + + + + + + + "फ़ोल्डर" "Android ऐप्लिकेशन" "फ़ाइल" diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index d50fda6e1df0634f722e616f5ec6df51456d3ce3..478fdad7cdae5401a4c4483d0d359b37870724e1 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -142,8 +142,10 @@ "Wi-Fi pozivi" "VoWifi" "Isključeno" - "Prednost ima Wi-Fi mreža" - "Za mobilne uređaje" + + + + "Samo Wi-Fi" "{0}: Nije proslijeđeno" "{0}: {1}" @@ -230,7 +232,8 @@ "Izvješće o bugovima" "Završi sesiju" "Snimka zaslona" - "Izvješće o programskoj pogrešci" + + "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." "Interaktivno izvješće" "To možete upotrebljavati u većini slučajeva. Moći ćete pratiti izradu izvješća, unijeti više pojedinosti o problemu i izraditi snimke zaslona. Mogu se izostaviti neki odjeljci koji se upotrebljavaju rjeđe i produljuju izradu izvješća." @@ -284,9 +287,12 @@ "Lokacija" "pristupiti lokaciji ovog uređaja" "Želite li dopustiti aplikaciji <b>%1$s</b> da pristupa lokaciji ovog uređaja?" - "Aplikacija će imati pristup lokaciji samo dok upotrebljavate aplikaciju." - "Želite li dopustiti aplikaciji <b>%1$s</b> da pristupa lokaciji ovog uređaja?" - "Aplikacija će uvijek imati pristup lokaciji, čak i dok ne upotrebljavate aplikaciju." + + + + + + "Kalendar" "pristupati kalendaru" "Želite li dopustiti aplikaciji <b>%1$s</b> da pristupa vašem kalendaru?" @@ -319,7 +325,10 @@ "Želite li dopustiti aplikaciji <b>%1$s</b> da pristupa vašoj glazbi?" "Fotografije i videozapisi" "pristup fotografijama i & videozapisima" - "Želite li dopustiti da <b>%1$s</b> pristupa vašim fotografijama i videozapisima, uključujući označene lokacije?" + + + + "Dohvaćati sadržaj prozora" "Pregledat će sadržaj prozora koji upotrebljavate." "Uključiti značajku Istraži dodirom" @@ -512,8 +521,10 @@ "Aplikaciji omogućuje komunikaciju s oznakama, karticama i čitačima komunikacije kratkog dometa (NFC)." "onemogućavanje zaključavanja zaslona" "Aplikaciji omogućuje onemogućavanje zaključavanja tipkovnice i svih pripadajućih sigurnosnih zaporki. Na primjer, telefon onemogućuje zaključavanje tipkovnice kod primanja dolaznog telefonskog poziva, nakon kojeg se zaključavanje tipkovnice ponovo omogućuje." - "zahtijevati složenost zaključavanja zaslona" - "Omogućuje aplikaciji da sazna razinu složenosti zaključavanja zaslona (visoka, srednja, niska ili nijedna), što upućuje na mogući raspon duljine i vrstu zaključavanja zaslona. Aplikacija također korisnicima može predložiti da ažuriraju zaključavanje zaslona na određenu razinu, no korisnici to mogu slobodno zanemariti i nastaviti dalje. Napominjemo da se zaključavanje zaslona ne pohranjuje u običnom tekstu, pa aplikacija ne zna točnu zaporku." + + + + "koristiti biometrijski hardver" "Aplikaciji omogućuje upotrebu biometrijskog hardvera radi autentifikacije" "upravljanje hardverom za čitanje otisaka prstiju" @@ -568,37 +579,59 @@ "Aplikaciji omogućuje pozivanje načina za dodavanje i brisanje predložaka lica za upotrebu." "upotrebljavati hardver za autentifikaciju lica" "Aplikaciji omogućuje upotrebu hardvera za autentifikaciju lica radi autentifikacije" - "Obrada lica nije uspjela. Pokušajte ponovo." - "Lice je presvijetlo. Smanjite osvjetljenje." - "Lice je pretamno. Otkrijte izvor svjetlosti." - "Odmaknite senzor od lica." - "Približite senzor licu." - "Podignite senzor." - "Spustite senzor." - "Pomaknite senzor udesno." - "Pomaknite senzor ulijevo." - "Gledajte senzor." - "Lice nije otkriveno." - "Previše kretanja." + + + + + + + + + + + + + + + + + + + + + + + + "Ponovo registrirajte svoje lice." - "Otkriveno je neko drugo lice." + + "Previše slično, promijenite pozu." - "Gledajte izravnije u kameru." - "Gledajte izravnije u kameru." + + + + "Izravnajte glavu okomito." - "Otkrijte lice." + + + + "Hardver za lice nije dostupan." - "Isteklo je vrijeme čekanja za lice. Pokušajte opet" + + "Nije moguće pohraniti lice." "Otkazana je radnja s licem." "Autentifikaciju lica otkazao je korisnik." "Previše pokušaja. Pokušajte ponovo kasnije." "Previše pokušaja. Autentifikacija lica onemogućena" - "Pokušajte ponovo." - "Nije registrirano nijedno lice." - "Ovaj uređaj nema senzor za autentifikaciju lica." + + + + + + "Lice %d" @@ -1233,9 +1266,16 @@ "Otvori aplikaciju %1$s" "Aplikacija %1$s zatvorit će se bez spremanja" "Proces %1$s premašio je ograničenje memorije" + + "Generirana je snimka memorije. Dodirnite za dijeljenje." "Žalite li dijeliti snimku memorije procesa?" - "Proces %1$s premašio je ograničenje memorije od %2$s. Dostupna vam je snimka memorije procesa koju možete podijeliti s razvojnim programerom. Budite oprezni: ta snimka memorije može sadržavati vaše osobne podatke kojoj aplikacija ima pristup." + + + + + + "Izaberite radnju za tekst" "Glasnoća zvona" "Glasnoća medija" @@ -1276,8 +1316,10 @@ "Dodirnite za prikaz svih mreža" "Poveži" "Sve mreže" - "Dostupna je Wi‑Fi mreža koju predlaže %s" - "Želite li se povezati s mrežama koje predlaže %s?" + + + + "Da" "Ne" "Wi‑Fi će se uključiti automatski" @@ -1289,9 +1331,14 @@ "Prijava na mrežu" - "Wi-Fi nema pristup internetu" + + "Dodirnite za opcije" "Povezano" + + + + "Promjene postavki vaše žarišne točke" "Promijenila se frekvencija vaše žarišne točke." "Ovaj uređaj ne podržava vašu postavku za upotrebu samo 5 GHz. Upotrebljavat će frekvenciju od 5 GHz kada je dostupna." @@ -1376,6 +1423,10 @@ "Priključen je alat za otklanjanje pogrešaka putem USB-a" "Dodirnite da biste isključili otklanjanje pogrešaka putem USB-a" "Odaberite da biste onemogućili rješavanje programske pogreške na USB-u." + + + + "Tekućina ili prljavština u USB priključku" "USB priključak automatski je onemogućen. Dodirnite da biste saznali više." "Upotreba USB priključka ponovo je sigurna" @@ -1939,8 +1990,6 @@ "Dodirnite za otključavanje" "%1$s – veza je uspostavljena" "Dodirnite da biste pregledali datoteke" - "Prikvači" - "Otkvači" "Informacije o aplikaciji" "−%1$s" "Pokretanje demo-načina..." @@ -2032,6 +2081,22 @@ "Obavještavanje o informacijama u Rutinskom načinu rada" "Baterija se može isprazniti prije uobičajenog vremena punjenja" "Štednja baterije aktivirana je kako bi se produljilo trajanje baterije" + + + + + + + + + + + + + + + + "Mapa" "Android aplikacija" "Datoteka" diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 5d0086bc8ecfa64e99aa4bbb9ba1ccd81c2e4c97..2632482a43a162e6ce20bb88e6ae0a070fd75404 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -141,8 +141,10 @@ "Wi-Fi-hívás" "VoWifi" "Ki" - "Wi-Fi előnyben részesítve" - "Preferált: mobil" + + + + "Csak Wi-Fi" "{0}: nincs átirányítva" "{0}: {1}" @@ -228,7 +230,8 @@ "Programhiba bejelentése" "Munkamenet befejezése" "Képernyőkép" - "Hibajelentés készítése" + + "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." "Interaktív jelentés" "Ezt használja a legtöbb esetben. Segítségével nyomon követheti a jelentés folyamatát, további részleteket adhat meg a problémáról, illetve képernyőképeket készíthet. A folyamat során kimaradhatnak az olyan kevésbé használt részek, amelyek jelentése túl sok időt igényel." @@ -281,9 +284,12 @@ "Helyadatok" "hozzáférés az eszköz földrajzi helyéhez" "Engedélyezi a(z) <b>%1$s</b> számára, hogy hozzáférjen az eszköz helyadataihoz?" - "Az alkalmazás csak akkor férhet hozzá a helyadatokhoz, amikor használja az alkalmazást." - "Engedélyezi, hogy a(z) <b>%1$s</b> hozzáférjen a helyadatokhoz?" - "Az alkalmazás bármikor hozzáférhet majd a helyadatokhoz, akkor is, amikor nem használja az alkalmazást." + + + + + + "Naptár" "hozzáférés a naptárhoz" "Engedélyezi a(z) <b>%1$s</b> számára, hogy hozzáférjen a naptárhoz?" @@ -316,7 +322,10 @@ "Engedélyezi a(z) <b>%1$s</b&gt számára, hogy hozzáférjen az Ön zenéihez?" "Fotók és videók" "hozzáférés a fényképekhez és videókhoz" - "Engedélyezi a(z) <b>%1$s</b> számára a fotókhoz és videókhoz (köztük a címkézett helyekhez) való hozzáférést?" + + + + "Ablaktartalom lekérdezése" "A használt ablak tartalmának vizsgálata." "Felfedezés érintéssel bekapcsolása" @@ -509,8 +518,10 @@ "Lehetővé teszi az alkalmazás számára, hogy NFC (Near Field Communication - kis hatósugarú vezeték nélküli kommunikáció) technológiát használó címkékkel, kártyákkal és leolvasókkal kommunikáljon." "képernyőzár kikapcsolása" "Lehetővé teszi az alkalmazás számára a billentyűzár és bármely kapcsolódó jelszavas védelem kikapcsolását. Például a telefon feloldja a billentyűzárat bejövő hívás esetén, majd újra bekapcsolja azt a hívás végeztével." - "képernyőzár összetettségi szintjének lekérése" - "Lehetővé teszi az alkalmazás számára, hogy megismerje a képernyőzár összetettségi szintjét (magas, közepes, alacsony vagy nincs), amely jelzi a képernyőzár lehetséges típusát és hosszát. Az alkalmazás ezenkívül javaslatot tehet a felhasználóknak a képernyőzár bizonyos szintre való frissítésére, de ezt a javaslatot a felhasználók figyelmen kívül hagyhatják. A képernyőzárat nem egyszerű szöveges formátumban tárolja a rendszer, ezért az alkalmazás nem fogja tudni a pontos jelszót." + + + + "biometrikus hardver használata" "Lehetővé teszi az alkalmazás számára a biometrikus hardver hitelesítésre való használatát" "ujjlenyomat-olvasó hardver kezelése" @@ -565,37 +576,59 @@ "Engedélyezi, hogy az alkalmazás arcsablon-hozzáadási és -törlési metódusokat hívjon." "arcfelismerő hardver használata" "Engedélyezi, hogy az alkalmazás hitelesítésre használja az arcfelismerő hardvert" - "Nem sikerült feldolgozni az arcot. Próbálja újra." - "Az arc túl világos. Kevesebb fény szükséges." - "Az arc túl sötét. Több fény szükséges." - "Tartsa az érzékelőt távolabb az arctól." - "Tartsa az érzékelőt közelebb az archoz." - "Emelje magasabbra az érzékelőt." - "Engedje lejjebb az érzékelőt." - "Mozdítsa el jobbra az érzékelőt." - "Mozdítsa el balra az érzékelőt." - "Nézzen az érzékelőbe." - "Nem észlelhető arc." - "Túlságosan sok a mozgás." + + + + + + + + + + + + + + + + + + + + + + + + "Rögzítsen újra képet az arcáról." - "Másik arcot észleltünk." + + "Túlságosan hasonló, változtasson a pózon." - "Nézzen szemből a kamerába." - "Nézzen szemből a kamerába." + + + + "Fejét tartsa egyenesen, függőleges irányban." - "Ne takarja el az arcát." + + + + "Az arcfelismerő hardver nem áll rendelkezésre." - "Időtúllépés az arcbeolvasásnál. Próbálja újra." + + "Az arc nem tárolható." "Az arccal kapcsolatos művelet törölve." "Az arc hitelesítését a felhasználó visszavonta." "Túl sok próbálkozás. Próbálja újra később." "Túl sok próbálkozás. Arcfelismerés letiltva." - "Próbálkozzon újra." - "Nincs regisztrált arc." - "Ez az eszköz nem rendelkezik arcfelismerő érzékelővel." + + + + + + "%d arc" @@ -1213,9 +1246,16 @@ "%1$s megnyitása" "A(z) %1$s mentés nélkül bezáródik" "A(z) %1$s túllépte a memóriakorlátot" + + "Elkészült a memória-pillanatfelvétel (heap dump). A megosztáshoz koppintson rá." "Megosztja a memória-pillanatfelvételt?" - "A(z) %1$s folyamat túllépte a(z) %2$s méretű memóriakorlátot. Elérhető a memória-pillanatfelvétel (heap dump), hogy megossza a fejlesztővel. Figyelem: ez a felvétel tartalmazhatja az Ön olyan személyes adatait, amelyekhez az alkalmazásnak hozzáférése van." + + + + + + "Válasszon ki egy műveletet a szöveghez" "Csengetés hangereje" "Média hangereje" @@ -1254,8 +1294,10 @@ "Koppintással megjelenítheti az összes hálózatot" "Kapcsolódás" "Összes hálózat" - "Rendelkezésre áll egy %s által javasolt Wi-Fi-hálózat" - "Szeretne csatlakozni a(z) %s által javasolt hálózatokhoz?" + + + + "Igen" "Nem" "A Wi-Fi automatikusan bekapcsol" @@ -1267,9 +1309,14 @@ "Bejelentkezés a hálózatba" - "A Wi-Fi-hálózaton nincs internetkapcsolat" + + "Koppintson a beállítások megjelenítéséhez" "Csatlakozva" + + + + "A hotspot beállításainak módosítása" "A hotspot sávja megváltozott." "Ez az eszköz nem támogatja a csak 5 GHz-es sávra vonatkozó beállítást. Az eszköz akkor használ 5 GHz-es sávot, ha a sáv rendelkezésre áll." @@ -1354,6 +1401,10 @@ "USB hibakereső csatlakoztatva" "Koppintson az USB-hibakeresés kikapcsolásához" "Válassza ezt az USB hibakeresés kikapcsolásához." + + + + "Folyadék vagy szennyeződés az USB-portban" "USB-port automatikusan letiltva. Koppintson, ha további információra van szüksége." "Az USB-port biztonságosan használható" @@ -1905,8 +1956,6 @@ "A feloldáshoz koppintson rá" "Csatlakoztatva a(z) %1$s eszközhöz" "Koppintson ide a fájlok megtekintéséhez" - "Rögzítés" - "Feloldás" "Alkalmazásinformáció" "−%1$s" "Bemutató indítása…" @@ -1997,6 +2046,22 @@ "Információs értesítés a rutinmódról" "Előfordulhat, hogy az akkumulátor lemerül a szokásos töltési időszak előtt" "Akkumulátorkímélő mód aktiválva az akkumulátor üzemidejének növelése érdekében" + + + + + + + + + + + + + + + + "Mappa" "Android-alkalmazás" "Fájl" diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 6432e37c905915795130e344fa8346247603c0cf..dc7731957174a9c4e32656880c1ea136897001d3 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -141,8 +141,10 @@ "Panggilan WiFi" "VoWifi" "Nonaktif" - "Wi-Fi dipilih" - "Seluler dipilih" + + + + "Khusus Wi-Fi" "{0}: Tidak diteruskan" "{0}: {1}" @@ -228,7 +230,8 @@ "Laporan bug" "Akhiri sesi" "Screenshot" - "Ambil laporan bug" + + "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." "Laporan interaktif" "Gunakan ini di berbagai keadaan. Ini memungkinkan Anda melacak kemajuan laporan, memasukkan detail masalah selengkapnya, dan mengambil screenshot. Mungkin menghilangkan beberapa bagian yang jarang digunakan dan yang perlu waktu lama untuk dilaporkan." @@ -281,9 +284,12 @@ "Lokasi" "mengakses lokasi perangkat ini" "Izinkan <b>%1$s</b> mengakses lokasi perangkat ini?" - "Aplikasi ini hanya akan memiliki akses ke lokasi saat Anda sedang menggunakan aplikasi." - "Selalu izinkan <b>%1$s</b> mengakses lokasi perangkat ini?" - "Aplikasi ini akan selalu memiliki akses ke lokasi, bahkan saat Anda sedang tidak menggunakan aplikasi." + + + + + + "Kalender" "mengakses kalender" "Izinkan <b>%1$s</b> mengakses kalender?" @@ -316,7 +322,10 @@ "Izinkan <b>%1$s</b> untuk mengakses musik?" "Foto & video" "mengakses foto & video Anda" - "Izinkan <b>%1$s</b> mengakses foto dan video Anda, termasuk lokasi yang diberi tag?" + + + + "Mengambil konten jendela" "Memeriksa konten jendela tempat Anda berinteraksi." "Mengaktifkan Jelajahi dengan Sentuhan" @@ -509,8 +518,10 @@ "Mengizinkan apl berkomunikasi dengan tag, kartu, dan alat pembaca Komunikasi Nirkabel Jarak Dekat (NFC)." "nonaktifkan kunci layar Anda" "Memungkinkan aplikasi menonaktifkan kunci tombol dan keamanan sandi apa pun yang terkait. Misalnya, ponsel menonaktifkan kunci tombol saat menerima panggilan telepon masuk, kemudian mengaktifkan kembali kunci tombol ketika panggilan selesai." - "meminta kompleksitas kunci layar" - "Mengizinkan aplikasi mempelajari tingkat kompleksitas kunci layar (tinggi, sedang, rendah, atau tidak ada), yang menunjukkan kemungkinan rentang durasi dan jenis kunci layar. Aplikasi juga dapat menyarankan agar pengguna memperbarui kunci layar ke tingkat tertentu, namun pengguna dapat mengabaikan dan keluar dengan bebas. Perlu diperhatikan bahwa kunci layar tidak disimpan dalam teks biasa, sehingga aplikasi tidak mengetahui sandi yang sebenarnya." + + + + "gunakan hardware biometrik" "Mengizinkan aplikasi menggunakan hardware biometrik untuk autentikasi" "kelola hardware sidik jari" @@ -565,37 +576,59 @@ "Mengizinkan apl memicu metode untuk menambah & menghapus template wajah untuk digunakan." "gunakan hardware autentikasi wajah" "Mengizinkan aplikasi untuk menggunakan hardware autentikasi wajah untuk autentikasi" - "Tidak dapat memproses wajah. Harap coba lagi." - "Wajah terlalu cerah. Coba dengan cahaya lebih redup." - "Wajah terlalu gelap. Buka sumber cahaya." - "Jauhkan sensor dari wajah." - "Dekatkan sensor ke wajah." - "Naikkan sensor." - "Turunkan sensor." - "Gerakkan sensor ke kanan." - "Gerakkan sensor ke kiri." - "Lihat sensor." - "Tidak ada wajah yang terdeteksi" - "Terlalu banyak gerakan." + + + + + + + + + + + + + + + + + + + + + + + + "Daftarkan ulang wajah Anda." - "Wajah yang berbeda terdeteksi." + + "Terlalu mirip, ubah pose Anda." - "Lihat langsung ke kamera." - "Lihat langsung ke kamera." + + + + "Luruskan kepala secara vertikal." - "Tunjukkan wajah Anda." + + + + "Hardware pemrosesan wajah tidak tersedia." - "Waktu tunggu wajah habis. Harap coba lagi." + + "Wajah tidak dapat disimpan." "Pemrosesan wajah dibatalkan." "Autentikasi wajah dibatalkan oleh pengguna." "Terlalu banyak percobaan. Coba lagi nanti." "Terlalu sering dicoba. Autentikasi wajah dinonaktifkan." - "Coba lagi." - "Tidak ada wajah yang didaftarkan." - "Perangkat ini tidak memiliki sensor autentikasi wajah." + + + + + + "%d wajah" @@ -1213,9 +1246,16 @@ "Buka %1$s" "%1$s akan ditutup tanpa menyimpan" "%1$s melampaui batas memori" + + "Informasi memori dikumpulkan. Tap untuk membagikan." "Share tumpukan membuang?" - "Proses %1$s telah melampaui batas memori proses %2$s. Tumpukan sampah tersedia untuk Anda bagikan kepada pengembangnya. Hati-hati, tumpukan sampah ini hanya dapat memuat informasi pribadi yang dapat diakses oleh aplikasi." + + + + + + "Pilih tindakan untuk teks" "Volume dering" "Volume media" @@ -1254,8 +1294,10 @@ "Tap untuk melihat semua jaringan" "Hubungkan" "Semua jaringan" - "Jaringan Wi‑Fi yang diusulkan oleh %s tersedia" - "Sambungkan ke jaringan yang diusulkan oleh %s?" + + + + "Ya" "Tidak" "Wi‑Fi akan aktif otomatis" @@ -1267,9 +1309,14 @@ "Login ke jaringan" - "Wi-Fi tidak memiliki akses internet" + + "Tap untuk melihat opsi" "Tersambung" + + + + "Perubahan pada setelan hotspot Anda" "Pita hotspot Anda telah berubah." "Perangkat ini tidak mendukung preferensi Anda, yaitu hanya 5GHz. Sebagai gantinya, perangkat ini akan menggunakan pita frekuensi 5GHz jika tersedia." @@ -1354,6 +1401,10 @@ "Debugging USB terhubung" "Tap untuk menonaktifkan debug USB" "Pilih untuk menonaktifkan debugging USB." + + + + "Cairan atau kotoran di port USB" "Port USB otomatis dinonaktifkan. Tap untuk mempelajari lebih lanjut." "Port USB aman digunakan" @@ -1905,8 +1956,6 @@ "Tap untuk membuka kunci profil kerja" "Tersambung ke %1$s" "Tap untuk melihat file" - "Pasang pin" - "Lepas pin" "Info aplikasi" "−%1$s" "Memulai demo..." @@ -1997,6 +2046,22 @@ "Notifikasi info Mode Rutinitas" "Baterai mungkin habis sebelum pengisian daya biasanya" "Penghemat Baterai diaktifkan untuk memperpanjang masa pakai baterai" + + + + + + + + + + + + + + + + "Folder" "Aplikasi Android" "File" diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index d27d8b58e6cf6e01ff096ee649fb92e6d95bf1a1..3f8c96013ae4ab1bf29b257592436cba5f0739d1 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -141,8 +141,10 @@ "WiFi símtöl" "VoWifi" "Slökkt" - "Wi-Fi í forgangi" - "Farsímakerfi í forgangi" + + + + "Wi-Fi eingöngu" "{0}: Ekki áframsent" "{0}: {1}" @@ -228,7 +230,8 @@ "Villutilkynning" "Ljúka lotu" "Skjámynd" - "Útbúa villutilkynningu" + + "Þ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." "Gagnvirk skýrsla" "Þú getur notað þetta í flestum tilvikum. Með þessu móti geturðu fylgst með framgangi tilkynningarinnar og slegið inn viðbótarupplýsingar um vandamálið. Hugsanlegt er að lítið notuðum hlutum verði sleppt til að spara tíma." @@ -281,9 +284,12 @@ "Staðsetning" "fá aðgang að staðsetningu þessa tækis" "Viltu veita <b>%1$s</b> aðgang að staðsetningu þessa tækis?" - "Forritið hefur aðeins aðgang að staðsetningunni á meðan þú notar forritið." - "Viltu alltaf veita <b>%1$s</b> aðgang að staðsetningu þessa tækis?" - "Forritið hefur alltaf aðgang að staðsetningunni, jafnvel þegar þú ert ekki að nota forritið." + + + + + + "Dagatal" "fá aðgang að dagatalinu þínu" "Viltu veita <b>%1$s</b> aðgang að dagatalinu þínu?" @@ -316,7 +322,10 @@ "Viltu veita <b>%1$s</b> aðgang að tónlistinni þinni?" "Myndir og myndskeið" "fá aðgang að myndunum og myndskeiðunum þínum" - "Viltu veita <b>%1$s</b> aðgang að myndunum þínum og myndskeiðum, þ.m.t. merktum staðsetningum?" + + + + "Sækja innihald glugga" "Kanna innihald glugga sem þú ert að nota." "Kveikja á snertikönnun" @@ -509,8 +518,10 @@ "Leyfir forriti að eiga samskipti við NFC-merki, -spjöld og -lesara (nándarsamskipti)." "slökkva á skjálásnum" "Leyfir forriti að slökkva á símalásnum og öðrum öryggisaðgerðum tengdum aðgangsorði. Til dæmis gerir síminn lásinn óvirkan þegar símtal berst og læsist svo aftur að símtali loknu." - "biðja um flókinn skjálás" - "Leyfir forritinu að læra hversu flókinn skjálásinn er (erfiður, miðlungs, léttur eða enginn), sem gefur til kynna lengd og tegund skjálássins. Forritið getur einnig lagt til að notendur geri skjálásinn flóknari upp að tilteknu marki, en notendur geta valið að hunsa það og halda áfram að vafra. Hafðu í huga að skjálásinn er ekki geymdur í ódulkóðuðum texta svo forritið veit ekki nákvæmt aðgangsorð." + + + + "nota búnað fyrir líffræðileg gögn" "Leyfir forritinu að nota búnað fyrir líffræðileg gögn til auðkenningar" "stjórna fingrafarabúnaði" @@ -565,37 +576,59 @@ "Leyfir forritinu að beita aðferðum til að bæta við og eyða andlitssniðmátum til notkunar." "nota vélbúnað andlitsgreiningar" "Leyfir forritinu að nota andlitsgreiningarvélbúnað til auðkenningar" - "Ekki tókst að vinna úr andlitinu. Reyndu aftur." - "Andlitið er of bjart. Prófaðu í minni birtu." - "Andlitið er of dökkt. Finndu betri lýsingu." - "Færðu skynjarann lengra frá andlitinu." - "Færðu skynjarann nær andlitinu." - "Færðu skynjarann ofar." - "Færðu skynjarann neðar." - "Færðu skynjarann til hægri." - "Færðu skynjarann til vinstri." - "Horfðu á skynjarann." - "Ekkert andlit fannst." - "Of mikil hreyfing." + + + + + + + + + + + + + + + + + + + + + + + + "Skráðu nafnið þitt aftur." - "Annað andlit greint." + + "Of svipað. Stilltu þér öðruvísi upp." - "Líttu beint í myndavélina." - "Líttu beint í myndavélina." + + + + "Vinsamlega réttu úr höfðinu." - "Ekki hylja andlitið." + + + + "Andlitsvélbúnaður ekki til staðar." - "Tímamörk runnu út fyrir andlit. Reyndu aftur." + + "Ekki tókst að geyma andlit." "Hætt við andlitsgreiningu." "Notandi hætti við andlitsgreiningu." "Of margar tilraunir. Reyndu aftur síðar." "Of margar tilraunir. Slökkt á andlitsgreiningu." - "Reyndu aftur." - "Ekkert andlit skráð." - "Þetta tæki er ekki með skynjara fyrir andlitsgreiningu." + + + + + + "Andlit %d" @@ -1213,9 +1246,16 @@ "Opna %1$s" "%1$s verður lokað án þess að vista" "%1$s er yfir minnishámarki" + + "Minnisgögnum hefur verið safnað. Ýttu til að deila." "Deila minnisgögnum?" - "Ferlið %1$s er komið yfir %2$s minnishámark sitt. Þú getur deilt minnisgögnum (heap dump) með þróunaraðilanum. Athugaðu að minnisgögnin kunna að innihalda allar þær persónuupplýsingar sem forritið hefur aðgang að um þig." + + + + + + "Veldu aðgerð fyrir texta" "Hljóðstyrkur hringingar" "Hljóðstyrkur efnisspilunar" @@ -1254,8 +1294,10 @@ "Ýttu til að sjá öll netkerfi" "Tengjast" "Öll netkerfi" - "Wi‑Fi net sem %s stakk upp á er í boði" - "Viltu tengjast netum sem %s stakk upp á?" + + + + "Já" "Nei" "Kveikt verður sjálfkrafa á Wi‑Fi" @@ -1267,9 +1309,14 @@ "Skrá inn á net" - "Wi-Fi er ekki með tengingu við internetið" + + "Ýttu til að sjá valkosti" "Tengt" + + + + "Breytingar á stillingum heits reits" "Tíðnisvið heita reitsins hefur breyst." "Þetta tæki styður ekki val þitt fyrir aðeins 5 GHz. Í staðinn mun þetta tæki nota 5 GHz tíðnisvið þegar það er í boði." @@ -1355,6 +1402,10 @@ "USB-villuleit tengd" "Ýttu til að slökkva á USB-villuleit" "Veldu til að gera USB-villuleit óvirka." + + + + "Vökvi eða óhreinindi í USB-tengi" "USB-tengi er gert óvirkt sjálfkrafa. Ýttu til að fá frekari upplýsingar." "Óhætt að nota USB-tengi" @@ -1906,8 +1957,6 @@ "Ýttu til að opna vinnusnið" "Tengt við %1$s" "Ýttu til að skoða skrárnar" - "Festa" - "Losa" "Forritsupplýsingar" "−%1$s" "Byrjar kynningu…" @@ -1998,6 +2047,22 @@ "Upplýsingatilkynning aðgerðastillingar" "Rafhlaðan kann að tæmast áður en hún kemst í hleðslu" "Kveikt á rafhlöðusparnaði til að lengja endingu rafhlöðunnar" + + + + + + + + + + + + + + + + "Mappa" "Android forrit" "Skrá" diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index c5592b6488dbc3ce7466d2a26784bd4cdf785095..255510a1208a192efbd458f1c35e2acf70f25220 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -141,8 +141,10 @@ "Chiamate Wi-Fi" "VoWifi" "Non attiva" - "Rete preferita: Wi-Fi" - "Rete preferita: dati mobili" + + + + "Solo Wi-Fi" "{0}: inoltro non effettuato" "{0}: {1}" @@ -228,7 +230,8 @@ "Segnalazione di bug" "Termina sessione" "Screenshot" - "Apri segnalazione bug" + + "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." "Rapporto interattivo" "Utilizza questa opzione nella maggior parte dei casi. Ti consente di monitorare l\'avanzamento della segnalazione, di inserire maggiori dettagli relativi al problema e di acquisire screenshot. Potrebbero essere omesse alcune sezioni meno utilizzate il cui inserimento nella segnalazione richiede molto tempo." @@ -281,9 +284,12 @@ "Geolocalizzazione" "accedere alla posizione di questo dispositivo" "Consentire all\'app <b>%1$s</b> di accedere alla posizione di questo dispositivo?" - "L\'app avrà accesso alla posizione soltanto quando la usi." - "Vuoi consentire sempre a <b>%1$s</b> di accedere alla posizione di questo dispositivo?" - "L\'app avrà sempre accesso alla posizione, anche quando non la usi." + + + + + + "Calendario" "accedere al calendario" "Consentire all\'app <b>%1$s</b> di accedere al tuo calendario?" @@ -316,7 +322,10 @@ "Vuoi consentire all\'app <b>%1$s</b> di accedere alla tua musica?" "Foto e video" "Accesso alle tue foto e ai tuoi video" - "Vuoi consentire a <b>%1$s</b> di accedere alle tue foto e ai tuoi video, incluse le località taggate?" + + + + "Recuperare contenuti della finestra" "Esaminare i contenuti di una finestra con cui interagisci." "Attivare Esplora al tocco" @@ -509,8 +518,10 @@ "Consente all\'applicazione di comunicare con tag, schede e lettori NFC (Near Field Communication)." "disattivazione blocco schermo" "Consente all\'applicazione di disattivare il blocco tastiera ed eventuali protezioni tramite password associate. Ad esempio, il telefono disattiva il blocco tastiera quando riceve una telefonata in arrivo e lo riattiva al termine della chiamata." - "richiedi complessità del blocco schermo" - "Consente all\'app di conoscere il livello di complessità del blocco schermo (alto, medio, basso o nessuno), che indica l\'intervallo di caratteri possibile e il tipo di blocco schermo. L\'app può inoltre suggerire agli utenti di aggiornare il blocco schermo a un livello specifico di complessità, ma gli utenti possono ignorare liberamente il suggerimento e uscire. Tieni presente che il blocco schermo non viene memorizzato come testo non crittografato, quindi l\'app non conosce la password esatta." + + + + "Utilizzo di hardware biometrico" "Consente all\'app di utilizzare hardware biometrico per eseguire l\'autenticazione" "gestisci hardware per il riconoscimento delle impronte digitali" @@ -565,37 +576,59 @@ "Consente all\'app di richiamare i metodi per aggiungere e rimuovere i modelli di volti." "utilizza l\'hardware per l\'autenticazione dei volti" "Consente all\'app di utilizzare hardware per l\'autenticazione dei volti" - "Impossibile elaborare il volto. Riprova." - "Volto troppo luminoso. Prova dove c\'è meno luce." - "Volto troppo scuro. Non coprire la fonte di luce." - "Allontana il sensore dal volto." - "Avvicina il sensore al volto." - "Sposta il sensore verso l\'alto." - "Sposta il sensore verso il basso." - "Sposta il sensore verso destra." - "Sposta il sensore verso sinistra." - "Guarda il sensore." - "Nessun volto rilevato." - "Troppo movimento." + + + + + + + + + + + + + + + + + + + + + + + + "Ripeti l\'acquisizione del volto." - "È stato rilevato un volto diverso." + + "Troppo simile; cambia posa." - "Guarda più direttamente verso la fotocamera." - "Guarda più direttamente verso la fotocamera." + + + + "Raddrizza la testa in verticale." - "Scopri il volto." + + + + "Hardware per il volto non disponibile." - "Timeout operazione associata al volto. Riprova." + + "Il volto non può essere memorizzato." "Operazione associata al volto annullata." "Autenticazione del volto annullata dall\'utente." "Troppi tentativi. Riprova più tardi." "Troppi tentativi. Autenticazione disattivata." - "Riprova." - "Nessun volto registrato." - "Questo dispositivo non dispone di sensore di autenticazione." + + + + + + "Volto %d" @@ -1213,9 +1246,16 @@ "Apri %1$s" "%1$s si chiuderà senza salvare" "%1$s ha superato il limite di memoria" + + "Dump dell\'heap raccolto. Tocca per condividere." "Condividere il dump dell\'heap?" - "Il processo %1$s ha superato il suo limite di memoria pari a %2$s. È disponibile un dump dell\'heap che puoi condividere con lo sviluppatore. Presta attenzione: questo dump dell\'heap può contenere tue informazioni personali a cui l\'applicazione ha accesso." + + + + + + "Scegli un\'azione per il testo" "Volume suoneria" "Volume contenuti multimediali" @@ -1254,8 +1294,10 @@ "Tocca per vedere tutte le reti" "Connetti" "Tutte le reti" - "È disponibile una rete Wi-Fi suggerita da %s" - "Vuoi collegarti alle reti suggerite da %s?" + + + + "Sì" "No" "Il Wi‑Fi verrà attivato automaticamente" @@ -1267,9 +1309,14 @@ "Accedi alla rete" - "La rete Wi-Fi non ha accesso a Internet" + + "Tocca per le opzioni" "Connesso" + + + + "Modifiche alle tue impostazioni dell\'hotspot" "La tua banda di hotspot è cambiata." "Questo dispositivo non supporta la tua preferenza esclusiva per 5 GHz. Utilizzerà invece la banda a 5 GHz solo quando è disponibile." @@ -1354,6 +1401,10 @@ "Debug USB collegato" "Tocca per disattivare il debug USB" "Seleziona per disattivare il debug USB." + + + + "Liquidi o detriti nella porta USB" "La porta USB viene disattivata automaticamente. Tocca per avere ulteriori informazioni." "Puoi usare la porta USB in sicurezza" @@ -1905,8 +1956,6 @@ "Tocca per sbloc. prof. di lav." "Connesso a %1$s" "Tocca per visualizzare i file" - "Blocca" - "Sgancia" "Informazioni app" "−%1$s" "Avvio della demo…" @@ -1997,6 +2046,22 @@ "Notifica di informazioni sulla modalità Routine" "La batteria potrebbe esaurirsi prima della ricarica abituale" "Risparmio energetico attivo per far durare di più la batteria" + + + + + + + + + + + + + + + + "Cartella" "Applicazione Android" "File" diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 89813a74934a58c10ccefe86286ce85f2628e379..a42f6cc61f74cfc3028d7deff98a220d93235169 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -143,8 +143,10 @@ "‏שיחות Wi-Fi" "VoWifi" "כבוי" - "‏Wi-Fi מועדף" - "מצב מועדף: רשת סלולרית" + + + + "‏Wi-Fi בלבד" "{0}: ללא העברה" "{0}: {1}" @@ -232,7 +234,8 @@ "דיווח על באג" "סיום הפעלה" "צילום מסך" - "שלח דיווח על באג" + + "פעולה זו תאסוף מידע על מצב המכשיר הנוכחי שלך על מנת לשלוח אותו כהודעת אימייל. היא תימשך זמן קצר מרגע פתיחת דיווח הבאג ועד לשליחת ההודעה בפועל. אנא המתן בסבלנות." "דוח אינטראקטיבי" "השתמש באפשרות זו ברוב המקרים. היא מאפשרת לך לעקוב אחר התקדמות הדוח, להזין פרטים נוספים על הבעיה וליצור צילומי מסך. היא עשויה להשמיט כמה קטעים שנמצאים פחות בשימוש ואשר יצירת הדיווח עליהם נמשכת זמן רב." @@ -287,9 +290,12 @@ "מיקום" "גישה אל מיקום המכשיר הזה" "‏לתת לאפליקציה <b>%1$s</b> הרשאת גישה למיקום המכשיר?" - "לאפליקציה תהיה גישה אל נתוני המיקום רק במהלך השימוש באפליקציה." - "‏תמיד לתת לאפליקציה <b>%1$s</b> הרשאת גישה למיקום המכשיר?" - "לאפליקציה תמיד תהיה גישה לנתוני המיקום, גם כשהאפליקציה אינה בשימוש." + + + + + + "יומן" "גישה אל היומן" "‏לתת לאפליקציה <b>%1$s</b> הרשאת גישה ליומן?" @@ -322,7 +328,10 @@ "‏האם לתת לאפליקציה <b>%1$s</b> הרשאת גישה למוזיקה שלך?" "תמונות וסרטונים" "גישה לתמונות ולסרטונים שלך" - "‏האם לאפשר ל-<b>%1$s</b> לקבל גישה לתמונות ולסרטונים שלך, כולל מיקומים מתויגים?" + + + + "אחזור תוכן של חלון" "בדוק את התוכן של חלון שאיתו אתה מבצע אינטראקציה." "הפעלה של \'גילוי באמצעות מגע\'" @@ -515,8 +524,10 @@ "מאפשר לאפליקציה נהל תקשורת עם תגים, כרטיסים וקוראים מסוג \'תקשורת מטווח קצר\'." "ביטול נעילת המסך שלך" "מאפשר לאפליקציה להשבית את נעילת המקשים וכל אמצעי אבטחה משויך המבוסס על סיסמה. לדוגמה, הטלפון משבית את נעילת המקשים בעת קבלה של שיחת טלפון נכנסת, ולאחר מכן מפעיל מחדש את נעילת המקשים עם סיום השיחה." - "בקשה לפרטי המורכבות של נעילת מסך" - "‏מאפשרת לאפליקציה ללמוד את רמת המורכבות של נעילת המסך (גבוהה, בינונית, נמוכה או ללא). רמה זו מציינת את הטווח האפשרי לאורך ולסוג של נעילת המסך. האפליקציה יכולה גם להציע למשתמשים שיעדכנו את נעילת המסך ברמה מסוימת, אבל המשתמשים יכולים להתעלם מההצעה ולנווט לפריט אחר כרצונם. יש לשים לב שנעילת המסך לא מאוחסנת ב-plaintext, ולכן האפליקציה לא יודעת מה הסיסמה המדויקת." + + + + "שימוש בחומרה ביומטרית" "מאפשרת לאפליקציה להשתמש בחומרה ביומטרית לצורך אימות" "ניהול חומרה של טביעות אצבעות" @@ -571,37 +582,59 @@ "מאפשרת לאפליקציה להפעיל שיטות להוספה ומחיקה של תבניות פנים שבהן ייעשה שימוש." "שימוש בחומרה של זיהוי פנים לצורך אימות" "מאפשרת לאפליקציה להשתמש בחומרה של זיהוי פנים לצורך אימות" - "לא ניתן היה לעבד את הפנים. יש לנסות שוב." - "הפנים בהירים מדי. יש לנסות באור עמום יותר." - "הפנים כהים מדי. יש להוסיף מקור אור." - "יש להרחיק את החיישן מהפנים." - "יש לקרב את החיישן לפנים." - "יש להזיז את החיישן גבוה יותר." - "יש להזיז את החיישן נמוך יותר." - "יש להזיז את החיישן ימינה." - "יש להזיז את החיישן שמאלה." - "יש להסתכל על החיישן." - "לא זוהו פנים." - "יש יותר מדי תנועה." + + + + + + + + + + + + + + + + + + + + + + + + "יש לרשום מחדש את הפנים." - "זוהו פנים שונות." + + "דומה מדי, יש לשנות תנוחה." - "יש להביט ישירות אל המצלמה." - "יש להביט ישירות אל המצלמה." + + + + "יש ליישר את הראש במאונך." - "יש להסיר את הכיסוי מהפנים." + + + + "החומרה לזיהוי הפנים לא זמינה." - "חלף הזמן הקצוב לזיהוי הפנים. יש לנסות שוב." + + "לא ניתן לשמור את הפנים." "פעולת הפנים בוטלה." "זיהוי הפנים בוטל על ידי המשתמש." "יותר מדי ניסיונות. יש לנסות שוב מאוחר יותר." "יותר מדי ניסיונות. אימות הפנים הושבת." - "יש לנסות שוב." - "לא נרשמו פנים." - "במכשיר זה אין חיישן לאימות פנים." + + + + + + "פנים %d" @@ -1253,9 +1286,16 @@ "פתיחת %1$s" "%1$s האפליקציה תיסגר ללא שמירה" "%1$s חורג מהגבלת הזיכרון" + + "‏Dump של ערימה נאסף. יש להקיש כדי לשתף." "‏האם לשתף את נתוני ה-Dump של הערימה?" - "‏התהליך %1$s חרג ממגבלת זיכרון התהליך שלו, בגודל %2$s. נתונים על Dump של ערימה זמינים לך לשיתוף עם המפתח של התהליך. זהירות: ה-Dump של הערימה יכול להכיל מידע אישי הזמין לאפליקציה." + + + + + + "בחירת פעולה לביצוע עם טקסט" "עוצמת קול של צלצול" "עוצמת קול של מדיה" @@ -1298,8 +1338,10 @@ "יש להקיש כדי לראות את כל הרשתות" "התחבר" "כל הרשתות" - "‏רשת ה-Wi‑Fi שהוצעה על ידי %s לא זמינה" - "רוצה להתחבר לרשתות שהוצעו על ידי %s?" + + + + "כן" "לא" "‏ה-Wi-Fi יופעל אוטומטית" @@ -1311,9 +1353,14 @@ "היכנס לרשת" - "‏לרשת ה-Wi-Fi אין גישה לאינטרנט" + + "הקש לקבלת אפשרויות" "הרשת מחוברת" + + + + "שינויים להגדרות של הנקודה לשיתוף אינטרנט" "התדר של הנקודה לשיתוף אינטרנט השתנה." "‏מכשיר זה לא תומך בהעדפות שלך ל-5GHz בלבד. במקום זאת, מכשיר זה ישתמש בתדר 5GHz כשיהיה זמין." @@ -1398,6 +1445,10 @@ "‏ניפוי באגים של USB מחובר" "‏יש להקיש כדי לכבות את ניפוי הבאגים ב-USB" "‏בחר להשבית ניפוי באגים ב-USB." + + + + "‏יש נוזלים או חלקיקים ביציאת ה-USB" "‏יציאת ה-USB הושבתה באופן אוטומטי. יש להקיש לקבלת מידע נוסף." "‏יציאת ה-USB בטוחה לשימוש" @@ -1973,8 +2024,6 @@ "הקש לביטול נעילת פרופיל העבודה" "מחובר אל %1$s" "הקש כדי להציג קבצים" - "הצמד" - "בטל הצמדה" "פרטי אפליקציה" "−%1$s" "מתחיל בהדגמה…" @@ -2067,6 +2116,22 @@ "התראת מידע לגבי מצב שגרתי" "הסוללה עלולה להתרוקן לפני המועד הרגיל של הטעינה" "תכונת החיסכון בסוללה הופעלה כדי להאריך את חיי הסוללה" + + + + + + + + + + + + + + + + "תיקייה" "‏אפליקציית Android" "קובץ" diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 79e2f09d51dde3e8d9562496efdd704ee1534f8b..9e466cb4ac321bae17bebd45b6582875efd2eff6 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -141,8 +141,10 @@ "Wi-Fi 通話" "VoWifi" "OFF" - "Wi-Fi優先" - "モバイル優先" + + + + "Wi-Fiのみ" "{0}:転送できません" "{0}:{1}" @@ -228,7 +230,8 @@ "バグレポート" "セッションを終了" "スクリーンショット" - "バグレポートを取得" + + "現在の端末の状態に関する情報が収集され、その内容がメールで送信されます。バグレポートが開始してから送信可能な状態となるまでには多少の時間がかかりますのでご了承ください。" "対話型レポート" "ほとんどの場合はこのオプションを使用します。レポートの進行状況を追跡し、問題についての詳細情報の確認やスクリーンショットの作成が可能です。レポート作成に時間がかかる、あまり使用されない項目は省略されることがあります。" @@ -281,9 +284,12 @@ "位置情報" "この端末の位置情報へのアクセス" "この端末の位置情報へのアクセスを <b>%1$s</b> に許可しますか?" - "このアプリは、使用時のみ、位置情報にアクセスできるようになります。" - "この端末の位置情報へのアクセスを <b>%1$s</b> に常に許可しますか?" - "このアプリは、未使用時も含め、常に位置情報にアクセスできるようになります。" + + + + + + "カレンダー" "カレンダーへのアクセス" "カレンダーへのアクセスを <b>%1$s</b> に許可しますか?" @@ -316,7 +322,10 @@ "音楽へのアクセスを <b>%1$s</b> に許可しますか?" "写真と動画" "写真と動画へのアクセス" - "タグ付けされた場所を含め、写真と動画へのアクセスを <b>%1$s</b> に許可しますか?" + + + + "ウィンドウコンテンツの取得" "ユーザーがアクセスしているウィンドウのコンテンツを検査します。" "タッチガイドの有効化" @@ -509,8 +518,10 @@ "NFCタグ、カード、リーダーとの通信をアプリに許可します。" "画面ロックの無効化" "キーロックとキーロックに関連付けられたパスワードのセキュリティを無効にすることをアプリに許可します。たとえば、かかってきた電話を受ける際にキーロックを無効にし、通話が終了したらキーロックを再度有効にする場合などに使用します。" - "画面ロックの複雑さのリクエスト" - "このアプリに画面ロックの複雑さレベル(高、中、低、なし)を認識することを許可します。複雑さレベルは、画面ロックの文字数の範囲やタイプを示すものです。アプリから一定レベルまで画面ロックを更新するよう推奨されることもありますが、ユーザーは無視したり別の操作を行ったりできます。画面ロックは平文で保存されないため、アプリが正確なパスワードを知ることはありません。" + + + + "生体認証ハードウェアの使用" "生体認証ハードウェアを認証に使用することをアプリに許可します" "指紋ハードウェアの管理" @@ -565,37 +576,59 @@ "使用する顔テンプレートの追加や削除を行うメソッドの呼び出しをアプリに許可します。" "顔認証ハードウェアの使用" "顔認証ハードウェアを認証に使用することをアプリに許可します" - "顔を認識できませんでした。もう一度お試しください。" - "顔が明るすぎます。照明を暗くしてみてください。" - "顔が暗すぎます。光源のカバーを外してください。" - "センサーを顔から遠ざけてください。" - "センサーを顔に近づけてください。" - "センサーを上に動かしてください。" - "センサーを下に動かしてください。" - "センサーを右に動かしてください。" - "センサーを左に動かしてください。" - "センサーを見てください。" - "顔を検出できません。" - "あまり動かさないでください。" + + + + + + + + + + + + + + + + + + + + + + + + "顔を登録し直してください。" - "別の顔が検出されました。" + + "似すぎています。ポーズを変えてください。" - "もっとまっすぐ顔をカメラに向けてください。" - "もっとまっすぐカメラに顔を向けてください。" + + + + "頭を左右に傾けず、まっすぐにしてください。" - "顔を隠さないでください。" + + + + "顔認証ハードウェアが使用できません。" - "読み取りのタイムアウトです。もう一度お試しください。" + + "顔の情報を保存できません。" "顔の操作をキャンセルしました。" "顔の認証がユーザーによりキャンセルされました。" "試行回数の上限です。後でもう一度お試しください。" "試行回数の上限です。顔認証は無効になりました。" - "もう一度お試しください。" - "顔の情報が登録されていません。" - "このデバイスには顔認証センサーがありません。" + + + + + + "顔 %d" @@ -1213,9 +1246,16 @@ "%1$s を開く" "%1$s を保存せずに閉じます" "%1$sはメモリの上限を超えました" + + "ヒープダンプを収集しました。共有するにはタップしてください。" "ヒープダンプを共有しますか?" - "プロセス%1$sはプロセスメモリの上限%2$sを超えました。ヒープダンプをデベロッパーと共有できます。このヒープダンプには、アプリがアクセスできる個人情報が含まれている可能性があるのでご注意ください。" + + + + + + "アプリケーションを選択" "着信音量" "メディアの音量" @@ -1254,8 +1294,10 @@ "すべてのネットワークを表示するにはタップします" "接続" "すべてのネットワーク" - "%s でおすすめの Wi-Fi ネットワークが利用可能です" - "%s でおすすめのネットワークに接続してもよろしいですか?" + + + + "はい" "いいえ" "Wi-Fi は自動的にオンになります" @@ -1267,9 +1309,14 @@ "ネットワークにログインしてください" - "Wi‑Fi はインターネットに接続していません" + + "タップしてその他のオプションを表示" "接続しました" + + + + "アクセス ポイントの設定の変更" "アクセス ポイントの帯域幅が変更されました。" "この端末は 5 GHz のみという設定に対応していません。ただし、5 GHz 周波数帯が利用できるときには利用します。" @@ -1354,6 +1401,10 @@ "USBデバッグが接続されました" "タップして USB デバッグを無効にしてください" "USBデバッグを無効にする場合に選択します。" + + + + "USB ポート内の液体やゴミ" "USB ポートが自動的に無効になりました。タップして詳細をご確認ください。" "USB ポートを安全に使用できます" @@ -1905,8 +1956,6 @@ "タップしてプロファイルをロック解除" "%1$s に接続しました" "タップしてファイルを表示" - "固定" - "固定を解除" "アプリ情報" "−%1$s" "デモを開始しています…" @@ -1997,6 +2046,22 @@ "ルーティン モード情報の通知" "通常の充電を行う前に電池が切れる可能性があります" "電池を長持ちさせるため、バッテリー セーバーが有効になりました" + + + + + + + + + + + + + + + + "フォルダ" "Android アプリ" "ファイル" diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 4a3cb8b4f5ff433eb019f4affe59c53d05afe73e..c6e636d6469f612e2288f17eb4bf9f65469b1acc 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -141,8 +141,10 @@ "WiFi დარეკვა" "VoWifi" "გამორთული" - "სასურველია Wi-Fi" - "უპირატესობა მიენიჭოს მობილურს" + + + + "მხოლოდ Wi-Fi" "{0}: არ არის გადამისამართებული" "{0}: {1}" @@ -228,7 +230,8 @@ "ხარვეზის შესახებ ანგარიში" "სესიის დასრულება" "ეკრანის ანაბეჭდი" - "შექმენით შეცდომის ანგარიში" + + "იგი შეაგროვებს ინფორმაციას თქვენი მოწყობილობის ამჟამინდელი მდგომარეობის შესახებ, რათა ის ელფოსტის შეტყობინების სახით გააგზავნოს. ხარვეზის ანგარიშის მომზადებასა და შეტყობინების გაგზავნას გარკვეული დრო სჭირდება. გთხოვთ, მოითმინოთ." "ინტერაქტიული ანგარიში" "გამოიყენეთ ეს ვარიანტი შემთხვევათა უმეტესობაში. ის საშუალებას მოგცემთ, თვალი მიადევნოთ ანგარიშის პროგრესს, პრობლემის შესახებ მეტი დეტალი შეიყვანოთ და გადაიღოთ ეკრანის ანაბეჭდები. ამ ვარიანტის არჩევის შემთხვევაში, შეიძლება მოხდეს ზოგიერთი ნაკლებად გამოყენებადი სექციის გამოტოვება, რომელთა შესახებ მოხსენებასაც დიდი დრო სჭირდება." @@ -281,9 +284,12 @@ "მდებარეობა" "მოწყობილობის მდებარეობაზე წვდომა" "გსურთ, მიანიჭოთ <b>%1$s-ს</b> ამ მოწყობილობის მდებარეობაზე წვდომის ნებართვა?" - "ამ აპს მდებარეობაზე წვდომა მხოლოდ მაშინ ექნება, როცა თქვენ მას გამოიყენებთ." - "გსურთ, მიანიჭოთ <b>%1$s-ს</b> ამ მოწყობილობის მდებარეობაზე წვდომის ნებართვა?" - "ამ აპს ყოველთვის ექნება მდებარეობაზე წვდომა, მაშინაც კი, როცა თქვენ მას არ იყენებთ." + + + + + + "კალენდარი" "თქვენს კალენდარზე წვდომა" "გსურთ, მიანიჭოთ <b>%1$s-ს</b> თქვენს კალენდარზე წვდომის ნებართვა?" @@ -316,7 +322,10 @@ "გსურთ, მიანიჭოთ <b>%1$s</b>-ს თქვენს მუსიკაზე წვდომა?" "ფოტოები და ვიდეოები" "თქვენს ფოტოებსა და ვიდეოებზე წვდომა" - "გსურთ, მიანიჭოთ <b>%1$s</b>-ს წვდომა თქვენს ფოტოებსა და ვიდეოებზე, მათ შორის, თეგებით მონიშნულ მდებარეობებზე?" + + + + "ფანჯრის კონტენტის მოძიება" "იმ ფანჯრის კონტენტის შემოწმება, რომელშიც მუშაობთ." "„შეხებით აღმოჩენის“ ჩართვა" @@ -509,8 +518,10 @@ "აპს შეეძლება ახლო მოქმედების რადიოკავშირის (NFC) მეშვეობით ტეგების, ბარათებისა და წამკითხველების შემცველი მონაცემების მიმოცვლა." "თქვენი ეკრანის ბლოკის გათიშვა" "შეეძლება კლავიატურის დაბლოკვისა და პაროლით უზრუნველყოფილი ნებისმიერი უსაფრთხოების ფუნქციის დეაქტივაცია. მაგალითად, ტელეფონი შემომავალი ზარის დროს აუქმებს კლავიატურის დაბლოკვას და კვლავ ააქტიურებს მას, როგორც კი ზარი დასრულდება." - "ეკრანის დაბლოკვის მეთოდის სირთულის შესახებ ინფორმაციის მოთხოვნა" - "საშუალებას აძლევს აპს, შეიტყოს ეკრანის დაბლოკვის მეთოდის სირთულე (მაღალი, საშუალო, დაბალი ან არანაირი), რისი მეშვეობითაც შესაძლებელია ეკრანის დაბლოკვის მეთოდის სიგრძის შესაძლო დიაპაზონისა და ტიპის განსაზღვრა. გარდა ამისა, აპს შეუძლია მომხმარებლებისთვის ეკრანის დაბლოკვის მეთოდის გარკვეულ დონემდე გაძლიერების შეთავაზება, თუმცა მომხმარებლებს შეეძლებათ აღნიშნული შეტყობინების უგულებელყოფა და სხვა ეკრანზე გადასვლა. გაითვალისწინეთ, რომ ეკრანის დაბლოკვის მეთოდი არ ინახება ჩვეულებრივი ტექსტის სახით, ამიტომ აპს არ ეცოდინება ზუსტი პაროლი." + + + + "ბიომეტრიული აპარატის გამოყენება" "საშუალებას აძლევს აპს, ავტორიზაციისთვის გამოიყენოს ბიომეტრიული აპარატი" "თითის ანაბეჭდის აპარატის მართვა" @@ -565,37 +576,59 @@ "საშუალებას აძლევს აპს, დაამატოს და წაშალოს სახეების შაბლონები." "სახის ამოცნობის აპარატურის გამოყენება" "საშუალებას აძლევს აპს, ავტორიზაციისთვის გამოიყენოს სახის ამოცნობის აპარატურა" - "სახე ვერ მუშავდება. გთხოვთ, ცადოთ ხელახლა." - "სახე გადანათებულია. დაუკელით განათებას." - "სახე ჩაბნელებულია. მოუმატეთ განათებას." - "დააშორეთ მოწყობილობის სენსორი სახეს." - "მიუახლოვეთ მოწყობილობის სენსორი სახეს." - "ასწიეთ მოწყობილობის სენსორი ოდნავ ზემოთ." - "ჩასწიეთ მოწყობილობის სენსორი ოდნავ ქვემოთ." - "გასწიეთ მოწყობილობის სენსორი ოდნავ მარჯვნივ." - "გასწიეთ მოწყობილობის სენსორი ოდნავ მარცხნივ." - "შეხედეთ სენსორს." - "სახის ამოცნობა ვერ მოხერხდა." - "დაფიქსირდა მეტისმეტად ბევრი მოძრაობა." + + + + + + + + + + + + + + + + + + + + + + + + "გთხოვთ, ხელახლა დაარეგისტრიროთ თქვენი სახე." - "ამოცნობილია განსხვავებული სახე." + + "მეტისმეტად მსგავსია. გთხოვთ, შეცვალოთ პოზა." - "გთხოვთ, უფრო პირდაპირ შეხედოთ კამერას." - "გთხოვთ, უფრო პირდაპირ შეხედოთ კამერას." + + + + "გთხოვთ, ვერტიკალურად გაასწოროთ თავი." - "გთხოვთ, გამოაჩინოს სახე." + + + + "სახის ამოცნობის აპარატურა მიუწვდომელია." - "სახის ამოცნობის დრო ამოიწურა. ცადეთ ხელახლა." + + "სახის შენახვა ვერ მოხერხდა." "სახის ამოცნობა გაუქმდა." "სახის ავტორიზაცია გაუქმდა მომხმარებლის მიერ." "დაფიქსირდა ბევრი მცდელობა. ცადეთ მოგვიანებით." "დაფიქსირდა ბევრი მცდელობა. სახის ამოცნობა გაითიშა." - "ცადეთ ხელახლა." - "სახე რეგისტრირებული არ არის." - "ამ მოწყობილობას არ აქვს სახის ამოცნობის სენსორი." + + + + + + "სახე %d" @@ -1213,9 +1246,16 @@ "გახსენით %1$s" "%1$s დაიხურება მონაცემთა შენახვის გარეშე" "%1$s-მა გადააჭარბა მეხსიერების ლიმიტს" + + "გროვის ამონაწერი მომზადდა, შეეხეთ გასაზიარებლად." "გავაზიაროთ გროვის ამონაწერი?" - "პროცესმა %1$s გადააჭარბა საპროცესო მეხსიერების %2$s-იან ლიმიტს. გროვის ამონაწერი ხელმისაწვდომია მის დეველოპერთან გასაზიარებლად. ფრთხილად: გროვის ამონაწერი შეიძლება შეიცავდეს ნებისმიერ თქვენს პირად ინფორმაციას, რომელზეც ამ აპლიკაციას წვდომა აქვს." + + + + + + "შეარჩიეთ ქმედება ტექსტისთვის." "მრეკავის ხმა" "მედიის ხმა" @@ -1254,8 +1294,10 @@ "შეეხეთ ყველა ქსელის სანახავად" "დაკავშირება" "ყველა ქსელი" - "ხელმისაწვდომია %s-ის მიერ შემოთავაზებული Wi‑Fi ქსელი" - "გსურთ %s-ის მიერ შემოთავაზებულ ქსელებთან დაკავშირება?" + + + + "დიახ" "არა" "Wi-Fi ავტომატურად ჩაირთვება" @@ -1267,9 +1309,14 @@ "ქსელში შესვლა" - "Wi‑Fi ქსელს ინტერნეტზე წვდომა არ აქვს" + + "შეეხეთ ვარიანტების სანახავად" "დაკავშირებულია" + + + + "თქვენი უსადენო ქსელის პარამეტრების ცვლილება" "თქვენი უსადენო ქსელის დიაპაზონი შეიცვალა." "ამ მოწყობილობას არ შეუძლია მხოლოდ 5 გჰც სიხშირეზე მუშაობა. აღნიშნული სიხშირის გამოყენება მოხდება მაშინ, როცა ეს შესაძლებელია." @@ -1354,6 +1401,10 @@ "USB გამართვა შეერთებულია" "შეეხეთ, რათა გამორთოთ USB შეცდომების გამართვა" "მონიშნეთ რათა შეწყვიტოთ USB-ის გამართვა" + + + + "USB პორტში აღმოჩენილია სითხე ან ჭუჭყი" "USB პორტი ავტომატურად გათიშულია. შეეხეთ დამატებითი ინფორმაციისთვის." "USB პორტის გამოყენება ახლა უსაფრთხოა" @@ -1396,8 +1447,7 @@ "დაყენება" "გამოღება" "დათვალიერება" - - + "გამომავალი სიგნალის გადართვა" "%s აკლია" "ისევ მიუერთეთ მოწყობილობა" "მიმდინარეობს %s-ის გადატანა" @@ -1906,8 +1956,6 @@ "შეეხეთ პროფილის განსაბლოკად" "დაკავშირებულია %1$s-თან" "შეეხეთ ფაილების სანახავად" - "ჩამაგრება" - "ჩამაგრების მოხსნა" "აპის შესახებ" "−%1$s" "მიმდინარეობს დემონსტრაციის დაწყება…" @@ -1998,6 +2046,22 @@ "რუტინის რეჟიმის საინფორმაციო შეტყობინება" "ბატარეა შეიძლება დაჯდეს დატენის ჩვეულ დრომდე" "ბატარეის დამზოგი გააქტიურდა ბატარეის მუშაობის გასახანგრძლივლებლად" + + + + + + + + + + + + + + + + "საქაღალდე" "Android-ის აპლიკაცია" "ფაილი" diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index cb38ea13b9ab64b667b1c1c9aaa66c846394b0af..494f26a3bf7cd9f0317432be0bd1762f8e30441b 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -141,8 +141,10 @@ "ការហៅតាម Wi-Fi" "VoWifi" "បិទ" - "Wi-Fi ជាអាទិភាព" - "ទិន្នន័យទូរសព្ទចល័តជាអាទិភាព" + + + + "Wi-Fi តែប៉ុណ្ណោះ" "{0} ៖ មិន​បាន​បញ្ជូន​បន្ត" "{0}: {1}" @@ -228,7 +230,8 @@ "របាយការណ៍​កំហុស" "បញ្ចប់​សម័យ" "រូបថតអេក្រង់" - "យក​របាយការណ៍​កំហុស" + + "វា​នឹង​​ប្រមូល​ព័ត៌មាន​អំពី​ស្ថានភាព​ឧបករណ៍​របស់​អ្នក ដើម្បី​ផ្ញើ​ជា​សារ​អ៊ីមែល។ វា​នឹង​ចំណាយ​ពេល​តិច​ពី​ពេល​ចាប់ផ្ដើម​របាយការណ៍​រហូត​ដល់​ពេល​វា​រួចរាល់​ដើម្បី​ផ្ញើ សូម​អត់ធ្មត់។" "របាយការណ៍អន្តរកម្ម" "ប្រើក្នុងកាលៈទេសៈភាគច្រើន។ វាអនុញ្ញាតឲ្យអ្នកតាមដានដំណើរការនៃរបាយការណ៍ និងបញ្ចូលព័ត៌មានលម្អិតបន្ថែមអំពីបញ្ហា និងថតរូបអេក្រង់។ វាអាចនឹងរំលងផ្នែកមួយចំនួនដែលមិនសូវប្រើ ដែលធ្វើឲ្យចំណាយពេលយូរក្នុងការរាយការណ៍។" @@ -281,9 +284,12 @@ "ទីតាំង" "ចូលដំណើរការទីតាំងរបស់ឧបករណ៍នេះ" "អនុញ្ញាតឱ្យ <b>%1$s</b> ចូលប្រើ​ទីតាំងរបស់ឧបករណ៍នេះ?" - "កម្មវិធីនេះ​នឹងមាន​សិទ្ធិ​ចូលប្រើ​ទីតាំង នៅពេល​អ្នកប្រើ​កម្មវិធីនេះ​តែ​ប៉ុណ្ណោះ។" - "អនុញ្ញាត​ឱ្យ <b>%1$s</b> ចូលប្រើ​ទីតាំង​របស់ឧបករណ៍​នេះ​ជានិច្ច​មែនទេ?" - "កម្មវិធីនេះ​នឹងមាន​សិទ្ធិ​ចូលប្រើ​ទីតាំង​ជានិច្ច ទោះបីជា​នៅពេល​អ្នក​មិនប្រើ​កម្មវិធីនេះ​ក៏​ដោយ។" + + + + + + "ប្រតិទិន" "ចូលប្រើប្រិតិទិនរបស់អ្នក" "អនុញ្ញាតឱ្យ <b>%1$s</b> ចូលប្រើ​ប្រតិទិនរបស់អ្នក?" @@ -316,7 +322,10 @@ "អនុញ្ញាត​ឱ្យ <b>%1$s</b> ចូលប្រើ​តន្រ្តី​របស់​អ្នក?" "​រូបថត និង​វីដេអូ" "ចូលប្រើ​រូបថត និង​វីដេអូ​របស់អ្នក" - "អនុញ្ញាត​ឱ្យ <b>%1$s</b> ចូលប្រើ​រូបថត និង​វីដេអូ​របស់អ្នក រួមទាំង​ទីតាំង​ដែលបានដាក់ស្លាក?" + + + + "ទាញយក​ខ្លឹមសារ​វិនដូ" "ពិនិត្យ​ខ្លឹមសារវិនដូ​ដែល​អ្នក​កំពុង​ទាក់ទង​ជា​មួយ។" "បើក​ការ​រក​មើល​​ដោយ​ប៉ះ" @@ -509,8 +518,10 @@ "ឲ្យ​កម្មវិធី​ទាក់ទង​ជា​មួយ​ស្លាក (NFC) កាត និង​កម្មវិធី​អាន។" "បិទ​ការ​ចាក់​សោ​អេក្រង់​របស់​អ្នក" "ឲ្យ​កម្មវិធី​បិទ​ការ​ចាក់សោ​សុវត្ថិភាព​ពាក្យ​សម្ងាត់​ដែល​បាន​ភ្ជាប់​ណា​មួយ។ ​ឧទាហរណ៍​ត្រឹមត្រូវ​​​នៃ​ការ​បិទ​ទូរស័ព្ទ​ពេល​ទទួលការ​ហៅ​ចូល បន្ទាប់​ម​បើក​សោ​ពេល​ការ​ហៅ​បាន​បញ្ចប់។" - "ស្នើ​សុំកម្រិត​​ស្មុគស្មាញ​នៃការចាក់សោអេក្រង់" - "អនុញ្ញាតឱ្យ​កម្មវិធី​រៀនអំពី​កម្រិតស្មុគស្មាញ​នៃការចាក់សោអេក្រង់ (ខ្ពស់ មធ្យម​ ទាប ឬគ្មាន) ដែល​បញ្ជាក់អំពី​​ចន្លោះប្រវែងដែលអាចមាន និងប្រភេទ​នៃការចាក់សោអេក្រង់។ កម្មវិធី​នេះ​ក៏​អាច​ណែនាំឱ្យ​អ្នកប្រើប្រាស់​ធ្វើបច្ចុប្បន្នភាព​ការចាក់សោ​អេក្រង់​ទៅកម្រិតជាក់លាក់​ផងដែរ ប៉ុន្តែ​អ្នកប្រើប្រាស់​អាច​មិនអើពើនឹង​ការណែនាំនេះ​ដោយសេរី។ សូម​ចំណាំថា ការចាក់សោអេក្រង់​មិន​ត្រូវបាន​រក្សាទុក​ជាអត្ថបទ​ធម្មតាទេ ដូច្នេះ​កម្មវិធីនេះ​មិន​ស្គាល់​ពាក្យសម្ងាត់​ពិតប្រាកដ​ឡើយ។" + + + + "ប្រើ​ឧបករណ៍​ស្កេន​ស្នាមម្រាមដៃ" "អនុញ្ញាត​ឱ្យ​កម្មវិធី​ប្រើ​ឧបករណ៍​ស្កេន​ស្នាមម្រាមដៃ​សម្រាប់​ការផ្ទៀងផ្ទាត់" "គ្រប់គ្រងផ្នែករឹងស្នាមម្រាមដៃ" @@ -565,37 +576,59 @@ "អនុញ្ញាតឱ្យកម្មវិធីប្រើវិធីសាស្ត្រដើម្បី​បញ្ចូល និងលុបទម្រង់​គំរូ​ផ្ទៃមុខសម្រាប់ប្រើប្រាស់។" "ប្រើ​ផ្នែករឹង​ផ្ទៀងផ្ទាត់​ផ្ទៃ​មុខ" "អនុញ្ញាត​ឱ្យ​កម្មវិធី​ប្រើ​ផ្នែករឹង​ផ្ទៀងផ្ទាត់​ផ្ទៃមុខ​សម្រាប់​ការផ្ទៀងផ្ទាត់" - "មិនអាចដំណើរការ​ផ្ទៃមុខបានទេ។ សូមព្យាយាមម្តងទៀត។" - "ផ្ទៃ​មុខ​ចាំង​ពេក។ សូម​សាកល្បង​នៅកន្លែងដែលមាន​ពន្លឺ​ទាប​ជាងនេះ។" - "ផ្ទៃ​មុខ​ងងឹត​ពេក។ សូមរកកន្លែង​ដែលមាន​ប្រភពពន្លឺ។" - "សូម​ផ្លាស់ទីឧបករណ៍​ចាប់សញ្ញា​ឱ្យ​ឆ្ងាយ​ពី​មុខ​។" - "សូម​ដាក់​ឧបករណ៍​ចាប់សញ្ញា​ឱ្យនៅជិតមុខ​ជាងនេះ។" - "សូម​ផ្លាស់ទី​ឧបករណ៍​ចាប់សញ្ញា​ឱ្យខ្ពស់​ជាងនេះ។" - "សូម​ផ្លាស់ទី​ឧបករណ៍ចាប់សញ្ញា​ឱ្យ​ទាបជាងនេះ។" - "សូម​ផ្លាស់ទី​ឧបករណ៍ចាប់សញ្ញា​ទៅ​ស្ដាំ។" - "សូម​ផ្លាស់ទី​ឧបករណ៍ចាប់សញ្ញា​ទៅឆ្វេង។" - "សូម​មើល​ទៅ​ឧបករណ៍​ចាប់សញ្ញា។" - "រកមិន​ឃើញ​មុខទេ។" - "មាន​ចលនា​ខ្លាំងពេក។" + + + + + + + + + + + + + + + + + + + + + + + + "សូម​​ស្កេន​បញ្ចូល​មុខរបស់អ្នក​ម្ដងទៀត។" - "បាន​រកឃើញ​មុខផ្សេង។" + + "ស្រដៀងគ្នា​ពេក សូមផ្លាស់ប្ដូរ​កាយវិការ​របស់អ្នក។" - "សូម​មើល​ឱ្យចំកាមេរ៉ា​ជាងមុន។" - "សូម​មើល​ឱ្យចំកាមេរ៉ា​ជាងមុន។" + + + + "សូម​ងើយ​ក្បាល​របស់អ្នកឱ្យត្រង់។" - "សូម​កុំបាំងមុខ​របស់អ្នក។" + + + + "មិន​អាច​ប្រើ​ផ្នែករឹង​ចាប់ផ្ទៃ​មុខ​បានទេ។" - "ការសម្គាល់​ផ្ទៃមុខ​បាន​អស់ម៉ោង។ សូមព្យាយាមម្ដងទៀត។" + + "មិន​អាច​រក្សាទុក​ផ្ទៃ​មុខ​បានទេ។" "បាន​បោះបង់​ប្រតិបត្តិការចាប់​ផ្ទៃមុខ។" "ការផ្ទៀងផ្ទាត់​មុខ​ត្រូវបានបោះបង់ដោយអ្នកប្រើប្រាស់។" "ព្យាយាមចូលច្រើនពេកហើយ។ សូមព្យាយាមម្តងទៀតពេលក្រោយ។" "ព្យាយាមចូលច្រើនពេកហើយ។ បាន​បិទការផ្ទៀងផ្ទាត់​ផ្ទៃ​មុខ។" - "សូមព្យាយាម​ម្ដងទៀត។" - "មិន​អាច​ថត​បញ្ចូលផ្ទៃ​មុខ​បានទេ។" - "ឧបករណ៍​នេះ​មិន​មាន​ឧបករណ៍​ផ្ទៀងផ្ទាត់​មុខ​ទេ។" + + + + + + "ផ្ទៃមុខទី %d" @@ -1215,9 +1248,16 @@ "បើក %1$s" "%1$s នឹង​បិទ​ដោយ​មិន​រក្សាទុក" "%1$s លើសពីកម្រិតកំណត់មេម៉ូរី" + + "ព័ត៌មានកែបញ្ហាត្រូវបាន​ប្រមូល។ សូមចុចដើម្បីចែករំលែក។" "ចែករំលែក heap dump?" - "ដំណើរការ %1$s បានលើសកម្រិតកំណត់មេម៉ូរីរបស់វាដែលមានទំហំ %2$s។ Heap dump មានផ្តល់ជូនដល់អ្នកដើម្បីចែករំលែកជាមួយអ្នកអភិវឌ្ឍន៍របស់វា។ ត្រូវប្រុងប្រយ័ត្ន៖ Heap dump នេះអាចផ្ទុកព័ត៌មានផ្ទាល់ខ្លួនរបស់អ្នកណាមួយ ដែលកម្មវិធីអាចចូលប្រើបាន។" + + + + + + "ជ្រើស​សកម្មភាព​សម្រាប់​អត្ថបទ" "កម្រិត​សំឡេង​រោទ៍" "កម្រិត​សំឡេង​មេឌៀ" @@ -1256,8 +1296,10 @@ "ចុចដើម្បីមើលបណ្តាញទាំងអស់" "ភ្ជាប់" "បណ្ដាញ​ទាំងអស់" - "អាចប្រើ​បណ្ដាញ Wi‑Fi ដែល​ណែនាំ​ដោយ %s បាន" - "តើអ្នក​ចង់ភ្ជាប់​ទៅកាន់​បណ្ដាញ​ដែល​ណែនាំ​ដោយ %s ដែរទេ?" + + + + "បាទ/ចាស" "ទេ" "Wi‑Fi នឹង​បើក​ដោយ​ស្វ័យប្រវត្តិ" @@ -1269,9 +1311,14 @@ "ចូលទៅបណ្តាញ" - "Wi-Fi មិនមាន​ការតភ្ជាប់​អ៊ីនធឺណិតទេ" + + "ប៉ះសម្រាប់ជម្រើស" "បានភ្ជាប់" + + + + "ប្ដូរ​ទៅ​ការ​កំណត់​ហតស្ប៉ត​របស់អ្នក" "រលកសញ្ញាហតស្ប៉តរបស់​អ្នកបាន​ផ្លាស់ប្ដូរ។" "ឧបករណ៍​នេះ​មិន​អាច​ប្រើចំណូល​ចិត្ត​របស់អ្នកសម្រាប់តែ 5GHz ទេ។ ផ្ទុយ​មកវិញ ឧបករណ៍​នេះ​នឹង​ប្រើរលកសញ្ញា​ 5GHz នៅពេល​ដែលអាច​ប្រើបាន។" @@ -1356,6 +1403,10 @@ "បាន​ភ្ជាប់​ការ​កែ​កំហុសតាម​ USB" "ចុច​ដើម្បី​បិទ​ការកែកំហុសតាម ​USB" "ជ្រើស​រើស ដើម្បី​បិទ​ការ​កែ​កំហុសតាម USB ។" + + + + "មានទឹក ឬ​កម្ទេចផ្សេងៗ​នៅក្នុងរន្ធ USB" "រន្ធ USB ត្រូវបាន​បិទ​ដោយ​ស្វ័យប្រវត្តិ។ សូមចុច​ដើម្បី​ស្វែងយល់​បន្ថែម។" "អាច​ប្រើរន្ធ USB បានដោយ​សុវត្ថិភាព" @@ -1907,8 +1958,6 @@ "ប៉ះដើម្បីដោះសោប្រវត្តិរូបការងារ" "បានភ្ជាប់ទៅ %1$s" "ប៉ះដើម្បីមើលឯកសារ" - "ខ្ទាស់" - "មិនខ្ទាស់" "ព័ត៌មាន​កម្មវិធី" "−%1$s" "កំពុងចាប់ផ្តើមការបង្ហាញសាកល្បង…" @@ -1999,6 +2048,22 @@ "ការ​ជូនដំណឹង​ព័ត៌មាន​របស់​មុខងារ​ទម្លាប់" "ថ្ម​អាច​នឹង​អស់ មុនពេល​សាកថ្មធម្មតា" "បាន​បើក​ដំណើរការកម្មវិធី​សន្សំ​ថ្ម ដើម្បីបង្កើនកម្រិត​ថាមពល​​ថ្ម" + + + + + + + + + + + + + + + + "ថត" "​កម្មវិធី Android" "ឯកសារ" diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 69963ef0548fcbacb9f6b27ab5d36a4295371027..1409e33a9f3839f085dfe07d7e0c2ca3cda5816b 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -141,8 +141,10 @@ "Wi-Fi 통화" "VoWifi" "꺼짐" - "Wi-Fi를 기본으로 설정" - "모바일에 최적화됨" + + + + "Wi-Fi에서만" "{0}: 착신전환 안됨" "{0}: {1}" @@ -228,7 +230,8 @@ "버그 신고" "세션 끝내기" "스크린샷" - "버그 신고" + + "현재 기기 상태에 대한 정보를 수집하여 이메일 메시지로 전송합니다. 버그 신고를 시작하여 전송할 준비가 되려면 약간 시간이 걸립니다." "대화형 보고서" "대부분의 경우 이 옵션을 사용합니다. 신고 진행 상황을 추적하고 문제에 대한 세부정보를 입력하고 스크린샷을 찍을 수 있습니다. 신고하기에 시간이 너무 오래 걸리고 사용 빈도가 낮은 일부 섹션을 생략할 수 있습니다." @@ -281,9 +284,12 @@ "위치" "이 기기의 위치정보에 액세스" "<b>%1$s</b>에서 내 기기 위치에 액세스하도록 허용하시겠습니까?" - "앱을 사용할 때만 앱에서 위치에 액세스합니다." - "<b>%1$s</b>에서 내 기기 위치에 액세스하도록 허용하시겠습니까?" - "앱을 사용하지 않을 때에도 앱에서 항상 위치에 액세스합니다." + + + + + + "캘린더" "캘린더에 액세스" "<b>%1$s</b>에서 내 캘린더에 액세스하도록 허용하시겠습니까?" @@ -316,7 +322,10 @@ "<b>%1$s</b>에서 내 음악에 액세스하도록 허용하시겠습니까?" "사진 및 동영상" "사진 및 동영상에 액세스" - "<b>%1$s</b>에서 태그가 지정된 위치를 포함한 내 사진과 동영상에 액세스하도록 허용하시겠습니까?" + + + + "창 콘텐츠 가져오기" "상호작용 중인 창의 콘텐츠를 검사합니다." "터치하여 탐색 사용" @@ -509,8 +518,10 @@ "앱이 NFC(근거리 무선 통신) 태그, 카드 및 리더와 통신할 수 있도록 허용합니다." "화면 잠금 사용 중지" "앱이 키 잠금 및 관련 비밀번호 보안을 사용중지할 수 있도록 허용합니다. 예를 들어, 휴대전화가 수신전화를 받을 때 키 잠금을 사용중지했다가 통화가 끝나면 키 잠금을 다시 사용할 수 있습니다." - "화면 잠금 복잡도 요청" - "앱이 화면 잠금 길이와 유형의 가능한 범위를 나타내는 잠금 화면 복잡도 수준(높음, 보통, 낮음 또는 없음)을 파악하도록 허용합니다. 앱이 사용자에게 화면 잠금을 특정 수준으로 업데이트할 것을 제안할 수도 있지만, 사용자는 자유롭게 이를 무시하고 다른 곳으로 이동할 수 있습니다. 화면 잠금은 일반 텍스트로 저장되지 않으므로 앱에서 정확한 비밀번호를 알 수 없습니다." + + + + "생체 인식 하드웨어 사용" "앱에서 생체 인식 하드웨어를 인증에 사용하도록 허용합니다." "지문 하드웨어 관리" @@ -565,37 +576,59 @@ "사용할 얼굴 템플릿의 추가 및 삭제 메서드를 앱에서 호출하도록 허용합니다." "얼굴 인증 하드웨어 사용" "앱에서 얼굴 인증 하드웨어를 인증에 사용하도록 허용합니다." - "얼굴을 인식할 수 없습니다. 다시 시도해 주세요." - "얼굴이 너무 밝습니다. 조명을 더 어둡게 해 보세요." - "얼굴이 너무 어둡습니다. 조명을 더 밝게 해 보세요." - "센서를 얼굴에서 더 멀리 떨어뜨려 주세요." - "센서를 얼굴에 더 가까이 대 주세요." - "센서를 위쪽으로 옮겨 주세요." - "센서를 아래쪽으로 옮겨 주세요." - "센서를 오른쪽으로 옮겨 주세요." - "센서를 왼쪽으로 옮겨 주세요." - "센서를 바라보세요." - "얼굴을 감지할 수 없습니다." - "너무 많이 움직였습니다." + + + + + + + + + + + + + + + + + + + + + + + + "얼굴을 다시 등록해 주세요." - "다른 얼굴이 감지되었습니다." + + "너무 비슷합니다. 다른 포즈를 취해 보세요." - "카메라를 더 똑바로 바라보세요." - "카메라를 더 똑바로 바라보세요." + + + + "고개를 똑바로 세워 주세요." - "얼굴이 보이게 해 주세요." + + + + "얼굴 인식 하드웨어를 사용할 수 없습니다." - "얼굴 인식 시간이 초과되었습니다. 다시 시도하세요." + + "얼굴을 저장할 수 없습니다." "얼굴 인식 작업이 취소되었습니다." "사용자가 얼굴 인증을 취소했습니다." "시도 횟수가 너무 많습니다. 나중에 다시 시도하세요." "시도 횟수가 너무 많아 얼굴 인증이 사용 중지되었습니다." - "다시 시도해 보세요." - "등록된 얼굴이 없습니다." - "이 기기에는 얼굴 인증 센서가 없습니다." + + + + + + "얼굴 %d" @@ -1213,9 +1246,16 @@ "%1$s 열기" "%1$s이(가) 저장되지 않고 종료됩니다." "%1$s에서 메모리 제한을 초과했습니다." + + "힙 덤프가 수집되었습니다. 공유하려면 탭하세요." "힙 덤프를 공유할까요?" - "프로세스 %1$s에서 프로세스 메모리 한도(%2$s)를 초과했습니다. 힙 덤프를 개발자와 공유할 수 있습니다. 주의: 애플리케이션이 액세스할 수 있는 개인 정보가 이 힙 덤프에 포함되어 있을 수 있습니다." + + + + + + "텍스트에 대한 작업 선택" "벨소리 볼륨" "미디어 볼륨" @@ -1254,8 +1294,10 @@ "모든 네트워크를 보려면 탭하세요." "연결" "모든 네트워크" - "%s에서 제안한 Wi‑Fi 네트워크를 사용할 수 있습니다" - "%s이(가) 제안한 네트워크에 연결하시겠습니까?" + + + + "예" "아니요" "Wi‑Fi가 자동으로 사용 설정됨" @@ -1267,9 +1309,14 @@ "네트워크에 로그인" - "Wi-Fi가 인터넷에 연결되어 있지 않습니다" + + "탭하여 옵션 보기" "연결되었습니다." + + + + "핫스팟 설정 변경" "핫스팟 대역이 변경되었습니다." "이 기기에서는 5GHz 전용 환경설정이 지원되지 않습니다. 대신 가능할 때만 기기에서 5GHz 대역이 사용됩니다." @@ -1354,6 +1401,10 @@ "USB 디버깅 연결됨" "USB 디버깅을 사용 중지하려면 탭하세요." "USB 디버깅을 사용하지 않으려면 선택합니다." + + + + "USB 포트에서 액체 또는 이물질 감지됨" "USB 포트가 자동으로 사용 중지되었습니다. 자세한 내용을 보려면 탭하세요." "USB 포트를 사용해도 좋음" @@ -1396,8 +1447,7 @@ "설정" "마운트 해제" "둘러보기" - - + "출력 전환" "%s 없음" "기기를 다시 삽입하세요." "%s 이동 중" @@ -1906,8 +1956,6 @@ "탭하여 직장 프로필 잠금 해제" "%1$s에 연결됨" "파일을 확인하려면 탭하세요." - "고정" - "고정 해제" "앱 정보" "−%1$s" "데모 시작 중..." @@ -1998,6 +2046,22 @@ "루틴 모드 정보 알림" "평소에 충전하는 시간 전에 배터리가 소진될 수 있습니다." "배터리 수명을 연장하기 위해 배터리 세이버가 활성화되었습니다." + + + + + + + + + + + + + + + + "폴더" "Android 애플리케이션" "파일" diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 95e85c68d2205c52b4f29be83f6230c262b23860..b66ec15830e19a5713731d8a53b0c7fc34e5541e 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -141,8 +141,10 @@ "Wi-Fi аркылуу чалынууда" "VoWifi" "Өчүк" - "Wi-Fi тандалган" - "Тандалган мобилдик түзмөк" + + + + "Wi-Fi гана" "{0}: Багытталган эмес" "{0}: {1}" @@ -228,7 +230,8 @@ "Ката тууралуу билдирүү" "Сеансты бүтүрүү" "Скриншот" - "Ката тууралуу билдирүү түзүү" + + "Ушуну менен түзмөгүңүздүн учурдагы абалы тууралуу маалымат топтолуп, электрондук почта аркылуу жөнөтүлөт. Отчет даяр болгуча бир аз күтө туруңуз." "Интерактивдүү кабар" "Ката жөнүндө кабардын абалын жана көйгөй тууралуу кошумча маалыматты көрсөтүү үчүн ушул функцияны колдонууну сунуштайбыз. Ката жөнүндө кабар жөнөтүлүп жатканда көп убакыт талап кылынбашы үчүн негизги бөлүмдөр гана көрүнөт." @@ -281,9 +284,12 @@ "Жайгашкан жер" "түзмөктүн жайгашкан жерин аныктоого" "<b>%1$s</b> колдонмосу бул түзмөктүн кайда жүргөнүн көрүп турганга уруксат бересизби?" - "Сиз бул колдонмону пайдаланып жатканда гана ал жайгашкан жериңизди көрө алат." - "<b>%1$s</b> колдонмосуна бул түзмөктүн жайгашкан жерин пайдаланууга дайыма уруксат берилсинби?" - "Сиз бул колдонмону пайдаланбай турганда да ал жайгашкан жериңизди көрүп турат." + + + + + + "Жылнаама" "жылнаамаңызды пайдалануу" "<b>%1$s</b> колдонмосуна жылнаамаңызды пайдаланууга уруксат берилсинби?" @@ -316,7 +322,10 @@ "<b>%1$s</b> колдонмосуна музыканы пайдаланууга уруксат берилсинби?" "Сүрөттөр жана видеолор" "сүрөттөр менен видеолорго кирүү мүмкүнчүлүгү" - "<b>%1$s</b> колдонмосуна сүрөттөрүңүздү, видеолоруңузду, анын ичинде тегделген жайгашкан жерлериңизди көрүүгө уруксат бересизби?" + + + + "Терезедеги мазмунду алып турат" "Учурда ачылып турган терезедеги маалыматты талдайт." "\"Сыйпалап изилдөө\" мүмкүнчүлүгүн иштетет" @@ -509,8 +518,10 @@ "Колдонмого Жакынкы аралыкта байланышуу (NFC) белгилери, карталары жана окугучтары менен байланышуу мүмкүнчүлүгүн берет." "экранды бөгөттөөнү өчүрүү" "Колдонмого экрандын бөгөттөөчү жана ага байланыштуу сырсөз коргоосун өчүрүү уруксатын берет. Мисалы, чалуу келгенде экрандын бөгөтүн алып салат, чалуу бүткөндө кайрадан орнотот." - "экранды бөгөттөөнүн татаалдык деңгээлин суроо" - "Колдонмого экранды бөгөттөөнүн татаалдыгын (татаал, орточо, оңой же такыр жок) үйрөнүүгө мүмкүнчүлүк берет. Татаалдык деңгээли сырсөздүн узундугу жана экранды бөгөттөөнүн түрү боюнча айырмаланат. Колдонмо экранды бөгөттөөнү белгилүү деңгээлге тууралоону колдонуучуларга сунуштай да алат, бирок колдонуучулар ага көңүл бурбай койсо болот. Сырсөздү колдонмо билбеши үчүн, экранды бөгөттөө сырсөзүн кадимки текстте сактоого болбойт." + + + + "биометрикалык аппаратты колдонуу" "Колдонмого аныктыгын текшерүү үчүн биометрикалык аппаратты пайдалануу мүмкүндүгүн берет" "манжа изинин аппараттык камсыздоосун башкаруу" @@ -565,37 +576,59 @@ "Колдонмого пайдалануу үчүн жүздүн үлгүлөрүн кошуу жана жок кылуу мүмкүндүгүн берет." "жүздүн аныктыгын текшерүүчү аппараттык камсыздоону колдонуу" "Колдонмого аныктыгын текшерүү үчүн жүздүн аныктыгын текшерүүчү аппараттык камсыздоону пайдалануу мүмкүндүгүн берет" - "Жүзүңүз таанылбай койду. Кайра аракет кылыңыз." - "Өтө жарык болуп жатат. Жаракты азайтып көрүңүз." - "Өтө караңгы болуп жатат. Жарыкты ачып коюңуз." - "Сенсорду бетиңизден алысыраак жылдырыңыз." - "Сенсорду бетиңизге жакыныраак жылдырыңыз." - "Сенсорду жогору жакка жылдырыңыз." - "Сенсорду төмөн жакка жылдырыңыз." - "Сенсорду оң жакка жылдырыңыз." - "Сенсорду сол жакка жылдырыңыз." - "Сенсорду караңыз." - "Жүзүңүз табылган жок." - "Кыймылдап жибердиңиз." + + + + + + + + + + + + + + + + + + + + + + + + "Жүзүңүздү кайра таанытыңыз." - "Башка жүз аныкталды." + + "Мурункуга окшош болуп калды, башкача туруңуз." - "Камерага түз караңыз." - "Камерага түз караңыз." + + + + "Башыңызды түз кармаңыз." - "Жүзүңүздү ачыңыз." + + + + "Жүздү аныктоочу аппараттык камсыздоо жеткиликсиз." - "Жүздүн аныктыгын текшерүүнү күтүү мөөнөтү бүттү. Кайра аракет кылыңыз." + + "Жүздү сактоо мүмкүн эмес." "Жүздүн аныктыгын текшерүү жокко чыгарылды." "Жүздүн аныктыгын текшерүү колдонуучу аркылуу жокко чыгарылды." "Өтө көп жолу аракет жасадыңыз. Кийинчерээк кайра аракет кылыңыз." "Өтө көп жолу аракет жасадыңыз. Жүздүн аныктыгын текшерүү сенсору өчүрүлдү." - "Кайра аракет кылыңыз." - "Бир да жүз катталган жок." - "Бул түзмөктө жүздүн аныктыгын текшерүү сенсору жок." + + + + + + "Жүз %d" @@ -1213,9 +1246,16 @@ "%1$s колдонмосун ачуу" "%1$s сакталбастан жабылат" "%1$s эстутум чегинен ашып кетти" + + "Үймө дамп топтолду. Бөлүшүү үчүн таптап коюңуз." "Үймө дамп бөлүшүлсүнбү?" - "%1$s процесси өзүнүн %2$s процесс чегинен ашып кетти. Үймө дамп сиз үчүн иштеп чыгуучу менен бөлүшүүгө даяр. Абайлаңыз: бул үймө дампта колдонмонун уруксаты бар жеке маалыматыңыз камтылышы мүмкүн." + + + + + + "Текст үчүн аракет тандаңыз" "Коңгуроонун үн көлөмү" "Мультимедианын үнү" @@ -1254,8 +1294,10 @@ "Бардык тармактарды көрүү үчүн басыңыз" "Туташуу" "Бардык тармактар" - "%s сунуштаган Wi‑Fi тармагы жеткиликтүү" - "%s сунуштаган тармактарга туташкыңыз келеби?" + + + + "Ооба" "Жок" "Wi‑Fi автоматтык түрдө күйөт" @@ -1267,9 +1309,14 @@ "Тармакка кирүү" - "Wi-Fi тармагы Интернетке туташпай турат" + + "Параметрлерди ачуу үчүн таптап коюңуз" "Туташты" + + + + "Туташуу түйүнүңүздүн жөндөөлөрүнө өзгөртүүлөр киргизилди" "Туташуу түйүнүңүздүн жыштыгы өзгөрдү." "Бул түзмөк 5ГГцти гана колдонуу жөндөөсүн колдоого албайт. Анын ордуна, бул түзмөк 5ГГц жыштыгын ал жеткиликтүү болгондо колдонот." @@ -1356,6 +1403,10 @@ "USB аркылуу мүчүлүштүктөрдү оңдоону өчүрүү үчүн таптаңыз" + + + + "USB портунда суюктук же урандылар бар" "USB порт автоматтык түрдө өчүрүлдү. Кененирээк маалымат алуу үчүн таптап коюңуз." "USB портун колдонууга болот" @@ -1907,8 +1958,6 @@ "Таптап жумуш профилин ачыңыз" "%1$s менен туташты" "Файлдарды көрүү үчүн таптап коюңуз" - "Кадоо" - "Кадоодон алып коюу" "Колдонмо тууралуу" "−%1$s" "Демо режим башталууда…" @@ -1999,6 +2048,22 @@ "Режимдин адаттагы билдирмеси" "Батарея кубаттоого чейин отуруп калышы мүмкүн" "Батареянын отуруп калбашы үчүн Батареяны үнөмдөгүч режими иштетилди" + + + + + + + + + + + + + + + + "Папка" "Android колдонмосу" "Файл" diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 1e76147ccc0b65c11e616028868103faad7563e9..84c4000a3139608f05bd252a3c5060e81ea3e58b 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -141,8 +141,10 @@ "ການໂທ Wi-Fi" "VoWifi" "ປິດ" - "ເລືອກໃຊ້ Wi​-Fi ກ່ອນ" - "ຕ້ອງການໃຊ້ມືຖື" + + + + "Wi​-Fi ເທົ່າ​ນັ້ນ" "{0}: ບໍ່ຖືກສົ່ງຕໍ່" "{0}: {1}" @@ -228,7 +230,8 @@ "ລາຍງານຂໍ້ຜິດພາດ" "ສິ້ນສຸດເຊດຊັນ" "ພາບໜ້າຈໍ" - "ໃຊ້ລາຍງານຂໍ້ບົກພ່ອງ" + + "ນີ້ຈະເປັນການເກັບກຳຂໍ້ມູນກ່ຽວກັບ ສະຖານະປັດຈຸບັນຂອງອຸປະກອນທ່ານ ເພື່ອສົ່ງເປັນຂໍ້ຄວາມທາງອີເມວ. ມັນຈະໃຊ້ເວລາໜ້ອຍນຶ່ງ ໃນການເລີ່ມຕົ້ນການລາຍງານຂໍ້ຜິດພາດ ຈົນກວ່າຈະພ້ອມທີ່ຈະສົ່ງໄດ້, ກະລຸນາລໍຖ້າ." "ລາຍງານແບບໂຕ້ຕອບ" "ໃຊ້ພາຍໃຕ້ສະຖານະການສ່ວນໃຫຍ່. ມັນອະນຸຍາດໃຫ້ທ່ານສາມາດຕິດຕາມສະຖານະລາຍງານ, ປ້ອນລາຍລະອຽດເພີ່ມເຕີມກ່ຽວກັບບັນຫາ ແລະ ຖ່າຍຮູບໜ້າຈໍໄດ້. ມັນອາດລະເລີຍພາກສ່ວນທີ່ບໍ່ຄ່ອຍໃຊ້ທີ່ໃຊ້ເວລາລາຍງານດົນອອກໄປ." @@ -281,9 +284,12 @@ "ສະ​ຖານ​ທີ່" "ເຂົ້າເຖິງຂໍ້ມູນສະຖານທີ່ຂອງອຸປະກອນນີ້" "ອະນຸຍາດ <b>%1$s</b> ໃຫ້ເຂົ້າເຖິງສະຖານທີ່ຂອງອຸປະກອນບໍ?" - "ແອັບຈະມີສິດເຂົ້າເຖິງສະຖານທີ່ໃນເວລາທີ່ທ່ານກຳລັງໃຊ້ແອັບຢູ່ເທົ່ານັ້ນ." - "ອະນຸຍາດໃຫ້ <b>%1$s</b> ເຂົ້າເຖິງສະຖານທີ່ຂອງອຸປະກອນນີ້ບໍ?" - "ແອັບຈະມີສິດເຂົ້າເຖິງສະຖານທີ່ທຸກເທື່ອ ເຖິງແມ່ນໃນເວລາທ່ານບໍ່ໃຊ້ແອັບຢູ່ກໍຕາມ." + + + + + + "ປະຕິທິນ" "ເຂົ້າ​ຫາ​ປະ​ຕິ​ທິນ​ຂອງ​ທ່ານ" "ອະນຸຍາດ <b>%1$s</b> ໃຫ້ເຂົ້າເຖິງປະຕິທິນຂອງທ່ານບໍ?" @@ -316,7 +322,10 @@ "ອະນຸຍາດ <b>%1$s</b> ໃຫ້ເຂົ້າເຖິງເພງຂອງທ່ານບໍ?" "ຮູບພາບ ແລະ ວິດີໂອ" "ເຂົ້າເຖິງຮູບພາບ ແລະ ວິດີໂອຂອງທ່ານ" - "ອະນຸຍາດໃຫ້ <b>%1$s</b> ເຂົ້າເຖິງຮູບພາບ ແລະ ວິດີໂອຂອງທ່ານ, ຮວມທັງສະຖານທີ່ທີ່ຖືກແທັກນຳບໍ?" + + + + "ດຶງຂໍ້ມູນເນື້ອຫາໃນໜ້າຈໍ" "ກວດກາເນື້ອຫາຂອງໜ້າຈໍທີ່ທ່ານກຳລັງມີປະຕິສຳພັນນຳ." "ເປີດໃຊ້ \"ການສຳຫຼວດໂດຍສຳຜັດ\"" @@ -509,8 +518,10 @@ "ອະນຸຍາດໃຫ້ແອັບຯຕິດຕໍ່ສື່ສານກັບປ້າຍກຳກັບ, ບັດ ແລະໂຕອ່ານຂອງການສື່ສານໄລຍະສັ້ນ (NFC)." "ປິດການລັອກໜ້າຈໍ" "ອະນຸຍາດໃຫ້ແອັບຯປິດການເຮັດວຽກຂອງປຸ່ມລັອກ ແລະລະບົບຄວາມປອດໄພຂອງລະຫັດຜ່ານທີ່ເຊື່ອມໂຍງກັນ. ໂຕຢ່າງ: ໂທລະສັບຈະປິດການເຮັດວຽກຂອງປຸ່ມລັອກເມື່ອມີສາຍໂທເຂົ້າ ຈາກນັ້ນຈຶ່ງເປີດໃຊ້ໄດ້ອີກເມື່ອວາງສາຍແລ້ວ." - "ຮ້ອງຂໍຄວາມຊັບຊ້ອນການລັອກໜ້າຈໍ" - "ອະນຸຍາດໃຫ້ແອັບສຶກສາລະດັບຄວາມຊັບຊ້ອນຂອງໜ້າຈໍລັອກ (ສູງ, ກາງ, ຕ່ຳ ຫຼື ບໍ່ມີ), ເຊິ່ງລະບຸຂອບເຂດຄວາມເປັນໄປໄດ້ຂອງຄວາມຍາວ ແລະ ປະເພດຂອງການລັອກໜ້າຈໍ. ແອັບນີ້ສາມາດແນະນຳຜູ້ໃຊ້ວ່າເຂົາເຈົ້າສາມາດອັບເດດໜ້າຈໍລັອກເປັນລະດັບໃດໜຶ່ງເປັນການສະເພາະໄດ້, ແຕ່ຜູ້ໃຊ້ສາມາດທີ່ຈະບໍ່ສົນໃຈ ຫຼື ເປີດໄປອັນອື່ນໄດ້. ກະລຸນາຮັບຊາບວ່າການລັອກໜ້າຈໍບໍ່ໄດ້ບັນທຶກໃນແບບຂໍ້ຄວາມທຳມະດາ, ດັ່ງນັ້ນແອັບຈະບໍ່ຮູ້ລະຫັດຜ່ານທີ່ແນ່ນອນ." + + + + "ໃຊ້ຮາດແວຊີວະມິຕິ" "ອະນຸຍາດໃຫ້ແອັບນຳໃຊ້ຮາດແວຊີວະມິຕິສຳລັບການພິສູດຢືນຢັນ" "ຈັດ​ການ​ຮາດ​ແວ​ລາຍ​ນີ້ວ​ມື" @@ -565,37 +576,59 @@ "ອະນຸຍາດໃຫ້ແອັບເປີດວິທີການຕ່າງໆເພື່ອເພີ່ມ ແລະ ລຶບແມ່ແບບໃບໜ້າສຳລັບການນຳໃຊ້." "ໃຊ້ຮາດແວການກວດສອບຄວາມຖືກຕ້ອງດ້ວຍໃບໜ້າ" "ອະນຸຍາດໃຫ້ແອັບໃຊ້ຮາດແວການກວດສອບຄວາມຖືກຕ້ອງດ້ວຍໃບໜ້າສຳລັບການກວດສອບຄວາມຖືກຕ້ອງ" - "ບໍ່ສາມາດປະມວນຜົນຂໍ້ມູນໃບໜ້າໄດ້. ກະລຸນາລອງອີກຄັ້ງ." - "ໃບໜ້າສະຫວ່າງເກີນໄປ. ກະລຸນາລອງໃນບ່ອນທີ່ແສງໜ້ອຍກວ່າ." - "ໃບໜ້າມືດເກີນໄປ. ກະລຸນາເປີດແຫຼ່ງກຳເນີດແສງ." - "ກະລຸນາຍ້າຍເຊັນເຊີອອກຫ່າງຈາກໃບໜ້າຕື່ມອີກ." - "ກະລຸນາຍ້າຍເຊັນເຊີເຂົ້າໃກ້ໃບໜ້າຕື່ມອີກ." - "ກະລຸນາຍ້າຍເຊັນເຊີໃຫ້ສູງຂຶ້ນອີກ." - "ກະລຸນາຍ້າຍເຊັນເຊີໃຫ້ຕໍ່າລົງອີກ." - "ກະລຸນາຍ້າຍເຊັນເຊີໄປເບື້ອງຂວາ." - "ກະລຸນາຍ້າຍເຊັນເຊີໄປເບື້ອງຊ້າຍ." - "ກະລຸນາແນມເບິ່ງເຊັນເຊີ." - "ກວດບໍ່ພົບໃບໜ້າ." - "ເຄື່ອນໄຫວຫຼາຍເກີນໄປ." + + + + + + + + + + + + + + + + + + + + + + + + "ກະລຸນາລົງທະບຽນອຸປະກອນຂອງທ່ານອີກເທື່ອໜຶ່ງ." - "ກວດພົບໃບໜ້າຕ່າງກັນ." + + "ຄ້າຍກັນເກີນໄປ, ກະລຸນາປ່ຽນທ່າຂອງທ່ານ." - "ກະລຸນາເບິ່ງຊື່ໆໄປທາງກ້ອງຫຼາຍຂຶ້ນ." - "ກະລຸນາເບິ່ງຊື່ໆໄປທາງກ້ອງຫຼາຍຂຶ້ນ." + + + + "ກະລຸນາຍັບຫົວຂອງທ່ານໃຫ້ຊື່ຕາມລວງຕັ້ງ." - "ກະລຸນາຢ່າປິດບັງໃບໜ້າຂອງທ່ານ." + + + + "ຮາດແວກວດໃບໜ້າບໍ່ສາມາດໃຊ້ໄດ້." - "ໝົດເວລາກວດໃບໜ້າແລ້ວ. ກະລຸນາລອງອີກຄັ້ງ." + + "ບໍ່ສາມາດເກັບຮັກສາໃບໜ້າໄວ້ໄດ້." "ຍົກເລີກການດຳເນີນການກັບໃບໜ້າແລ້ວ." "ຜູ້ໃຊ້ຍົກເລີກການພິສູດຢືນຢັນໃບໜ້າແລ້ວ." "ມີຄວາມພະຍາຍາມຫຼາຍຄັ້ງເກີນໄປ. ກະລຸນາລອງໃໝ່ໃນພາຍຫຼັງ." "ມີຄວາມພະຍາຍາມຫຼາຍຄັ້ງເກີນໄປ. ປິດນຳໃຊ້ການກວດສອບຄວາມຖືກຕ້ອງດ້ວຍໃບໜ້າແລ້ວ." - "ລອງອີກຄັ້ງ." - "ບໍ່ໄດ້ລົງທະບຽນໃບໜ້າໃດ." - "ອຸປະກອນນີ້ບໍ່ມີເຊັນເຊີກວດສອບຄວາມຖືກຕ້ອງດ້ວຍໃບໜ້າ." + + + + + + "ໃບໜ້າ %d" @@ -1213,9 +1246,16 @@ "Open %1$s" "%1$s will close without saving" "%1$s ເກີນ​ຂີດ​ຄວາມ​ຈຳ​ແລ້ວ" + + "ກວດພົບ Heap dump. ແຕະເພື່ອແບ່ງປັນ." "ແບ່ງ​ປັນ​ການ​ເທກອງ​ບໍ?" - "ຂະ​ບວນ​ການ %1$s ເກີນ​ຂີດ​ຈຳ​ກັດ​ຄວາມ​ຈຳ​ຂະ​ບວນ​ການ​ຂອງ​ມັນ​ຂອງ %2$s ແລ້ວ. ການ​ເທກອງ​ມີ​ໃຫ້​ສຳ​ລັບ​ທ່ານ ເພື່ອ​ແບ່ງ​ປັນ​ກັບ​ຜູ້​ພ​ັດ​ທະ​ນາ​ຂອງ​ມັນ. ລະ​ວັງ: ການ​ເທກອງ​ນີ້​ສາ​ມາດ​ມີ​ຂໍ້​ມູນ​ສ່ວນ​ຕົວ​ໃດ​ໜຶ່ງ​ຂອງ​ທ່ານ ທີ່​ແອັບ​ພ​ລິ​ເຄ​ຊັນ​ມີ​ການ​ເຂົ້າ​ຫາ." + + + + + + "ເລືອກການເຮັດວຽກຂອງຂໍ້ຄວາມ" "ລະດັບສຽງເອີ້ນເຂົ້າ" "ລະດັບສຽງຂອງສື່" @@ -1254,8 +1294,10 @@ "ແຕະເພື່ອເບິ່ງເຄືອຂ່າຍທັງໝົດ" "ເຊື່ອມ​ຕໍ່" "ເຄືອຂ່າຍທັງໝົດ" - "ສາມາດໃຊ້ເຄືອຂ່າຍ Wi‑Fi ທີ່ %s ສະເໜີໄດ້" - "ທ່ານຕ້ອງການເຊື່ອມຕໍ່ຫາເຄືອຂ່າຍທີ່ສະເໜີໂດຍ %s ຫຼືບໍ່?" + + + + "ແມ່ນ" "ບໍ່ແມ່ນ" "ຈະມີການເປີດໃຊ້ Wi‑Fi ອັດຕະໂນມັດ" @@ -1267,9 +1309,14 @@ "ລົງຊື່ເຂົ້າເຄືອຂ່າຍ" - "Wi-Fi ບໍ່ມີສັນຍານອິນເຕີເນັດ" + + "ແຕະເພື່ອເບິ່ງຕົວເລືອກ" "ເຊື່ອມຕໍ່ແລ້ວ" + + + + "ການປ່ຽນແປງການຕັ້ງຄ່າຮັອດສະປອດຂອງທ່ານ" "ຄື້ນຄວາມຖີ່ຮັອດສະປອດຂອງທ່ານປ່ຽນແປງແລ້ວ." "ອຸປະກອນນີ້ບໍ່ຮອງຮັບການຕັ້ງຄ່າຂອງທ່ານສຳລັບ 5GHz ເທົ່ານັ້ນ. ແຕ່ວ່າອຸປະກອນນີ້ຈະໃຊ້ຄື້ນຄວາມຖີ່ 5GHz ເມື່ອສາມາດໃຊ້ໄດ້." @@ -1354,6 +1401,10 @@ "ເຊື່ອມຕໍ່ການດີບັກຜ່ານ USB ແລ້ວ" "ແຕະເພື່ອປິດການດີບັກ USB" "ເລືອກເພື່ອປິດການດີບັ໊ກຜ່ານ USB." + + + + "ມີຂອງແຫລວ ຫຼື ເສດດິນໃນຜອດ USB" "ປິດຜອດ USB ໂດຍອັດຕະໂນມັດແລ້ວ. ແຕະເພື່ອສຶກສາເພີ່ມເຕີມ." "ປອດໄພທີ່ຈະໃຊ້ຜອດ USB ແລ້ວ" @@ -1905,8 +1956,6 @@ "ແຕະເພື່ອປົດລັອກໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ" "ເຊື່ອມຕໍ່ກັບ %1$s ແລ້ວ" "ແຕະເພື່ອເບິ່ງໄຟລ໌" - "ປັກໝຸດ" - "ຖອນປັກໝຸດ" "ຂໍ້ມູນແອັບ" "−%1$s" "ກຳລັງເລີ່ມເດໂມ…" @@ -1997,6 +2046,22 @@ "ການແຈ້ງເຕືອນຂໍ້ມູນໂໝດກິດຈະວັດປະຈຳວັນ" "ແບັດເຕີຣີອາດໝົດກ່ອນການສາກຕາມປົກກະຕິ" "ເປີດຕົວປະຢັດແບັດເຕີຣີເພື່ອຂະຫຍາຍອາຍຸແບັດເຕີຣີ" + + + + + + + + + + + + + + + + "ໂຟນເດີ" "ແອັບພລິເຄຊັນ Android" "ໄຟລ໌" diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 94ab46ce6bc4740552f100463bb00b5a7797de30..2d6e4cb9366b98db9965e56e992e81fe7dad72ff 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -143,8 +143,10 @@ "„Wi-Fi“ skambinimas" "VoWifi" "Išjungta" - "Pageidautinas „Wi-Fi“ ryšys" - "Pirmenybė mobiliojo ryšio tinklui" + + + + "Tik „Wi-Fi“" "{0}: neperadresuota" "{0}: {1}" @@ -232,7 +234,8 @@ "Pranešimas apie riktą" "Baigti seansą" "Ekrano kopija" - "Pranešti apie riktą" + + "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." "Interakt. ataskaita" "Naudokite tai esant daugumai aplinkybių. Galite stebėti ataskaitos eigą, įvesti daugiau išsamios informacijos apie problemą ir padaryti ekrano kopijų. Gali būti praleidžiamos kelios rečiau naudojamos skiltys, kurių ataskaitų teikimas ilgai trunka." @@ -287,9 +290,12 @@ "Vietovė" "pasiekti įrenginio vietovės informaciją" "Suteikti <b>%1$s</b> galimybę pasiekti įrenginio vietovę?" - "Programa galės pasiekti vietovę, tik kai ją naudosite." - "Visada leisti programai <b>%1$s</b> pasiekti šio įrenginio vietovę?" - "Programa visada galės pasiekti vietovę, net kai jos nenaudosite." + + + + + + "Kalendorius" "pasiekti kalendorių" "Suteikti <b>%1$s</b> galimybę pasiekti kalendorių?" @@ -322,7 +328,10 @@ "Suteikti programai <b>%1$s</b> prieigą prie muzikos?" "Nuotraukos ir vaizdo įrašai" "pasiekti nuotraukas ir vaizdo įrašus" - "Leisti programai <b>%1$s</b> pasiekti nuotraukas ir vaizdo įrašus, įskaitant pažymėtas vietoves?" + + + + "Gauti lango turinį" "Tikrinti lango, su kuriuo sąveikaujate, turinį." "Įjungti „Naršyti paliečiant“" @@ -515,8 +524,10 @@ "Leidžiama programai perduoti artimojo lauko ryšių technologijos (ALR) žymas, korteles ir skaitymo programas." "išjungti ekrano užraktą" "Leidžiama programai neleisti klavišo užrakto ir visos susijusios slaptažodžio apsaugos. Pvz., telefonas neleidžia klavišo užrakto priimant gaunamąjį skambutį ir pakartotinai jį įgalina, kai skambutis baigiamas." - "pateikti ekrano užrakto sudėtingumo užklausą" - "Leidžiama programai sužinoti ekrano užrakto sudėtingumo lygį (aukštas, vidutinis, žemas arba nėra), nurodantį galimą ekrano užrakto trukmės diapazoną ir tipą. Be to, programa gali pasiūlyti naudotojams atnaujinti ekrano užraktą į tam tikrą lygį, bet naudotojai gali laisvai nepaisyti ir išeiti. Atminkite, kad ekrano užraktas nesaugomas kaip grynasis tekstas, todėl programa nežino tikslaus slaptažodžio." + + + + "naudoti biometrinę aparatinę įrangą" "Leidžiama programai naudoti biometrinę aparatinę įrangą tapatybei nustatyti" "tvarkyti piršto antspaudo aparatinę įrangą" @@ -571,37 +582,59 @@ "Programai leidžiama aktyv. metodus, norint pridėti ir ištrinti naudojamus veidų šablonus." "naudoti veido autentifikavimo aparatinę įrangą" "Programai leidžiama naudoti veido autentifikavimo aparatinę įrangą tapatybei nustatyti" - "Nepavyko apdoroti veido. Bandykite dar kartą." - "Veidas per šviesus. Band. sumažinti apšvietimą." - "Veidas per tamsus. Atidenkite apšvietimo šaltinį." - "Patraukite jutiklį toliau nuo veido." - "Laikykite jutiklį arčiau veido." - "Pakelkite jutiklį aukščiau." - "Nuleiskite jutiklį žemiau." - "Patraukite jutiklį dešinėn." - "Patraukite jutiklį kairėn." - "Žiūrėkite į jutiklį." - "Neaptikta jokių veidų." - "Įrenginys per daug judinamas." + + + + + + + + + + + + + + + + + + + + + + + + "Užregistruokite veidą iš naujo." - "Aptiktas kitas veidas." + + "Per daug panašu, pakeiskite veido išraišką." - "Žiūrėkite tiesiai į fotoaparatą." - "Žiūrėkite tiesiai į fotoaparatą." + + + + "Laikykite galvą vertikaliai." - "Atidenkite veidą." + + + + "Veido atpažinimo aparatinė įranga nepasiekiama." - "Baigėsi veido atpaž. skirt. laik. Band. dar kartą." + + "Nepavyko išsaugoti veido duomenų." "Veido atpažinimo operacija atšaukta." "Veido autentifikavimą atšaukė naudotojas." "Per daug bandymų. Vėliau bandykite dar kartą." "Per daug bandymų. Veido autentifik. išjungtas." - "Bandykite dar kartą." - "Neužregistruota jokių veidų." - "Šiame įrenginyje nėra veido autentifikavimo jutiklio." + + + + + + "%d veidas" @@ -1253,9 +1286,16 @@ "Atidaryti „%1$s“" "„%1$s“ bus uždaryta neišsaugojus duomenų" "„%1$s“ viršijo atminties limitą" + + "Atminties išklotinės duomenys surinkti. Palieskite, jei norite bendrinti." "Bendrinti atminties išklotinę?" - "Procesas „%1$s“ viršijo atminties limitą %2$s. Atminties išklotinė pasiekiama, kad galėtumėte bendrinti su jos kūrėju. Būkite atsargūs: šioje atminties išklotinėje gali būti jūsų asmeninės informacijos, kurią gali pasiekti programa." + + + + + + "Pasirinkite teksto veiksmą" "Skambučio garsumas" "Medijos garsumas" @@ -1298,8 +1338,10 @@ "Palieskite, jei norite matyti visus tinklus" "Prisijungti" "Visi tinklai" - "Pasiekiamas „%s“ siūlomas „Wi‑Fi“ tinklas" - "Ar norite prisijungti prie „%s“ siūlomų tinklų?" + + + + "Taip" "Ne" "„Wi‑Fi“ bus įjungtas automatiškai" @@ -1311,9 +1353,14 @@ "Prisijungti prie tinklo" - "„Wi‑Fi“ tinkle nėra interneto ryšio" + + "Palieskite, kad būtų rodomos parinktys." "Prisijungta" + + + + "Viešosios interneto prieigos taško nustatymų pakeitimai" "Viešosios prieigos taško dažnio juosta pasikeitė." "Šiame įrenginyje nepalaikoma tik 5 GHz nuostata. Vietoj to šiame įrenginyje bus naudojama 5 GHz dažnio juosta, kai bus pasiekiama." @@ -1398,6 +1445,10 @@ "USB derinimas prijungtas" "Palieskite, kad išjungtumėte USB derinimą" "Pasirinkite, kas išjungtumėte USB derinimą." + + + + "USB prievade yra skysčių ar smulkių dalelių" "USB prievadas automatiškai išjungtas. Palieskite, kad sužinotumėte daugiau." "Saugu naudoti USB prievadą" @@ -1973,8 +2024,6 @@ "Paliesk., kad atr. darbo prof." "Prisijungta prie „%1$s“" "Palieskite, kad peržiūrėtumėte failus" - "Prisegti" - "Atsegti" "Programos informacija" "–%1$s" "Paleidžiama demonstracinė versija…" @@ -2067,6 +2116,22 @@ "Veiksmų sekos režimo informacijos pranešimas" "Akumuliatoriaus energija gali išsekti prieš įprastą įkrovimą" "Akumuliatoriaus tausojimo priemonė suaktyvinta, kad akumuliatorius veiktų ilgiau" + + + + + + + + + + + + + + + + "Aplankas" "„Android“ programa" "Failas" diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 53ab075f8faf24ca82f660599e1e8f589b829850..93f86e6b70516a00c0c83bfbf90bb685cc21323f 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -142,8 +142,10 @@ "Wi-Fi zvani" "VoWifi" "Izslēgts" - "Vēlams Wi-Fi tīkls" - "Vēlams mobilo datu savienojums" + + + + "Tikai Wi-Fi" "{0}: nav pāradresēts" "{0}: {1}" @@ -230,7 +232,8 @@ "Kļūdu ziņojums" "Beigt sesiju" "Ekrānuzņēmums" - "Kļūdu ziņojuma sagatavošana" + + "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." "Interaktīvs pārskats" "Izmantojiet lielākajā daļā gadījumu. Varat izsekot pārskata izveides norisi, ievadīt papildu informāciju par problēmu un izveidot ekrānuzņēmumus. Var tikt izlaistas dažas mazāk izmantotas sadaļas, kuru izveidei nepieciešams daudz laika." @@ -284,9 +287,12 @@ "Atrašanās vieta" "piekļūt ierīces atrašanās vietas informācijai" "Vai atļaut lietotnei <b>%1$s</b> piekļūt šīs ierīces atrašanās vietai?" - "Lietotne varēs piekļūt atrašanās vietai tikai tad, kad izmantosiet šo lietotni." - "Vienmēr atļaut <b>%1$s</b> piekļūt atrašanās vietai?" - "Lietotne vienmēr varēs piekļūt atrašanās vietai, pat ja neizmantosiet šo lietotni." + + + + + + "Kalendārs" "piekļūt jūsu kalendāram" "Vai atļaut lietotnei <b>%1$s</b> piekļūt jūsu kalendāram?" @@ -319,7 +325,10 @@ "Vai atļaut lietotnei <b>%1$s</b> piekļūt jūsu mūzikai?" "Fotoattēli un videoklipi" "Piekļūt jūsu fotoattēliem un videoklipiem" - "Vai atļaut lietotnei <b>%1$s</b> piekļūt jūsu fotoattēliem, videoklipiem, tostarp atzīmētajām atrašanās vietām?" + + + + "Izgūt loga saturu." "Skatīt tā loga saturu, ar kuru mijiedarbojaties." "Aktivizēt funkciju “Pārlūkot pieskaroties”." @@ -512,8 +521,10 @@ "Ļauj lietotnei sazināties ar tuva darbības lauka sakaru (Near Field Communication — NFC) atzīmēm, kartēm un lasītājiem." "atspējot ekrāna bloķēšanu" "Ļauj lietotnei atspējot taustiņslēgu un visu saistīto paroļu drošību. Piemēram, tālrunis atspējo taustiņslēgu, saņemot ienākošu zvanu, un pēc zvana pabeigšanas atkārtoti iespējo taustiņslēgu." - "Ekrāna bloķēšanas sarežģītības pakāpes informācijas pieprasījums" - "Atļauj lietotnei piekļūt informācijai par ekrāna bloķēšanas sarežģītības pakāpi (augsta, vidēja, zema, nav), kas apzīmē iespējamo paroles garumu un ekrāna bloķēšanas veidus. Lietotnē lietotājiem var tikt rādīts arī ieteikums ekrāna bloķēšanai iestatīt citu līmeni, taču šo ieteikumu var ignorēt un aizvērt. Ņemiet vērā, ka ekrāna bloķēšanas parole netiek glabāta vienkārša teksta formātā, tāpēc lietotnei nav piekļuves precīzai parolei." + + + + "izmantot biometrisko datu aparatūru" "Atļauj lietotnei izmantot biometrisko datu aparatūru autentificēšanai" "pārvaldīt pirkstu nospiedumu aparatūru" @@ -568,37 +579,59 @@ "Atļauj lietotnei izsaukt metodes izmantojamo sejas veidņu pievienošanai un dzēšanai." "izmantot sejas autentifikācijas aparatūru" "Atļauj lietotnei izmantot sejas autentifikācijas aparatūru autentificēšanai" - "Nevarēja apstrādāt sejas datus. Mēģiniet vēlreiz." - "Seja ir pārāk izgaismota. Mēģiniet tumšākā vidē." - "Pārāk tumšs sejas attēls. Mēģiniet gaišākā vidē." - "Pavirziet sensoru tālāk no sejas." - "Pavirziet sensoru tuvāk sejai." - "Pavirziet sensoru augstāk." - "Pavirziet sensoru zemāk." - "Pavirziet sensoru pa labi." - "Pavirziet sensoru pa kreisi." - "Lūdzu, paskatieties uz sensoru." - "Nav atrasta neviena seja." - "Pārāk daudz kustību." + + + + + + + + + + + + + + + + + + + + + + + + "Lūdzu, atkārtoti reģistrējiet savu seju." - "Tika noteikta cita seja." + + "Pārāk līdzīgi. Lūdzu, mainiet pozu." - "Lūdzu, tiešāk skatieties uz kameru." - "Lūdzu, tiešāk skatieties uz kameru." + + + + "Lūdzu, vertikāli iztaisnojiet galvu." - "Lūdzu, atsedziet seju." + + + + "Sejas autentifikācijas aparatūra nav pieejama." - "Sejas datu nolasīšanas noildze. Mēģiniet vēlreiz." + + "Sejas datus nevar saglabāt." "Darbība ar sejas datiem atcelta." "Lietotājs atcēla sejas autentificēšanu." "Pārāk daudz mēģinājumu. Vēlāk mēģiniet vēlreiz." "Par daudz mēģinājumu. Sejas atpazīšana atspējota." - "Mēģiniet vēlreiz." - "Nav reģistrēti sejas dati." - "Šai ierīcei nav sejas autentifikācijas sensora." + + + + + + "Seja %d" @@ -1233,9 +1266,16 @@ "Atvērt %1$s" "Lietotne %1$s tiks aizvērta, neko nesaglabājot" "Process %1$s pārsniedza atmiņas ierobežojumu." + + "Apkopots kaudzes izraksts. Pieskarieties, lai kopīgotu." "Vai kopīgot kaudzes izrakstu?" - "Process %1$s pārsniedza procesu atmiņas ierobežojumu (%2$s). Tika apkopots kaudzes izraksts, ko varat kopīgot ar procesa izstrādātāju. Ņemiet vērā: kaudzes izrakstā var būt ietverta jūsu personas informācija, kurai var piekļūt lietojumprogramma." + + + + + + "Izvēlieties darbību tekstam" "Zvanītāja skaļums" "Multivides skaļums" @@ -1276,8 +1316,10 @@ "Pieskarieties, lai skatītu visus tīklus" "Izveidot savienojumu" "Visi tīkli" - "Ir pieejams Wi‑Fi tīkls, ko ieteica %s" - "Vai vēlaties izveidot savienojumu ar tīkliem, ko ieteica %s?" + + + + "Jā" "Nē" "Wi‑Fi tiks automātiski ieslēgts" @@ -1289,9 +1331,14 @@ "Pierakstīšanās tīklā" - "Wi-Fi tīklā nav piekļuves internetam." + + "Pieskarieties, lai skatītu iespējas." "Izveidots savienojums" + + + + "Izmaiņas tīklāja iestatījumos" "Ir mainīts tīklāja joslas platums." "Šajā ierīcē netiek atbalstīta jūsu preference par tikai 5 GHz joslu. 5 GHz josla ierīcē tiks izmantota, kad tā būs pieejama." @@ -1376,6 +1423,10 @@ "USB atkļūdošana ir pievienota." "Pieskarieties, lai izslēgtu USB atkļūdošanu" "Atlasiet, lai atspējotu USB atkļūdošanu." + + + + "USB pieslēgvietā ir šķidrums vai daļiņas" "USB pieslēgvieta ir automātiski atspējota. Pieskarieties, lai uzzinātu vairāk." "USB pieslēgvietas izmantošana ir droša" @@ -1939,8 +1990,6 @@ "Pieskarieties, lai atbloķētu." "Izveidots savienojums ar: %1$s" "Pieskarieties, lai skatītu failus." - "Piespraust" - "Atspraust" "Lietotnes informācija" "−%1$s" "Notiek demonstrācijas palaišana..." @@ -2032,6 +2081,22 @@ "Informatīvs paziņojums par akumulatoru" "Akumulators var izlādēties pirms parastā uzlādes laika" "Aktivizēts akumulatora jaudas taupīšanas režīms, lai palielinātu akumulatora darbības ilgumu" + + + + + + + + + + + + + + + + "Mape" "Android lietojumprogramma" "Fails" diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 4e0b47a22b928741c4baab0325b4c2cb6fe6e98b..20d42ade08cfe0026f169bde80065d98f98b5f3b 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -141,8 +141,10 @@ "Повикување преку Wi-Fi" "Глас преку Wi-Fi" "Исклучено" - "Се претпочита Wi-Fi" - "Претпочитам мобилен интернет" + + + + "Само Wi-Fi" "{0}: не е препратено" "{0}: {1}" @@ -228,7 +230,8 @@ "Извештај за грешка" "Завршете ја сесијата" "Слика од екранот" - "Направи извештај за грешки" + + "Ова ќе собира информации за моменталната состојба на вашиот уред, за да ги испрати како порака по е-пошта. Тоа ќе одземе малку време почнувајќи од извештајот за грешки додека не се подготви за праќање; бидете трпеливи." "Интерактивен извештај" "Користете го ова во повеќето ситуации. Ви дозволува да го следите напредокот на извештајот, да внесете повеќе детали во врска со проблемот и да сликате слики од екранот. Може да испушти некои помалку користени делови за коишто е потребно долго време за да се пријават." @@ -281,9 +284,12 @@ "Локација" "пристапува до локацијата на овој уред" "Дали да се дозволи <b>%1$s</b> да пристапува до локацијата на уредот?" - "Апликацијата ќе има пристап до локацијата само додека ја користите." - "Дозволете <b>%1$s</b> секогаш да пристапува до локацијата?" - "Апликацијата секогаш ќе има пристап до локацијата, дури и кога не ја користите." + + + + + + "Календар" "пристапува до календарот" "Дали да се дозволи <b>%1$s</b> да пристапува до календарот?" @@ -316,7 +322,10 @@ "Дали да се дозволи <b>%1$s</b> да пристапува до музиката?" "Фотографии и видеа" "пристапува до фотографиите и видеата" - "Дали да се дозволи <b>%1$s</b> да пристапува до фотографиите и видеата, вклучувајќи означени локации?" + + + + "Преземе содржина на прозорец" "Ја следи содржината на прозорецот со кој се комуницира." "Вклучи „Истражувај со допир“" @@ -509,8 +518,10 @@ "Дозволува апликацијата да комуницира со ознаки, картички и читачи за Комуникација при непосредна близина (NFC)." "оневозможи заклучување на екран" "Овозможува апликацијата да го оневозможи заклучувањето и каква било безбедност поврзана со лозинка. На пример, телефонот го оневозможува заклучувањето при прием на телефонски повик, а потоа повторно го овозможува заклучувањето кога повикот ќе заврши." - "побарува сложеност за заклучувањето на екранот" - "Дозволува апликацијата да го научи нивото на сложеност за заклучувањето на екранот (високо, средно, ниско или нема), коешто ги означува можниот опсег на должината и типот на заклучувањето на екранот. Апликацијата може и да им предлага на корисниците да го ажурираат заклучувањето на екранот на одредено ниво, но корисниците може слободно да го игнорираат тоа и да продолжат понатаму. Имајте предвид дека заклучувањето на екранот не се складира како обичен текст, па така апликацијата не ја знае точната лозинка." + + + + "користи биометриски хардвер" "Дозволува апликацијата да користи биометриски хардвер за проверка" "управувај хардвер за отпечатоци" @@ -565,37 +576,59 @@ "Дозволува апликац. да повика начини за додавање и бришење шаблони на лице за користење." "користи хардвер за проверка на лице" "Дозволува апликацијата да користи хардвер за лице за проверка" - "Лицето не можеше да се обработи. Обидете се пак." - "Лицето е пресветло. Пробајте на послаба светлина." - "Лицето е претемно. Пробајте со поголема светлина." - "Оддалечете го сензорот од лицето." - "Доближете го сензорот до лицето." - "Подигнете го сензорот." - "Спуштете го сензорот." - "Поместете го сензорот надесно." - "Поместете го сензорот налево." - "Погледнете во сензорот." - "Не е откриено лице." - "Премногу движење." + + + + + + + + + + + + + + + + + + + + + + + + "Повторно регистрирајте го лицето." - "Откриено е друго лице." + + "Премногу слично, сменете ја позата." - "Гледајте подиректно во камерата." - "Гледајте подиректно во камерата." + + + + "Исправете ја главата вертикално." - "Откријте го вашето лице." + + + + "Хардверот за лице не е достапен." - "Истече времето за проверка на лице. Повторен обид." + + "Лицето не може да се чува." "Операцијата со лице се откажа." "Проверката на лицето е откажана од корисникот." "Премногу обиди. Обидете се повторно подоцна." "Премногу обиди. Проверката на лице е оневозможена." - "Обидете се повторно." - "Нема регистрирано лице." - "Уредов нема сензор за проверка на лице." + + + + + + "Лице %d" @@ -1213,9 +1246,16 @@ "Отвори ја %1$s" "%1$s ќе се затвори без да се зачува" "%1$s го надмина ограничувањето на меморијата" + + "Сликата од меморијата е собрана. Допрете за споделување." "Сподели слика од меморија?" - "Процесот %1$s го надмина ограничувањето на меморијата на својот процес од %2$s. Достапна ви е слика од меморијата да ја споделите со неговиот програмер. Бидете внимателни: сликата од меморијата може да содржи кои било од вашите лични информации до кои апликацијата има пристап." + + + + + + "Избери дејство за текст" "Јачина на звук на ѕвонче" "Јачина на аудио/видео звук" @@ -1254,8 +1294,10 @@ "Допрете за да ги видите сите мрежи" "Поврзете се" "Сите мрежи" - "Wi‑Fi мрежата предложена од %s е достапна" - "Дали сакате да се поврзувате на мрежите што ги предлага %s?" + + + + "Да" "Не" "Wi‑Fi ќе се вклучи автоматски" @@ -1267,9 +1309,14 @@ "Најавете се на мрежа" - "Wi-Fi нема пристап до интернет" + + "Допрете за опции" "Поврзано" + + + + "Промени на поставките за точка на пристап" "Појасот за точка на пристап е променет." "Уредов не ги поддржува вашите поставки за само 5 GHz. Наместо тоа, ќе го користи појасот од 5 GHz кога е достапен." @@ -1355,6 +1402,10 @@ "Поврзано е отстранување грешки преку USB" "Допрете за да го исклучите отстранувањето грешки преку USB" "Изберете за да се оневозможи отстранување грешки на USB." + + + + "Течност или нечистотија во USB-портата" "USB-портата е автоматски оневозможена. Допрете за да дознаете повеќе." "Безбедно е да се користи USB-портата" @@ -1908,8 +1959,6 @@ "Допрете за да го отклучите" "Поврзан на %1$s" "Допрете за да ги погледнете датотеките" - "Прикачете" - "Откачете" "Информации за апликација" "−%1$s" "Се вклучува демонстрацијата…" @@ -2000,6 +2049,22 @@ "Известување за информации за режимот за рутини" "Батеријата може да се потроши пред вообичаеното време за полнење" "Активиран е „Штедачот на батерија“ за да се продолжи траењето на батеријата" + + + + + + + + + + + + + + + + "Папка" "Апликација за Android" "Датотека" diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 13ca198228338b48282abdda678b047c7760228c..ca822380ff37c015499e998316f9157e21198c80 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -141,8 +141,10 @@ "വൈഫൈ കോളിംഗ്" "Voവൈഫൈ" "ഓഫ്" - "വൈഫൈ തിരഞ്ഞെടുത്തിരിക്കുന്നു" - "മൊബൈൽ ഡാറ്റ ഉപയോഗിക്കാൻ താൽപ്പര്യപ്പെടുന്നു" + + + + "വൈഫൈ മാത്രം" "{0}: കൈമാറിയില്ല" "{0}: {1}" @@ -228,7 +230,8 @@ "ബഗ് റിപ്പോർട്ട്" "സെഷൻ അവസാനിപ്പിക്കുക" "സ്‌ക്രീൻഷോട്ട്" - "ബഗ് റിപ്പോർട്ട് എടുക്കുക" + + "ഒരു ഇമെയിൽ സന്ദേശമായി അയയ്‌ക്കുന്നതിന്, ഇത് നിങ്ങളുടെ നിലവിലെ ഉപകരണ നിലയെക്കുറിച്ചുള്ള വിവരങ്ങൾ ശേഖരിക്കും. ബഗ് റിപ്പോർട്ട് ആരംഭിക്കുന്നതിൽ നിന്ന് ഇത് അയയ്‌ക്കാനായി തയ്യാറാകുന്നതുവരെ അൽപ്പസമയമെടുക്കും; ക്ഷമയോടെ കാത്തിരിക്കുക." "ഇന്റരാക്റ്റീവ് റിപ്പോർട്ട്" "മിക്ക സാഹചര്യങ്ങളിലും ഇത് ഉപയോഗിക്കുക. റിപ്പോർട്ടിന്റെ പുരോഗതി കാണാനും പ്രശ്നത്തെ കുറിച്ചുള്ള കൂടുതൽ വിശദാംശങ്ങൾ നൽകാനും സ്ക്രീൻഷോട്ടുകൾ എടുക്കാനും ഇത് അനുവദിക്കുന്നു. റിപ്പോർട്ടുചെയ്യാൻ നീണ്ട സമയം എടുക്കുന്നതും നിങ്ങൾ കുറച്ച് ഉപയോഗിക്കുന്നതുമായ ചില വിഭാഗങ്ങളെ ഇത് വിട്ടുകളഞ്ഞേക്കാം." @@ -281,9 +284,12 @@ "ലൊക്കേഷൻ" "ഈ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ആക്സസ് ചെയ്യാൻ" "ഈ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യാൻ <b>%1$s</b> ആപ്പിനെ അനുവദിക്കണോ?" - "നിങ്ങൾ ഉപയോഗിക്കുമ്പോൾ മാത്രം ആപ്പ് ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യും." - "ഈ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യാൻ <b>%1$s</b> എപ്പോഴും ആപ്പിനെ അനുവദിക്കണോ?" - "നിങ്ങൾ ആപ്പ് ഉപയോഗിക്കാത്തപ്പോൾ പോലും, ഏതുസമയത്തും ആപ്പ് ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യും." + + + + + + "കലണ്ടർ" "നിങ്ങളുടെ കലണ്ടർ ആക്‌സസ്സ് ചെയ്യുക" "നിങ്ങളുടെ കലണ്ടർ ആക്‌സസ് ചെയ്യാൻ <b>%1$s</b> ആപ്പിനെ അനുവദിക്കണോ?" @@ -316,7 +322,10 @@ "<b>%1$s</b> എന്നതിനെ നിങ്ങളുടെ സംഗീതം ആക്‌സസ് ചെയ്യാൻ അനുവദിക്കണോ?" "ഫോട്ടോകളും വീഡിയോകളും" "നിങ്ങളുടെ ഫോട്ടോകളും & വീഡിയോകളും" - "ടാഗ് ചെയ്‌ത ലൊക്കേഷനുകൾ ഉൾപ്പെടെ നിങ്ങളുടെ ഫോട്ടോകളും വീഡിയോകളും ആക്‌സസ് ചെയ്യാൻ <b>%1$s</b>-നെ അനുവദിക്കണോ?" + + + + "വിൻഡോ ഉള്ളടക്കം വീണ്ടെടുക്കുക" "നിങ്ങൾ സംവദിക്കുന്ന ഒരു വിൻഡോയുടെ ഉള്ളടക്കം പരിശോധിക്കുക." "സ്‌പർശനം വഴി പര്യവേക്ഷണം ചെയ്യുക, ഓണാക്കുക" @@ -509,8 +518,10 @@ "നിയർ ഫീൽഡ് കമ്മ്യൂണിക്കേഷൻ (NFC) ടാഗുകളുമായും കാർഡുകളുമായും റീഡറുകളുമായുള്ള ആശയവിനിമയത്തിന് അപ്ലിക്കേഷനുകളെ അനുവദിക്കുന്നു." "നിങ്ങളുടെ സ്‌ക്രീൻ ലോക്ക് പ്രവർത്തനരഹിതമാക്കുക" "കീലോക്കും ഏതെങ്കിലും അനുബന്ധ പാസ്‌വേഡ് സുരക്ഷയും പ്രവർത്തനരഹിതമാക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഉദാഹരണത്തിന്, ഒരു ഇൻകമിംഗ് കോൾ സ്വീകരിക്കുമ്പോൾ ഫോൺ കീലോക്ക് പ്രവർത്തനരഹിതമാക്കുന്നു, കോൾ അവസാനിക്കുമ്പോൾ കീലോക്ക് വീണ്ടും പ്രവർത്തനക്ഷമമാകുന്നു." - "സ്‌ക്രീൻ ലോക്ക് സങ്കീർണ്ണത അഭ്യർത്ഥിക്കുക" - "സ്ക്രീൻ ലോക്കിന്റെ സാധ്യമായ നീളവും തരവും സൂചിപ്പിക്കുന്ന, അതിന്റെ സങ്കീർണ്ണത നില (ഉയർന്നത്, ഇടത്തരം, കുറഞ്ഞത് അല്ലെങ്കിൽ ഒന്നുമില്ല) മനസിലാക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. സ്‌ക്രീൻ ലോക്ക് ഒരു പ്രത്യേക തലത്തിലേക്ക് അപ്ഡേറ്റ് ചെയ്യുന്ന ഉപയോക്താക്കൾക്ക് നിർദ്ദേശിക്കാനും ആപ്പിനാവും, മാത്രമല്ല ഉപയോക്താക്കൾക്ക് എളുപ്പത്തിൽ അവഗണിക്കാനും മറ്റൊന്നിലേക്ക് നാവിഗേറ്റ് ചെയ്യാനുമാവും. പ്ലെയിൻടെക്‌സ്‌റ്റിൽ സ്ക്രീൻ ലോക്ക് സംഭരിക്കപ്പെട്ടിട്ടില്ലെന്ന കാര്യം ശ്രദ്ധിക്കുക, അതിനാൽ ആപ്പിന് കൃത്യമായ പാസ്‌വേഡ് അറിയില്ല." + + + + "ബയോമെട്രിക് ഹാർ‌ഡ്‌വെയർ ഉപയോഗിക്കുക" "പരിശോധിച്ചുറപ്പിക്കുന്നതിനായി, ബയോമെട്രിക് ഹാർഡ്‌വെയർ ഉപയോഗിക്കാൻ ആപ്പിനെ അനുവദിക്കുക" "ഫിംഗർപ്രിന്റ് ഹാർഡ്‌വെയർ നിയന്ത്രിക്കുക" @@ -565,37 +576,59 @@ "ഉപയോഗിക്കാനായി, മുഖത്തിന്റെ ടെംപ്ലേറ്റുകൾ ചേർക്കാനും ഇല്ലാതാക്കാനുമുള്ള രീതികൾ അഭ്യർത്ഥിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു." "മുഖം തിരിച്ചറിയൽ ഹാർഡ്‌വെയർ ഉപയോഗിക്കുക" "പരിശോധിച്ചുറപ്പിക്കലിനായി മുഖം തിരിച്ചറിയൽ ഹാർഡ്‌വെയർ ഉപയോഗിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു" - "മുഖം പ്രോസസ്സ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക." - "മുഖം വളരെ തെളിച്ചമുള്ളതാണ്. കുറഞ്ഞ വെളിച്ചത്തിൽ ശ്രമിക്കുക." - "മുഖം വളരെ ഇരുണ്ടതാണ്. പ്രകാശ ലഭ്യത ഉറപ്പാക്കി ശ്രമിക്കുക." - "മുഖത്തിനടുത്തുനിന്ന് സെൻസർ അകലേയ്ക്ക് നീക്കുക." - "സെൻസർ മുഖത്തിനടുത്തേയ്ക്ക് കൊണ്ടുവരിക." - "സെൻസർ മുകളിലേക്ക് നീക്കുക." - "സെൻസർ താഴേയ്ക്ക് നീക്കുക." - "സെൻസർ വലത്തേയ്ക്ക് നീക്കുക." - "സെൻസർ ഇടത്തേയ്ക്ക് നീക്കുക." - "സെൻസറിലേക്ക് നോക്കുക." - "മുഖം കണ്ടെത്താനായില്ല." - "ഉപകരണം വളരെയധികം ഇളകുന്നു." + + + + + + + + + + + + + + + + + + + + + + + + "നിങ്ങളുടെ മുഖം വീണ്ടും എൻറോൾ ചെയ്യുക." - "മറ്റൊരു മുഖം തിരിച്ചറിഞ്ഞു." + + "വളരെയധികം സമാനത, നിങ്ങളുടെ പോസ് മാറ്റുക." - "അൽപ്പം കൂടി ക്യാമറയ്‌ക്ക് നേരെ നോക്കൂ." - "അൽപ്പം കൂടി ക്യാമറയ്‌ക്ക് നേരെ നോക്കൂ." + + + + "നിങ്ങളുടെ തല ലംബമായി നേരെയാക്കുക" - "നിങ്ങളുടെ മുഖം വ്യക്തമാക്കുക." + + + + "മുഖത്തിന്റെ ഹാർഡ്‌വെയർ ലഭ്യമല്ല." - "മുഖം നൽകേണ്ട സമയം കഴിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക." + + "മുഖം സൂക്ഷിക്കാനാവില്ല." "മുഖത്തിന്റെ പ്രവർത്തനം റദ്ദാക്കി." "മുഖം പരിശോധിച്ചുറപ്പിക്കൽ ഉപയോക്താവ് റദ്ദാക്കി." "നിരവധി തവണ ശ്രമിച്ചു. പിന്നീട് വീണ്ടും ശ്രമിക്കുക." "നിരവധി തവണ ശ്രമിച്ചു. മുഖം തിരിച്ചറിയൽ പ്രവർത്തനരഹിതമാക്കി." - "വീണ്ടും ശ്രമിക്കുക." - "ഒരു മുഖവും എൻറോൾ ചെയ്‌തിട്ടില്ല." - "ഈ ഉപകരണത്തിൽ മുഖം പരിശോധിച്ചുറപ്പിക്കാനുള്ള സെൻസറില്ല." + + + + + + "മുഖം %d" @@ -1213,9 +1246,16 @@ "%1$s തുറക്കുക" "%1$s സംരക്ഷിക്കാതെ അവസാനിപ്പിക്കും" "%1$s മെമ്മറി പരിധി കവിഞ്ഞു" + + "ഹീപ്പ് ഡംപ് ശേഖരിച്ചു. പങ്കിടാൻ ടാപ്പ് ചെയ്യുക" "ഹീപ്പ് ഡംപ് പങ്കിടണോ?" - "%1$s പ്രോസസ്സ് അതിന്റെ മെമ്മറി പരിധിയായ %2$s കവിഞ്ഞു. അതിന്റെ ഡവലപ്പറുമായി പങ്കിടാൻ ഒരു ഹീപ്പ് ഡംപ് നിങ്ങൾക്ക് ലഭ്യമാണ്. ശ്രദ്ധിക്കുക: ഈ ഹീപ്പ് ഡംപിൽ അപ്ലിക്കേഷന് ആക്‌സസ്സുള്ള ഏതെങ്കിലും സ്വകാര്യ വിവരങ്ങൾ അടങ്ങിയിരിക്കാം." + + + + + + "വാചകസന്ദേശത്തിനായി ഒരു പ്രവർത്തനം തിരഞ്ഞെടുക്കുക" "റിംഗർ വോളിയം" "മീഡിയ വോളിയം" @@ -1254,8 +1294,10 @@ "എല്ലാ നെറ്റ്‌വർക്കുകളും കാണാൻ ടാപ്പുചെയ്യുക" "കണക്റ്റുചെയ്യുക" "എല്ലാ നെറ്റ്‌വർക്കുകളും" - "%s നിർദ്ദേശിച്ച വൈഫൈ നെറ്റ്‌വർക്ക് ലഭ്യമാണ്" - "%s നിർദ്ദേശിച്ച നെറ്റ്‌വർക്കുകളിലേക്ക് കണക്‌റ്റ് ചെയ്യണോ?" + + + + "ഉവ്വ്" "ഇല്ല" "വൈഫൈ സ്വമേധയാ ഓണാകും" @@ -1267,9 +1309,14 @@ "നെറ്റ്‌വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക" - "വൈഫൈയ്ക്ക് ഇന്റർനെറ്റ് ആക്‌സസ് ഇല്ല" + + "ഓപ്ഷനുകൾക്ക് ടാപ്പുചെയ്യുക" "കണക്‌റ്റ് ചെയ്‌തു" + + + + "നിങ്ങളുടെ ഹോട്ട്‌സ്‌പോട്ട് ക്രമീകരണത്തിൽ വരുത്തിയ മാറ്റങ്ങൾ" "നിങ്ങളുടെ ഹോട്ട്‌സ്‌പോട്ട് ബാൻഡ് മാറി." "നിങ്ങളുടെ മുൻഗണനയനുസരിച്ചുള്ള, 5GHz മാത്രം എന്നത് ഈ ഉപകരണം പിന്തുണയ്ക്കുന്നില്ല. പകരം, 5GHz ബാൻഡ് ലഭ്യമാകുമ്പോൾ അത് ഉപയോഗിക്കും." @@ -1355,6 +1402,10 @@ "USB ഡീബഗ്ഗിംഗ് കണക്റ്റ് ചെയ്തു" "USB ഡീബഗ്ഗിംഗ് ഓഫാക്കാൻ ടാപ്പ് ചെയ്യുക" "USB ഡീബഗ്ഗുചെയ്യൽ പ്രവർത്തനരഹിതമാക്കാൻ തിരഞ്ഞെടുക്കുക." + + + + "USB പോർട്ടിൽ ദ്രാവകമോ പൊടിയോ കണ്ടെത്തി" "USB പോർട്ടർ സ്വയമേവ പ്രവർത്തനരഹിതമായി. കൂടുതലറിയാൻ ടാപ്പ് ചെയ്യുക." "USB പോർട്ട് ഇപ്പോൾ സുരക്ഷിതമായി ഉപയോഗിക്കാം" @@ -1906,8 +1957,6 @@ "ഔദ്യോഗിക പ്രൊഫൈൽ അൺലോക്കുചെയ്യാൻ ടാപ്പുചെയ്യുക" "%1$s എന്നതിലേക്ക് കണക്റ്റുചെയ്തു" "ഫയലുകൾ കാണുന്നതിന് ടാപ്പുചെയ്യുക" - "പിൻ ചെയ്യുക" - "അൺപിൻ ചെയ്യുക" "ആപ്പ് വിവരം" "−%1$s" "ഡെമോ ആരംഭിക്കുന്നു…" @@ -1998,6 +2047,22 @@ "ദിനചര്യ മോഡ് വിവരത്തെ കുറിച്ചുള്ള അറിയിപ്പ്" "സാധാരണയുള്ളതിലും നേരത്തെ ബാറ്ററിയുടെ ചാർജ് തീർന്നേക്കാം" "ബാറ്ററി ലൈഫ് വര്‍ദ്ധിപ്പിക്കാൻ, ബാറ്ററി ലാഭിക്കൽ സജീവമാക്കി" + + + + + + + + + + + + + + + + "ഫോള്‍ഡര്‍" "Android ആപ്പ്" "ഫയൽ" diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 101ef3532f3434630b2eef8c31b447c17c956ab0..6a6974d1a6d9da67cb4b70fa548a279670503902 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -141,8 +141,10 @@ "Wi-Fi дуудлага" "VoWifi" "Идэвхгүй" - "Wi-Fi давуу эрхтэй" - "Мобайл давуу эрхтэй" + + + + "Зөвхөн Wi-Fi" "{0}: дамжуулагдаагүй" "{0}: {1}" @@ -228,7 +230,8 @@ "Алдаа мэдээлэх" "Гаргах харилцан үйлдэл" "Дэлгэцийн зураг дарах" - "Алдааны тайлан авах" + + "Энэ таны төхөөрөмжийн одоогийн статусын талаарх мэдээллийг цуглуулах ба имэйл мессеж болгон илгээнэ. Алдааны мэдэгдлээс эхэлж илгээхэд бэлэн болоход хэсэг хугацаа зарцуулагдана тэвчээртэй байна уу." "Интерактив тайлан" "Үүнийг ихэнх тохиолдолд ашиглана уу. Энэ нь танд тайлангийн явцыг хянах, асуудлын талаар дэлгэрэнгүй мэдээлэл оруулах болон дэлгэцийн агшин авахыг зөвшөөрнө. Мөн тайлагнахад урт хугацаа шаарддаг таны бага ашигладаг зарим хэсгийг алгасах болно." @@ -281,9 +284,12 @@ "Байршил" "энэ төхөөрөмжийн байршилд хандалт хийх" "<b>%1$s</b>-д энэ төхөөрөмжийн байршилд хандахыг зөвшөөрөх үү?" - "Та аппыг зөвхөн хэрэглэж байгаа үед апп нь байршилд хандах болно." - "<b>%1$s</b>-д энэ төхөөрөмжийн байршилд хандахыг байнга зөвшөөрөх үү?" - "Та энэ аппыг хэрэглээгүй байсан ч апп нь байршилд үргэлж хандах болно." + + + + + + "Хуанли" "Хуанли руу хандах" "<b>%1$s</b>-д таны календарьт хандахыг зөвшөөрөх үү?" @@ -316,7 +322,10 @@ "<b>%1$s</b>-д таны хөгжимд хандахыг зөвшөөрөх үү?" "Зураг & видео" "зураг & видеондоо хандах" - "<b>%1$s</b>-д таны зураг, видео болон шошголсон байршилд хандахыг зөвшөөрөх үү?" + + + + "Цонхны агуулгыг авах" "Таны харилцан үйлчлэх цонхны контентоос шалгах." "Хүрч танихыг асаах" @@ -509,8 +518,10 @@ "Апп нь Ойролцоо Талбарын Холболт(NFC) таг, карт, болон уншигчтай холбогдох боломжтой." "дэлгэцний түгжээг идэвхгүй болгох" "Апп нь түгжээ болон бусад холбоотой нууц үгийн аюулгүй байдлыг идэвхгүй болгох боломжтой. Жишээ нь бол утас нь дуудлага ирэх үед түгжээг идэвхгүй болгох ба дуудлага дуусахад буцаан идэвхтэй болгодог." - "дэлгэцийн түгжээний төвөгтэй байдлын хүсэлт тавих" - "Аппад дэлгэцийн түгжээний боломжтой уртын хэмжээ болон төрлийг заадаг дэлгэцийн түгжээний төвөгтэй байдлын түвшнийг (өндөр, дундаж, бага эсвэл байхгүй) мэдэж авахыг зөвшөөрдөг. Түүнчлэн, апп хэрэглэгчдэд дэлгэцийн түгжээг тодорхой түвшинд шинэчлэхийг санал болгодог хэдий ч хэрэглэгч үүнийг чөлөөтэй үл хэрэгсэж, орхих боломжтой. Дэлгэцийн түгжээг ил бичвэрээр хадгалдаггүй тул апп тодорхой нууц үгийг мэддэггүй болохыг анхаарна уу." + + + + "биометрийн техник хангамжийг ашиглах" "Aппад биометрийн техник хангамжийг баталгаажуулалтад ашиглахыг зөвшөөрдөг" "хурууны хээний програм хангамжийг удирдах" @@ -565,37 +576,59 @@ "Аппад царайны загварыг ашиглахын тулд нэмэх эсвэл устгах аргыг идэвхжүүлэхийг зөвшөөрдөг." "царай танилтын техник хангамжийг ашиглах" "Аппад царай танилтын техник хангамжийг баталгаажуулалтад ашиглахыг зөвшөөрдөг" - "Царайг таньж чадсангүй. Дахин оролдоно уу." - "Царай хэт цайвар байна. Гэрэл багатай газар оролдож үзнэ үү." - "Царай хэт бараан байна. Гэрэлтэй газар туршиж үзнэ үү." - "Мэдрэгчийг царайнаас холдуулна уу." - "Mэдрэгчийг царайтай ойртуулна уу." - "Мэдрэгчийг дээшлүүлнэ үү." - "Mэдрэгчийг доошлуулна уу." - "Мэдрэгчийг баруун тийш болгоно уу." - "Мэдрэгчийг зүүн тийш болгоно уу." - "Мэдрэгч рүү харна уу." - "Царай олдсонгүй." - "Хэт их хөдөлгөөнтэй байна." + + + + + + + + + + + + + + + + + + + + + + + + "Нүүрээ дахин бүртгүүлнэ үү." - "Өөр нүүр илрүүллээ." + + "Хэт адилхан байгаа тул байрлалаа өөрчилнө үү." - "Камер луу аль болох эгц харна уу." - "Камер луу аль болох эгц харна уу." + + + + "Толгойгоо босоо чиглэлд тэгшилнэ үү." - "Нүүрээ ил гаргана уу." + + + + "Царайны техник хангамж боломжгүй байна." - "Царай таниулах хугацаа дууслаа. Дахин оролдоно уу." + + "Царайг хадгалах боломжгүй байна." "Царайны үйл ажиллагааг цуцаллаа." "Хэрэглэгч царайгаар баталгаажуулахыг цуцалсан байна." "Хэт олон удаа оролдлоо. Дараа дахин оролдоно уу." "Хэт олон удаа оролдлоо. Царай танилтыг идэвхгүй болголоо." - "Дахин оролдоно уу." - "Бүртгүүлсэн царай алга." - "Энэ төхөөрөмжид нүүр баталгаажуулах мэдрэгч алга." + + + + + + "Царай %d" @@ -1213,9 +1246,16 @@ "%1$s-г нээх" "%1$s-г хадгалахгүйгээр хаана" "%1$s санах ойн хязгаараас давсан" + + "Багтаамж хэтэрсэн байна. Хуваалцахын тулд товшино уу." "Хэт их хуримтлагдсан мэдээллийг хуваалцах уу?" - "Энэ үйл явц %1$s нь үйл ажиллагааны санах ойн хязгаар болох %2$s хэмжээг давсан байна. Та хэт их хуримтлагдсан мэдээллийг тэдгээрийн өөрсдийнх нь хөгжүүлэгчтэй хуваалцах боломжтой. Болгоомжтой байгаарай: энэхүү хэт их хуримтлагдсан мэдээлэлд аппликейшнаас нэвтрэх боломжтой таны хувийн мэдээлэл агуулагдсан байж болно." + + + + + + "Текст илгээх үйлдлийг сонгох" "Хонхны аяны хэмжээ" "Медиа дууны түвшин" @@ -1254,8 +1294,10 @@ "Бүх сүлжээг харахын тулд товшино уу" "Холбогдох" "Бүх сүлжээ" - "%s-н санал болгож буй Wi-Fi сүлжээ боломжтой байна" - "Та %s-н санал болгож буй сүлжээнд холбогдох уу?" + + + + "Тийм" "Үгүй" "Wi‑Fi автоматаар асна" @@ -1267,9 +1309,14 @@ "Сүлжээнд нэвтэрнэ үү" - "Wi-Fi-д интернет хандалт алга" + + "Сонголт хийхийн тулд товшино уу" "Холбогдсон" + + + + "Таны сүлжээний цэгийн тохиргооны өөрчлөлт" "Таны сүлжээний цэгийн зурвасыг өөрчилсөн." "Энэ төхөөрөмж таны \"зөвхөн 5Гц\" гэсэн давуу сонголтыг дэмждэггүй. Үүний оронд энэ төхөөрөмж 5Гц зурвасыг боломжтой үед нь ашиглах болно." @@ -1354,6 +1401,10 @@ "USB дебаг холбогдсон" "USB дебаг хийхийг унтраахын тулд товшино уу" "USB дебаг хийхийг идэвхгүй болгох бол сонгоно уу." + + + + "USB порт дээрх шингэн зүйл эсвэл бохирдол" "USB портыг автоматаар идэвхгүй болгосон байна. Дэлгэрэнгүй мэдээлэл авахын тулд товшино уу." "USB портыг ашиглахад аюулгүй байна" @@ -1905,8 +1956,6 @@ "Ажлын профайлын түгжээг тайлахын тулд дарна уу" "%1$s-д холбогдсон" "Файлыг үзэхийн тулд дарна уу" - "PIN" - "Unpin" "Апп-н мэдээлэл" "−%1$s" "Жишээг эхлүүлж байна…" @@ -1997,6 +2046,22 @@ "Хэвшлийн горимын мэдээллийн мэдэгдэл" "Батарей ихэвчлэн цэнэглэдэг хугацаанаас өмнө дуусаж болзошгүй" "Батарейны ажиллах хугацааг уртасгахын тулд Батарей хэмнэгчийг идэвхжүүллээ" + + + + + + + + + + + + + + + + "Фолдер" "Андройд апп" "Файл" diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 98b64e951abf8414b58f0ad3693050b8e320ffd6..61739ef76742ea014fa842deef87c482db186615 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -141,8 +141,10 @@ "Panggilan Wi-Fi" "VoWifi" "Mati" - "Wi-Fi diutamakan" - "Mudah alih diutamakan" + + + + "Wi-Fi sahaja" "{0}: Tidak dimajukan" "{0}: {1}" @@ -228,7 +230,8 @@ "Laporan pepijat" "Tamatkan sesi" "Tangkapan skrin" - "Ambil laporan pepijat" + + "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." "Laporan interaktif" "Gunakan laporan ini dalam kebanyakan keadaan. Anda boleh menjejak kemajuan laporan, memasukkan butiran lanjut tentang masalah tersebut dan mengambil tangkapan skrin. Laporan ini mungkin meninggalkan beberapa bahagian yang kurang digunakan, yang mengambil masa lama untuk dilaporkan." @@ -281,9 +284,12 @@ "Lokasi" "mengakses lokasi peranti ini" "Benarkan <b>%1$s</b> mengakses lokasi peranti ini?" - "Apl ini hanya dapat mengakses lokasi semasa anda menggunakan apl tersebut." - "Sentiasa benarkan <b>%1$s</b> mengakses lokasi peranti ini?" - "Apl ini akan dapat mengakses lokasi pada sepanjang masa, meskipun apabila anda tidak menggunakan apl tersebut." + + + + + + "Kalendar" "mengakses kalendar" "Benarkan <b>%1$s</b> mengakses kalendar anda?" @@ -316,7 +322,10 @@ "Benarkan <b>%1$s</b> mengakses muzik anda?" "Foto & video" "akses foto & video anda" - "Benarkan <b>%1$s</b> mengakses foto dan video anda, termasuk lokasi yang ditandai?" + + + + "Dapatkan kembali kandungan tetingkap" "Periksa kandungan tetingkap yang berinteraksi dengan anda." "Hidupkan Jelajah melalui Sentuhan" @@ -509,8 +518,10 @@ "Membenarkan apl berkomunikasi dengan teg, kad dan pembaca Komunikasi Medan Dekat (NFC)." "lumpuhkan kunci skrin anda" "Membenarkan apl melumpuhkan kunci kekunci dan sebarang keselamatan kata laluan yang berkaitan. Sebagai contoh, telefon melumpuhkan kunci kekunci apabila menerima panggilan telefon masuk kemudian mendayakan semula kunci kekunci apabila panggilan selesai." - "minta kerumitan kunci skrin" - "Membenarkan apl mengetahui tahap kerumitan kunci skrin (tinggi, sederhana, rendah atau tiada) yang menunjukkan julat panjang dan jenis kunci skrin yang berkemungkinan. Apl juga boleh mencadangkan kepada pengguna supaya mengemas kini kunci skrin pada tahap tertentu namun pengguna boleh mengabaikan dan menavigasi keluar dengan bebas. Sila ambil perhatian bahawa kunci skrin tidak disimpan dalam teks biasa, maka apl tidak mengetahui kata laluan yang tepat." + + + + "gunakan perkakasan biometrik" "Membenarkan apl menggunakan perkakasan biometrik untuk pengesahan" "urus perkakasan cap jari" @@ -565,37 +576,59 @@ "Membenarkan apl menggunakan kaedah untuk menambahkan dan memadamkan templat wajah untuk digunakan." "gunakan perkakasan pengesahan wajah" "Membenarkan apl menggunakan perkakasan pengesahan wajah untuk pengesahan" - "Tidak dapat memproses wajah. Sila cuba lagi." - "Wajah terlalu cerah. Cuba dlm cahaya lebih rendah." - "Wajah terlalu gelap. Sila buka sumber cahaya." - "Sila alihkan penderia lebih jauh dari wajah." - "Sila dekatkan penderia ke wajah." - "Sila alihkan penderia ke kedudukan lebih tinggi." - "Sila alihkan penderia ke kedudukan lebih rendah." - "Sila alihkan penderia ke kanan." - "Sila alihkan penderia ke kiri." - "Sila lihat penderia." - "Tiada wajah dikesan." - "Terlalu banyak gerakan." + + + + + + + + + + + + + + + + + + + + + + + + "Sila daftarkan semula wajah anda." - "Wajah yang berbeza dikesan." + + "Terlalu serupa, sila ubah lagak gaya anda." - "Sila lihat terus pada kamera." - "Sila lihat terus pada kamera." + + + + "Sila tegakkan kepala anda." - "Sila dedahkan wajah anda." + + + + "Perkakasan wajah tidak tersedia." - "Tamat masa wajah dicapai. Cuba lagi." + + "Wajah tidak dapat disimpan." "Pengendalian wajah dibatalkan." "Pengesahan wajah dibatalkan oleh pengguna." "Terlalu banyak percubaan. Cuba sebentar lagi." "Terlalu banyak percubaan. Pengesahan wajah dilumpuhkan." - "Cuba lagi." - "Tiada wajah didaftarkan." - "Peranti ini tiada penderia pengesahan wajah." + + + + + + "Wajah %d" @@ -1213,9 +1246,16 @@ "Buka %1$s" "%1$s akan ditutup tanpa menyimpan" "%1$s melebihi had memori" + + "Longgokan timbunan dikumpulkan. Ketik untuk berkongsi." "Kongsikan longgokan timbunan?" - "Proses %1$s telah melebihi had memori proses sebanyak %2$s. Longgokan timbunan tersedia untuk anda kongsikan dengan pembangun aplikasi. Sila berhati-hati: longgokan timbunan ini boleh mengandungi sebarang maklumat peribadi anda yang boleh diakses oleh aplikasi itu." + + + + + + "Pilih tindakan untuk teks" "Kelantangan pendering" "Kelantangan media" @@ -1254,8 +1294,10 @@ "Ketik untuk melihat semua rangkaian" "Sambung" "Semua rangkaian" - "Rangkaian Wi‑Fi yang dicadangkan oleh %s tersedia" - "Adakah anda mahu menyambung ke rangkaian yang dicadangkan oleh %s?" + + + + "Ya" "Tidak" "Wi‑Fi akan dihidupkan secara automatik" @@ -1267,9 +1309,14 @@ "Log masuk ke rangkaian" - "Wi-Fi tiada akses Internet" + + "Ketik untuk mendapatkan pilihan" "Disambungkan" + + + + "Perubahan kepada tetapan tempat liputan anda" "Jalur tempat liputan anda telah berubah." "Peranti ini tidak menyokong pilihan anda untuk 5GHz sahaja. Sebaliknya, peranti ini akan menggunakan jalur 5GHz apabila tersedia." @@ -1354,6 +1401,10 @@ "Penyahpepijatan USB disambungkan" "Ketik untuk mematikan penyahpepijatan USB" "Pilih untuk melumpuhkan penyahpepijatan USB." + + + + "Cecair atau serpihan dalam port USB" "Port USB dilumpuhkan secara automatik. Ketik untuk mengetahui lebih lanjut." "Selamat untuk menggunakan port USB" @@ -1396,8 +1447,7 @@ "Sediakan" "Tanggalkan" "Teroka" - - + "Tukar output" "%s tiada" "Masukkan peranti sekali lagi" "Mengalihkan %s" @@ -1906,8 +1956,6 @@ "Ketik utk membuka profil kerja" "Disambungkan ke %1$s" "Ketik untuk melihat fail" - "Semat" - "Nyahsemat" "Maklumat apl" "−%1$s" "Memulakan tunjuk cara…" @@ -1998,6 +2046,22 @@ "Pemberitahuan maklumat Mod Rutin" "Bateri mungkin habis sebelum pengecasan biasa" "Penjimat Bateri diaktifkan untuk memanjangkan hayat bateri" + + + + + + + + + + + + + + + + "Folder" "Aplikasi Android" "Fail" diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 09e963f8745fdefbee81604a2b86c6ea9288a1bd..c45cf0505dfb304ef792db8a120de411d07c38be 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -141,8 +141,10 @@ "WiFi ခေါ်ဆိုမှု" "VoWifi" "ပိတ်ထားရသည်" - "ဝိုင်ဖိုင်အား ပိုနှစ်သက်သော" - "မိုဘိုင်းကို အသုံးပြုလိုပါသည်" + + + + "ကြိုးမဲ့အင်တာနက် သာလျှင်" "{0}: ထပ်ဆင့်မပို့နိုင်ပါ" "{0}: {1}" @@ -228,7 +230,8 @@ "အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်း" "သတ်မှတ်ပေးထားသည့်အချိန် ပြီးဆုံးပြီ" "ဖန်သားပြင်ဓာတ်ပုံ" - "ချွတ်ယွင်းမှုမှတ်တမ်း ယူခြင်း" + + "သင့်ရဲ့ လက်ရှိ စက်အခြေအနေ အချက်အလက်များကို အီးမေးလ် အနေဖြင့် ပေးပို့ရန် စုဆောင်းပါမည်။ အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်းမှ ပေးပို့ရန် အသင့်ဖြစ်သည်အထိ အချိန် အနည်းငယ်ကြာမြင့်မှာ ဖြစ်သဖြင့် သည်းခံပြီး စောင့်ပါရန်" "လက်ငင်းတုံ့ပြန်နိုင်သည့် အစီရင်ခံချက်" "အခြေအနေတော်တော်များများတွင် ၎င်းကိုအသုံးပြုပါ။ ၎င်းသည် အစီရင်ခံစာကို မှတ်သားခြင်း၊ ပြဿနာအကြောင်း နောက်ထပ်အသေးစိတ်များကို ထည့်သွင်းခြင်းနှင့် မျက်နှာပြင်ပုံဖမ်းယူခြင်းတို့ကို ပြုလုပ်ခွင့်ပေးပါသည်။ ပေးပို့ရန် အလွန်ကြာပြီး အသုံးပြုခြင်းနည်းပါးသည့်အပိုင်းကို ၎င်းက ချန်ခဲ့နိုင်ပါသည်။" @@ -281,9 +284,12 @@ "တည်နေရာ" "ဤစက်ပစ္စည်း၏ တည်နေရာကို ရယူရန်" "<b>%1$s</b> အား ဤစက်ပစ္စည်း၏တည်နေရာကို သုံးခွင့်ပေးလိုပါသလား။" - "အက်ပ်ကိုအသုံးပြုသည့် အချိန်တွင်သာ ၎င်းကတည်နေရာကို အသုံးပြုခွင့်ရပါမည်။" - "<b>%1$s</b> အား ဤစက်ပစ္စည်း၏တည်နေရာကို အမြဲ အသုံးပြုခွင့်ပေးလိုပါသလား။" - "သင် အက်ပ်ကိုအသုံးပြုမနေသော်လည်း ၎င်းကတည်နေရာကို အမြဲ အသုံးပြုခွင့်ရနေပါမည်။" + + + + + + "ပြက္ခဒိန်" "သင့်ပြက္ခဒိန်အား ဝင်ရောက်သုံးရန်" "<b>%1$s</b> အား သင်၏ပြက္ခဒိန်ကို သုံးခွင့်ပေးလိုပါသလား။" @@ -316,7 +322,10 @@ "<b>%1$s</b> အား သင့်တေးဂီတကို ဝင်ခွင့်ပေးလိုပါသလား။" "ဓာတ်ပုံများနှင့် ဗီဒီယိုများ" "သင့်ဓာတ်ပုံနှင့် ဗီဒီယိုများသို့ ဝင်သည်" - "တဂ်လုပ်ထားသည့် တည်နေရာများအပါအဝင် သင့်ဓာတ်ပုံနှင့် ဗီဒီယိုများကို <b>%1$s</b> အား ဝင်ကြည့်ခွင့်ပေးလိုပါသလား။" + + + + "ဝင်းဒိုးတွင် ပါရှိသည်များကို ပြန်လည်ရယူရန်" "သင်အသုံးပြုနေသော ဝင်းဒိုးတွင် ပါရှိသည်များကို ကြည့်ရှုစစ်ဆေးသည်။" "တို့ထိခြင်းဖြင့် ရှာဖွေမှုကို ဖွင့်ရန်" @@ -509,8 +518,10 @@ "အက်ပ်အား တာတို စက်ကွင်း ဆက်သွယ်ရေး (NFC) တဲဂ်များ၊ ကဒ်များ နှင့် ဖတ်ကြသူတို့နှင့် ဆက်သွယ်ပြောဆိုခွင့် ပြုသည်။" "ဖန်သားပြင် သော့ချခြင်းအား မလုပ်နိုင်အောင် ပိတ်ရန်" "အပလီကေးရှင်းအား သော့ချခြင်းနှင့် သက်ဆိုင်ရာ စကားဝှက်သတ်မှတ်ခြင်းများအား မသုံးနိုင်အောင် ပိတ်ခြင်းကို ခွင့်ပြုရန်။ ဥပမာ ဖုန်းလာလျှင် သော့ပိတ်ခြင်း ပယ်ဖျက်ခြင်း၊ ဖုန်းပြောပြီးလျှင် သော့ကို အလိုအလျောက် ပြန်ပိတ်ခြင်း" - "ဖုန်းမျက်နှာပြင် လော့ခ်ချရန် ရှုပ်ထွေးမှုအဆင့် တောင်းခံခြင်း" - "ဖုန်းမျက်နှာပြင်လော့ခ်၏ ရှုပ်ထွေးမှုအဆင့် (မြင့်၊ အလယ်အလတ်၊ နိမ့် သို့မဟုတ် မရှိ) အား လေ့လာရန် အက်ပ်ကို ခွင့်ပြုသည်။ ၎င်းက သတ်မှတ်ထားနိုင်သော ဖုန်းမျက်နှာပြင်လော့ခ်၏ စာလုံးရေနှင့် အမျိုးအစားကို ညွှန်ပြပေးသည်။ အသုံးပြုသူများအနေနှင့် ဖုန်းမျက်နှာပြင်လော့ခ်ကို အတိုင်းအတာတစ်ခုအထိ အဆင့်မြှင့်ရန် အက်ပ်က အကြံပြုနိုင်သည်။ သို့သော်လည်း အသုံးပြုသူများက ၎င်းကို ဂရုပြုမနေဘဲ လွတ်လပ်စွာ ကြည့်ရှုနိုင်ပါသည်။ ဖုန်းမျက်နှာပြင်လော့ခ်ကို စာသားအတိုင်း သိမ်းမထားသဖြင့် အက်ပ်သည် စကားဝှက်အစစ်ကို မသိနိုင်ကြောင်း သတိပြုပါ။" + + + + "ဇီဝဗေဒဆိုင်ရာ အချက်အလက်သုံး ကွန်ပျူတာဆိုင်ရာ စက်ပစ္စည်းကို အသုံးပြုရန်" "အထောက်အထားစိစစ်ခြင်းအတွက် ဇီဝဗေဒဆိုင်ရာ သတင်းအချက်အလက်များသုံးသည့် ကွန်ပျူတာဆိုင်ရာ စက်ပစ္စည်းကို အသုံးပြုရန် အက်ပ်ကို ခွင့်ပြုသည်" "လက်ဗွေရာပစ္စည်းကို စီမံမည်" @@ -565,37 +576,59 @@ "အသုံးပြုရန်အတွက် မျက်နှာပုံစံထည့်ရန် (သို့) ဖျက်ရန်နည်းလမ်းကို အက်ပ်အား သုံးခွင့်ပြုသည်။" "မျက်နှာအထောက်အထားစိစစ်ခြင်း စက်ပစ္စည်းကို သုံးပါ" "အထောက်အထားစိစစ်ရန်အတွက် ဤအက်ပ်အား မျက်နှာအထောက်အထားစိစစ်ခြင်း စက်ပစ္စည်းကိုသုံးခွင့်ပြုသည်" - "မျက်နှာကို မမှတ်မိပါ။ ထပ်လုပ်ကြည့်ပါ။" - "မျက်နှာအလွန်လင်းနေသည်။ အလင်းလျှော့ပြီး စမ်းကြည့်ပါ။" - "မျက်နှာအလွန်မှောင်နေသည်။ ပြတင်းပေါက်ဖွင့်လိုက်ပါ။" - "အာရုံခံကိရိယာကို မျက်နှာနှင့် ခွာလိုက်ပါ။" - "အာရုံခံကိရိယာကို မျက်နှာအနီး ရွှေ့လိုက်ပါ။" - "အာရုံခံကိရိယာကို ပိုမြင့်အောင်ထားပါ။" - "အာရုံခံကိရိယာကို ပိုနိမ့်အောင်ထားပါ။" - "အာရုံခံကိရိယာကို ညာဘက်သို့ ရွှေ့ပါ။" - "အာရုံခံကိရိယာကို ဘယ်ဘက်သို့ ရွှေ့ပါ။" - "အာရုံခံကိရိယာကို ကြည့်ပါ။" - "မျက်နှာတစ်ခုမျှ မတွေ့ပါ။" - "လှုပ်ရှားမှု အလွန်များနေသည်။" + + + + + + + + + + + + + + + + + + + + + + + + "သင့်မျက်နှာကို ပြန်စာရင်းသွင်းပါ။" - "မတူသည့် မျက်နှာ တွေ့ရှိထားသည်။" + + "ဆင်တူနေသည်၊ အမူအရာ ပြောင်းပါ။" - "ကင်မရာကို တည့်တည့်ကြည့်ပါ။" - "ကင်မရာကို တည့်တည့်ကြည့်ပါ။" + + + + "ခေါင်းမတ်မတ်ထားပါ။" - "မျက်နှာ ဖော်ထားပါ။" + + + + "မျက်နှာ စက်ပစ္စည်း မရနိုင်ပါ။" - "မျက်နှာ သက်တမ်းကုန်သွားပါပြီ။ ထပ်စမ်းကြည့်ပါ။" + + "မျက်နှာကို သိမ်း၍မရပါ။" "မျက်နှာ ဆောင်ရွက်ခြင်းကို ပယ်ဖျက်လိုက်ပါပြီ။" "အသုံးပြုသူက မျက်နှာအထောက်အထားစိစစ်မှု မလုပ်တော့ပါ။" "အကြိမ်များစွာ စမ်းပြီးပါပြီ။ နောက်မှထပ်စမ်းပါ။" "အကြိမ်များစွာ စမ်းပြီးပါပြီ။ ပိတ်လိုက်ပါပြီ။" - "ထပ်စမ်းကြည့်ပါ။" - "စာရင်းသွင်းထားသည့် မျက်နှာတစ်ခုမျှ မရှိပါ။" - "ဤစက်တွင် မျက်နှာအထောက်အထားစိစစ်ခြင်း အာရုံခံကိရိယာမရှိပါ။" + + + + + + "မျက်နှာ %d" @@ -1213,9 +1246,16 @@ "%1$s ကို ဖွင့်ရန်" "မသိမ်းဘဲ %1$s ကို ပိတ်လိုက်ပါမည်" "%1$s သိမ်းထားနိုင်မှု အကန့်အသတ် ကျော်လွန်နေ" + + "သိမ်းဆည်းနိုင်မှု ပမာဏကျော်လွန်သွားပါပြီ။ မျှဝေရန် တို့ပါ။" "အရေးပေါ် သိမ်းထားပေးမှု ကို မျှဝေမလား။" - "%1$s လုပ်ဆောင်နိုင်မှုသည် %2$sရှိသည့် သိမ်းထားနိုင်မှု ပမာဏထက်ကျော်လွန်သွားသည်။ စက်လုပ်ဆောင်ရည်မြင့်တင်ပေးသူနှင့် မျှဝေမှုလုပ်ရန် အရေးပေါ်သိမ်းထားပေးမှု ရမည်။" + + + + + + "စာတိုအတွက် လုပ်ဆောင်ချက် ရေးပါ" "ဖုန်းမြည်သံအတိုးအကျယ်" "မီဒီယာအသံအတိုးအကျယ်" @@ -1254,8 +1294,10 @@ "ကွန်ရက်အားလုံးကို ကြည့်ရန် တို့ပါ" "ချိတ်ဆက်ရန်" "ကွန်ရက်အားလုံး" - "%s အကြံပြုထားသော Wi‑Fi ကွန်ရက်ကို ရရှိနိုင်ပါသည်" - "%s အဆိုပြုထားသော ကွန်ရက်များသို့ ချိတ်ဆက်လိုပါသလား။" + + + + "Yes" "No" "Wi‑Fi ကို အလိုအလျောက်​ ပြန်ဖွင့်ပေးလိမ့်ပါမည်" @@ -1267,9 +1309,14 @@ "ကွန်ယက်သို့ လက်မှတ်ထိုးဝင်ရန်" - "Wi-Fi တွင် အင်တာနက်ချိတ်ဆက်မှု မရှိပါ" + + "အခြားရွေးချယ်စရာများကိုကြည့်ရန် တို့ပါ" "ချိတ်ဆက်ထားသည်" + + + + "သင်၏ဟော့စပေါ့ ဆက်တင်များ ပြောင်းလဲမှု" "သင်၏ ဟော့စပေါ့လိုင်း ပြောင်းသွားပါပြီ။" "ဤစက်ပစ္စည်းသည် သင်၏ 5GHz သီးသန့်ရွေးချယ်မှုအတွက် ပံ့ပိုးမထားပါ။ ၎င်းအစား ဤစက်ပစ္စည်းသည် ရနိုင်သည့်အခါ 5GHz လိုင်းကို သုံးသွားပါမည်။" @@ -1355,6 +1402,10 @@ "USB အမှားရှာပြင်စနစ် ချိတ်ဆက်ထားသည်" "USB အမှားရှာပြင်ခြင်းကို ပိတ်ရန် တို့ပါ" "USB ဖြင့် အမှားရှာပြင်ခြင်းကို ပိတ်ရန် ရွေးပါ။" + + + + "USB ပို့တ်တွင် အရည် သို့မဟုတ် အမှိုက်စ ရှိနေသည်" "USB ပို့တ်ကို အလိုအလျောက် ပိတ်ထားသည်။ ပိုမိုလေ့လာရန် တို့ပါ။" "USB ပို့တ်ကို စိတ်ချစွာ သုံးနိုင်ပါပြီ" @@ -1906,8 +1957,6 @@ "သင့်အလုပ်ပရိုဖိုင်ကို သော့ဖွင့်ရန် တို့ပါ" "%1$s ချိတ်ဆက်ထားသည်" "ဖိုင်များကိုကြည့်ရန် တို့ပါ" - "တွဲပါ" - "ဖြုတ်ပါ" "အက်ပ်အချက်အလက်" "−%1$s" "သရုပ်ပြချက်ကို စတင်နေသည်…" @@ -1998,6 +2047,22 @@ "ပုံမှန်မုဒ်အတွက် အချက်အလက်ပြသည့် အကြောင်းကြားချက်" "ပုံမှန်အားသွင်းမှုမပြုလုပ်မီ ဘက်ထရီကုန်သွားနိုင်သည်" "ဘက်ထရီသက်တမ်းကို တိုးမြှင့်ရန် \'ဘက်ထရီအားထိန်း\' စတင်ပြီးပါပြီ" + + + + + + + + + + + + + + + + "ဖိုင်တွဲ" "Android အပလီကေးရှင်း" "ဖိုင်" diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 1aced28e61a755312d9cac5e81c3ceadf6ca42ad..02efec65ef352aed86e234609e8b6572cf030594 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -141,8 +141,10 @@ "Wi-Fi-anrop" "VoWifi" "Av" - "Wi-Fi er foretrukket" - "Først-på-mobil" + + + + "Bare Wi-Fi" "{0}: Ikke viderekoblet" "{0}: {1}" @@ -228,7 +230,8 @@ "Feilrapport" "Avslutt økten" "Skjermdump" - "Utfør feilrapport" + + "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." "Interaktiv rapport" "Bruk dette alternativet i de fleste tilfeller. Da kan du spore fremgangen for rapporten, skrive inn flere detaljer om problemet samt ta skjermdumper. Noen deler som tar lang tid å behandle, blir kanskje utelatt." @@ -281,9 +284,12 @@ "Posisjon" "få tilgang til enhetens plassering" "Vil du gi <b>%1$s</b> tilgang til denne enhetens posisjon?" - "Appen får bare tilgang til posisjonen når du bruker appen." - "Vil du alltid gi <b>%1$s</b> tilgang til denne enhetens posisjon?" - "Appen får alltid tilgang til posisjonen, selv når du ikke bruker appen." + + + + + + "Kalender" "åpne kalenderen din" "Vil du gi <b>%1$s</b> tilgang til kalenderen din?" @@ -316,7 +322,10 @@ "Vil du gi <b>%1$s</b> tilgang til musikken din?" "Bilder og videoer" "få tilgang til bildene og videoene dine" - "Vil du gi <b>%1$s</b> tilgang til bildene og videoene dine, inkludert merkede steder?" + + + + "hente innhold i vinduer" "Appen analyserer innholdet i vinduer du samhandler med." "slå på berøringsutforsking" @@ -509,8 +518,10 @@ "Lar appen kommunisere med etiketter, kort og lesere som benytter NFC-teknologi." "deaktivere skjermlåsen" "Lar appen deaktivere tastelåsen og eventuell tilknyttet passordsikkerhet. Et eksempel er at telefonen deaktiverer tastelåsen når du mottar et innkommende anrop, og deretter aktiverer tastelåsen igjen når samtalen er ferdig." - "be om skjermlåsens kompleksitet" - "Lar appen lære skjermlåsens kompleksitetsnivå (høy, middels, lav eller ingen), som indikerer det mulige området for lengde og type skjermlås. Appen kan foreslå at brukeren oppdaterer skjermlåsen til et bestemt nivå, men brukere kan velge å ignorere dette og navigere bort. Vær oppmerksom på at skjermlåsen ikke er lagret i klartekst, så appen kan ikke se det nøyaktige passordet." + + + + "bruke biometrisk maskinvare" "Lar appen bruke biometrisk maskinvare til godkjenning" "administrere fingeravtrykkmaskinvare" @@ -565,37 +576,59 @@ "Lar appen bruke metoder for å legge til og slette ansiktmaler for bruk." "bruke maskinvare for ansiktsautentisering" "Lar appen bruke maskinvare for ansiktsautentisering til autentisering" - "Kunne ikke behandle ansiktet. Prøv igjen." - "Ansiktet er for lyst. Prøv med svakere lys." - "Ansiktet er for mørkt. Bruk en lyskilde." - "Flytt sensoren lengre vekk fra ansiktet." - "Hold sensoren nærmere ansiktet." - "Flytt sensoren opp." - "Flytt sensoren ned." - "Flytt sensoren til høyre." - "Flytt sensoren til venstre." - "Se på sensoren." - "Ingen ansikter er oppdaget." - "For mye bevegelse." + + + + + + + + + + + + + + + + + + + + + + + + "Registrer ansiktet ditt på nytt." - "Et annet ansikt er gjenkjent." + + "For likt – endre posituren din." - "Se mer direkte mot kameraet." - "Se mer direkte mot kameraet." + + + + "Rett hodet ditt vertikalt." - "Ikke skjul ansiktet ditt." + + + + "Maskinvare for ansikt er ikke tilgjengelig." - "Tidsavbrudd for ansikt er nådd. Prøv igjen." + + "Ansiktet kan ikke lagres." "Ansikt-operasjonen ble avbrutt." "Ansiktsautentiseringen ble avbrutt av brukeren." "For mange forsøk. Prøv igjen senere." "For mange forsøk. Ansiktsautentisering er slått av." - "Prøv igjen." - "Ingen ansikt er registrert." - "Denne enheten har ikke sensor for ansiktsautentisering." + + + + + + "Ansikt %d" @@ -1213,9 +1246,16 @@ "Åpne %1$s" "%1$s lukkes uten å lagre" "%1$s er over minnegrensen" + + "Minnedumpen er samlet inn. Trykk for å dele." "Vil du dele minnedumpen («heap dump»)?" - "%1$s-prosessen er %2$s over grensen for prosessminne. En minnedump («heap dump») er tilgjengelig for deling med utvikleren. Vær forsiktig – denne minnedumpen kan inneholde noen av de personlige opplysningene dine som appen har tilgang til." + + + + + + "Velg handling for tekst" "Ringetonevolum" "Medievolum" @@ -1254,8 +1294,10 @@ "Trykk for å se alle nettverkene" "Koble til" "Alle nettverk" - "Et Wi‑Fi-nettverkt som ble foreslått av %s, er tilgjengelig" - "Vil du koble til nettverkene som foreslås av %s?" + + + + "Ja" "Nei" "Wi‑Fi slås på automatisk" @@ -1267,9 +1309,14 @@ "Logg på nettverk" - "Wi-Fi har ikke Internett-tilgang" + + "Trykk for å få alternativer" "Tilkoblet" + + + + "Endres til innstillingene dine for Wi-Fi-soner" "Båndet ditt for Wi-Fi-sone er endret." "Denne enheten støtter ikke innstillingen din for bare 5 GHz. I stedet bruker enheten 5 GHz-båndet når det er tilgjengelig." @@ -1354,6 +1401,10 @@ "USB-feilsøking tilkoblet" "Trykk for å slå av USB-feilsøking" "Velg for å deaktivere USB-debugging." + + + + "Væske eller rusk i USB-porten" "USB-porten deaktiveres automatisk. Trykk for å finne ut mer." "Trygt å bruke USB-porten" @@ -1905,8 +1956,6 @@ "Trykk for å låse opp jobbprofilen" "Koblet til %1$s" "Trykk for å se filer" - "Fest" - "Løsne" "Info om appen" "−%1$s" "Starter demo …" @@ -1997,6 +2046,22 @@ "Varsel med informasjon om rutinemodus" "Batteriet kan gå tomt før den vanlige ladingen" "Batterisparing er aktivert for å forlenge batterilevetiden" + + + + + + + + + + + + + + + + "Mappe" "Android-app" "Fil" diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 806e520c844bc0c5c4b24e67ecc04f985feed273..b51f4b54147794d6ae1d13c16a1c8ab219b8f644 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -141,8 +141,10 @@ "WiFi कलिङ" "VoWifi" "निष्क्रिय" - "Wi-Fi मनपराइयो" - "रूचाइएको मोबाइल" + + + + "Wi-Fi मात्र" "{0}: अगाडि पठाइएको छैन" "{0}: {1}" @@ -228,7 +230,8 @@ "बग रिपोर्ट" "सत्रको अन्त्य गर्नुहोस्" "स्क्रिनसट" - "बग रिपोर्ट लिनुहोस्" + + "एउटा इमेल सन्देशको रूपमा पठाउनलाई यसले तपाईँको हालैको उपकरणको अवस्थाको बारेमा सूचना जम्मा गर्ने छ। बग रिपोर्ट सुरु गरेदेखि पठाउन तयार नभएसम्म यसले केही समय लिन्छ; कृपया धैर्य गर्नुहोस्।" "अन्तरक्रियामूलक रिपोर्ट" "बढी भन्दा बढी परिस्थितिहरूमा यसको प्रयोग गर्नुहोस्। यसले तपाईँलाई रिपोर्टको प्रगति ट्र्याक गर्न, समस्याका बारे थप विवरणहरू प्रविष्ट गर्न र स्क्रिनसटहरू लिन अनुमति दिन्छ। यसले रिपोर्ट गर्न लामो समय लिने केही कम प्रयोग हुने खण्डहरूलाई समावेश नगर्न सक्छ।" @@ -281,9 +284,12 @@ "स्थान" "यस यन्त्रको स्थानमाथि पहुँच" "<b>%1$s</b> लाई यो यन्त्रको स्थानमाथि पहुँच राख्न दिने हो?" - "तपाईं उक्त अनुप्रयोग प्रयोग गरिरहेका बेलामा त्यससँग स्थानमाथिको पहुँच रहने छ।" - "<b>%1$s</b> लाई यो यन्त्रको स्थानमाथि पहुँच राख्न दिने हो?" - "तपाईं उक्त अनुप्रयोग प्रयोग नगरिरहेका बेलामा पनि त्यससँग स्थानमाथिको पहुँच रहने छ।" + + + + + + "पात्रो" "तपाईंको पात्रोमाथि पहुँच गर्नुहोस्" "<b>%1$s</b> लाई आफ्नो पात्रोमाथि पहुँच राख्न दिने हो?" @@ -316,7 +322,10 @@ "<b>%1$s</b> लाई तपाईंको सङ्गीतमाथि पहुँच राख्न दिने हो?" "तस्बिर तथा भिडियोहरू" "आफ्नो तस्बिर & भिडियोहरूमाथि पहुँच राख्नुहोस्‌" - "<b>%1$s</b> लाई ट्याग गरिएका स्थानहरूलगायत तपाईंका तस्बिर तथा भिडियोहरूमाथि पहुँच गर्न दिने हो?" + + + + "विन्डो सामग्रीको पुनःबहाली गर्नुहोस्।" "तपाईँको अन्तरक्रिया भइरहेको विन्डोको सामग्रीको निरीक्षण गर्नुहोस्।" "छोएर गरिने खोजलाई सुचारु गर्नुहोस्" @@ -509,8 +518,10 @@ "अनुप्रयोगलाई नयाँ क्षेत्र संचार (NFC) ट्यागहरू, कार्डहरू र पाठकहरूसँग अन्तर्क्रिया गर्न अनुमति दिन्छ।" "स्क्रिन लक असक्षम पार्नुहोस्" "कुनै सम्बन्धित पासवर्ड सुरक्षा र किलकलाई असक्षम पार्न अनुप्रयोगलाई अनुमति दिन्छ। उदाहरणको लागि, अन्तर्गमन फोन कल प्राप्त गर्दा फोनले किलकलाई असक्षम पार्छ, त्यसपछि कल सकिएको बेला किलक पुनःसक्षम पार्छ।" - "स्क्रिन लकको जटिलतासम्बन्धी जानकारी प्राप्त गर्ने अनुरोध गर्नुहोस्" - "यसले अनुप्रयोगलाई स्क्रिन लकको जटिलताको स्तर (उच्च, मध्यम, न्यून वा कुनै पनि होइन) थाहा पाउने अनुमति दिन्छ जसले स्क्रिन लकको लम्बाइको सम्भावित दायरा र त्यसको प्रकारलाई जनाउँछ। यसै गरी, यो अनुप्रयोगले प्रयोगकर्ताहरूलाई स्क्रिन लक अद्यावधिक गर्ने सुझाव पनि दिन सक्छ तर प्रयोगकर्ताहरू उक्त सुझावको बेवास्ता गरी बाहिर निस्कन सक्छन्। स्क्रिन लकलाई सादा पाठको ढाँचामा भण्डारण नगरिने हुँदा यो अनुप्रयोगले वास्तविक पासवर्ड थाहा पाउँदैन भन्ने कुरा याद राख्नुहोस्।" + + + + "बायोमेट्रिक हार्डवेयर प्रयोग गर्नुहोस्‌" "अनुप्रयोगलाई प्रमाणीकरणका लागि बायोमेट्रिक हार्डवेयर प्रयोग गर्न अनुमति दिन्छ" "औठाछाप हार्डवेयर व्यवस्थापन गर्नुहोस्" @@ -565,37 +576,59 @@ "अनुप्रयोगलाई प्रयोगका लागि अनुहार टेम्प्लेट थप्न र मेटाउने तरिका आह्वान गर्न अनुमति दिन्छ।" "अनुहार प्रमाणिकरण हार्डवेयर प्रयोग गर्नुहोस्" "अनुप्रयोगलाई प्रमाणीकरणका लागि अनुहार प्रमाणीकरण हार्डवेयर प्रयोग गर्न अनुमति दिन्छ" - "अनुहार प्रशोधन गर्न सकिएन। कृपया फेरि प्रयास गर्नुहोस्।" - "अनुहारमा धेरै उज्यालो छ। कृपया कम प्रकाशमा प्रयास गर्नुहोस्।" - "अनुहार धेरै गाढा छ। कृपया प्रकाशको स्रोतको थप्नुहोस्‌।" - "कृपया अनुहारबाट सेन्सरलाई अलिक टाढा सार्नुहोस्।" - "कृपया अनुहारलाई सेन्सरको नजिक ल्याउनुहोस्।" - "कृपया सेन्सरलाई माथि सार्नुहोस्।" - "कृपया सेन्सरलाई तल सार्नुहोस्।" - "कृपया सेन्सरलाई दायाँतिर सार्नुहोस्।" - "कृपया सेन्सरलाई बायाँतिर सार्नुहोस्।" - "कृपया सेन्सरमा हेर्नुहोस्।" - "अनुहार पत्ता लागेन।" - "अत्यधिक हल्लियो।" + + + + + + + + + + + + + + + + + + + + + + + + "कृपया आफ्नो अनुहार पुनः दर्ता गर्नुहोस्।" - "अर्कै अनुहार पत्ता लाग्यो।" + + "अनुहार उस्तै भयो, कृपया आफ्नो पोज बदल्नुहोस्।" - "कृपया आफ्नो अनुहार अझ सीधा पारेर क्यामेरामा हेर्नु" - "कृपया आफ्नो अनुहार अझ सीधा पारेर क्यामेरामा हेर्नु" + + + + "कृपया आफ्नो अनुहार ठाडो रूपमा सीधा पार्नुहोस्।" - "कृपया आफ्नो अनुहार नढाक्नुहोस्।" + + + + "अनुहार पहिचान गर्ने हार्डवेयर उपलब्ध छैन।" - "अनुहारको समय सकिएको छ। फेरि प्रयास गर्नुहोस्‌।" + + "अनुहार भण्डारण गर्न सकिँदैन।" "अनुहार पहिचान रद्द गरियो।" "प्रयोगकर्ताले अनुहार प्रमाणीकरण रद्द गर्नु भयो।" "धेरैपटक प्रयासहरू भए। पछि फेरि प्रयास गर्नुहोस्‌।" "अत्यधिक धेरैपटक गलत प्रयासहरू भए। अनुहार प्रमाणिकरणलाई असक्षम पारियो।" - "फेरि प्रयास गर्नुहोस्।" - "कुनै पनि अनुहार दर्ता गरिएन।" - "यो यन्त्रमा अनुहार प्रमाणीकरण गर्ने कुनै पनि सेन्सर छैन।" + + + + + + "अनुहार %d" @@ -1219,9 +1252,16 @@ "%1$s खोल्नुहोस्" "%1$s सुरक्षित नगरिकनै बन्द हुने छ" "%1$s ले मेमोरी सीमा नाघ्यो" + + "हिप डम्प सङ्‍कलन गरियो, ट्याप गरेर आदान प्रदान गर्नुहोस्।" "हिप डम्प साझेदारी गर्नुहुन्छ?" - "प्रक्रिया %1$sले यसको प्रक्रिया मेमोरी सीमा %2$s नाघेको छ। तपाईँको लागि विकासकर्तासँग साझेदारी गर्न एउटा हिप डम्प उपलब्ध छ। होसियार हुनुहोस्: यो हिप डम्पमा अनुप्रयोगको पहुँच भएको तपाईँको कुनै पनि व्यक्तिगत जानकारी हुन सक्छ।" + + + + + + "पाठको लागि एउटा प्रकार्य छान्नुहोस्" "बजाउने मात्रा" "मिडियाको आवाजको मात्रा" @@ -1260,8 +1300,10 @@ "सबै नेटवर्कहरू हेर्न ट्याप गर्नुहोस्" "जडान गर्नुहोस्" "सबै नेटवर्कहरू" - "%s ले प्रस्ताव गरेको Wi‑Fi नेटवर्क उपलब्ध छ" - "तपाईं %s ले प्रस्ताव गरेका नेटवर्कहरूमा जोडिन चाहनुहुन्छ?" + + + + "हो" "होइन" "Wi‑Fi स्वतः सक्रिय हुनेछ" @@ -1273,9 +1315,14 @@ "सञ्जालमा साइन इन गर्नुहोस्" - "Wi-Fi मार्फत इन्टरनेटमाथि पहुँच राख्न सकिँदैन" + + "विकल्पहरूका लागि ट्याप गर्नुहोस्" "जोडियो" + + + + "तपाईंको हटस्पट सेटिङहरूमा परिवर्तन हुन्छ" "तपाईंको हटस्पट ब्यान्ड परिवर्तन भएको छ।" "यो यन्त्रले तपाईंको 5GHz मात्रको प्राथमिकतालाई समर्थन गर्दैन। बरु, उपलब्ध भएको खण्डमा यो यन्त्रले 5GHz ब्यान्ड प्रयोग गर्छ।" @@ -1360,6 +1407,10 @@ "USB डिबग गर्ने सुविधा सुचारू छ" "USB डिबग प्रक्रिया निष्क्रिय पार्न ट्याप गर्नुहोस्‌" "USB डिबगिङलाई असक्षम पार्न ट्याप गर्नुहोस्।" + + + + "USB पोर्टमा तरल पदार्थ वा धुलो भएको कुरा पत्ता लाग्यो" "USB पोर्ट स्वतः असक्षम पारियो। थप जान्न ट्याप गर्नुहोस्।" "USB पोर्ट प्रयोग गर्न सुरक्षित छ" @@ -1911,8 +1962,6 @@ "कार्य प्रोफाइल अनलक गर्न ट्याप गर्नुहोस्" "%1$s मा जडान गरिएको छ" "फाइलहरू हेर्न ट्याप गर्नुहोस्" - "पिन गर्नुहोस्" - "अनपिन गर्नुहोस्" "अनुप्रयोगका बारे जानकारी" "−%1$s" "डेमो सुरु गर्दै…" @@ -2003,6 +2052,22 @@ "दिनचर्या मोडको जानकारीमूलक सूचना" "प्रायः चार्ज गर्ने समय हुनुभन्दा पहिले नै ब्याट्री सकिन सक्छ" "ब्याट्रीको आयु बढाउन ब्याट्री सेभर सक्रिय गरियो" + + + + + + + + + + + + + + + + "फोल्डर" "Android अनुप्रयोग" "फाइल" diff --git a/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_mask_pathdata_interpolator.xml b/core/res/res/values-night/colors_device_defaults.xml similarity index 76% rename from packages/SystemUI/res/interpolator/ic_signal_workmode_enable_mask_pathdata_interpolator.xml rename to core/res/res/values-night/colors_device_defaults.xml index 1820bab9e469e345d2e9b65f626314d8714ce484..08ad4926197b5882bea8c3371668d75142f0687b 100644 --- a/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_mask_pathdata_interpolator.xml +++ b/core/res/res/values-night/colors_device_defaults.xml @@ -1,6 +1,5 @@ - - + + + @color/accent_device_default_dark + \ No newline at end of file diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml index 0721f6fb3802453e1e5d4633b1b1abd86507ba67..86405100d0928c8f219071eec0ed1ee06d2f8867 100644 --- a/core/res/res/values-night/themes_device_defaults.xml +++ b/core/res/res/values-night/themes_device_defaults.xml @@ -63,9 +63,11 @@ easier. + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java index 03dfd3ee60800a1616148e081b294ea71bb418d3..fccb7196b12f35533b5b4ca3feee72d9ed5525b7 100644 --- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java +++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java @@ -71,8 +71,7 @@ public class BarView extends LinearLayout { //Set height of bar view mBarView.getLayoutParams().height = barViewInfo.getNormalizedHeight(); mIcon.setImageDrawable(barViewInfo.getIcon()); - // For now, we use the bar number as title. - mBarTitle.setText(Integer.toString(barViewInfo.getHeight())); + mBarTitle.setText(barViewInfo.getTitle()); mBarSummary.setText(barViewInfo.getSummary()); mIcon.setContentDescription(barViewInfo.getContentDescription()); } diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java index 1ef36a2a75b7643416f5df6b3096d30f9d4fe211..922337a92f1814c8af97212196434058c10dda1a 100644 --- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java +++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarViewInfo.java @@ -21,7 +21,6 @@ import android.view.View; import androidx.annotation.IntRange; import androidx.annotation.Nullable; -import androidx.annotation.StringRes; import java.util.Comparator; @@ -32,8 +31,8 @@ public class BarViewInfo implements Comparable { private final Drawable mIcon; private View.OnClickListener mClickListener; - @StringRes - private int mSummary; + private CharSequence mTitle; + private CharSequence mSummary; private @Nullable CharSequence mContentDescription; // A number indicates this bar's height. The larger number shows a higher bar view. private int mHeight; @@ -45,13 +44,16 @@ public class BarViewInfo implements Comparable { * * @param icon The icon of bar view. * @param barHeight The height of bar view. Larger number shows a higher bar view. - * @param summary The string resource id for summary. + * @param title The string for title. If this is null, use the height of the bar. + * @param summary The string for summary. * @param contentDescription Optional text that briefly describes the contents of the icon. */ - public BarViewInfo(Drawable icon, @IntRange(from = 0) int barHeight, @StringRes int summary, + public BarViewInfo(Drawable icon, @IntRange(from = 0) int barHeight, + @Nullable CharSequence title, CharSequence summary, @Nullable CharSequence contentDescription) { mIcon = icon; mHeight = barHeight; + mTitle = title; mSummary = summary; mContentDescription = contentDescription; } @@ -74,8 +76,12 @@ public class BarViewInfo implements Comparable { mHeight = height; } - void setSummary(@StringRes int resId) { - mSummary = resId; + void setTitle(CharSequence title) { + mTitle = title; + } + + void setSummary(CharSequence summary) { + mSummary = summary; } Drawable getIcon() { @@ -90,8 +96,12 @@ public class BarViewInfo implements Comparable { return mClickListener; } - @StringRes - int getSummary() { + @Nullable + CharSequence getTitle() { + return mTitle; + } + + CharSequence getSummary() { return mSummary; } diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml index 716fc8ded734cf7a36118ad6220f9cdd8b035fe9..71bbd5bd105a28aaa0476ca65ad17eb3a8cdae83 100644 --- a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml +++ b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml @@ -20,8 +20,8 @@ android:id="@+id/app_entities_header" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingStart="24dp" - android:paddingEnd="8dp" + android:paddingStart="16dp" + android:paddingEnd="16dp" android:gravity="center" android:orientation="vertical"> diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml index 013d2d0044713c45ffc77bb23d65486f02e9f998..0db6dfb94dd6f47e5f31b315886c55c4bf4a8533 100644 --- a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml +++ b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_view.xml @@ -20,7 +20,8 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - android:layout_marginEnd="16dp" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" android:gravity="center" android:clickable="true" android:background="@*android:drawable/btn_borderless_material" diff --git a/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml index da575dbd7facc0bcc33c6832cf9caee5f62e925b..3f0a06c68689ee5f405a038bed3a1c1b699757f5 100644 --- a/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml +++ b/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml @@ -46,6 +46,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="false" + android:gravity="center" android:ellipsize="marquee" android:textDirection="locale" android:layout_marginTop="8dp"/> diff --git a/packages/SettingsLib/Tile/Android.bp b/packages/SettingsLib/Tile/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..bf16ef317fd8a80020386a3587332a4fdb8d4cf1 --- /dev/null +++ b/packages/SettingsLib/Tile/Android.bp @@ -0,0 +1,11 @@ +android_library { + name: "SettingsLibTile", + + srcs: ["src/**/*.java"], + + static_libs: [ + "androidx.annotation_annotation", + ], + + min_sdk_version: "21", +} diff --git a/packages/SettingsLib/Tile/AndroidManifest.xml b/packages/SettingsLib/Tile/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..b13532e2a5fd8ff2fcd2e4021772b02c224443eb --- /dev/null +++ b/packages/SettingsLib/Tile/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DashboardCategory.java similarity index 93% rename from packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java rename to packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DashboardCategory.java index a3dda658bec721267296218235dda66b83ea7a8f..7b062b1ac9a9a37e7d7ceb39435946442ad2eb57 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DashboardCategory.java @@ -1,11 +1,11 @@ -/** - * Copyright (C) 2015 The Android Open Source Project +/* + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.android.settingslib.drawer; import static java.lang.String.CASE_INSENSITIVE_ORDER; @@ -26,6 +25,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +/** + * The category for handle {@link Tile} + */ public class DashboardCategory implements Parcelable { /** @@ -67,18 +69,30 @@ public class DashboardCategory implements Parcelable { return result; } + /** + * Add tile + */ public synchronized void addTile(Tile tile) { mTiles.add(tile); } + /** + * Remove tile + */ public synchronized void removeTile(int n) { mTiles.remove(n); } + /** + * Get size of tile + */ public int getTilesCount() { return mTiles.size(); } + /** + * Get tile + */ public Tile getTile(int n) { return mTiles.get(n); } diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java similarity index 95% rename from packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java rename to packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java index d28b00a7ed39836a7414070584ffbe46b2ac3248..5108efbdc2169fe57705a05e9b41700e98264df2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java @@ -1,11 +1,11 @@ -/** - * Copyright (C) 2015 The Android Open Source Project +/* + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -84,8 +84,8 @@ public class Tile implements Parcelable { mActivityPackage = in.readString(); mActivityName = in.readString(); mIntent = new Intent().setClassName(mActivityPackage, mActivityName); - final int N = in.readInt(); - for (int i = 0; i < N; i++) { + final int number = in.readInt(); + for (int i = 0; i < number; i++) { userHandle.add(UserHandle.CREATOR.createFromParcel(in)); } mCategory = in.readString(); @@ -101,9 +101,9 @@ public class Tile implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeString(mActivityPackage); dest.writeString(mActivityName); - final int N = userHandle.size(); - dest.writeInt(N); - for (int i = 0; i < N; i++) { + final int size = userHandle.size(); + dest.writeInt(size); + for (int i = 0; i < size; i++) { userHandle.get(i).writeToParcel(dest, flags); } dest.writeString(mCategory); @@ -151,6 +151,9 @@ public class Tile implements Parcelable { } } + /** + * Check whether title has order. + */ public boolean hasOrder() { return mMetaData.containsKey(META_DATA_KEY_ORDER) && mMetaData.get(META_DATA_KEY_ORDER) instanceof Integer; @@ -262,6 +265,9 @@ public class Tile implements Parcelable { } } + /** + * Check whether title has key. + */ public boolean hasKey() { return mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_KEYHINT); } @@ -361,9 +367,12 @@ public class Tile implements Parcelable { } }; + /** + * Check whether title is only have primary profile + */ public boolean isPrimaryProfileOnly() { - String profile = mMetaData != null ? - mMetaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL; + String profile = mMetaData != null + ? mMetaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL; profile = (profile != null ? profile : PROFILE_ALL); return TextUtils.equals(profile, PROFILE_PRIMARY); } diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java similarity index 98% rename from packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java rename to packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java index 91892abdfb446746aadf1fd4e8014d6e6141d340..31925ab64ec3a4cd17bc5496d1e8988c2c72fd10 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ package com.android.settingslib.drawer; @@ -39,6 +39,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +/** + * Utils is a helper class that contains profile key, meta data, settings action + * and static methods for get icon or text from uri. + */ public class TileUtils { private static final boolean DEBUG_TIMING = false; diff --git a/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_empty_recents.xml b/packages/SettingsLib/res/drawable/ic_media_device.xml similarity index 53% rename from packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_empty_recents.xml rename to packages/SettingsLib/res/drawable/ic_media_device.xml index 76f883115628f19626d04751051e89afdbeefd2c..5a6aeb4a8e237f25798052365fee1d4bb160e036 100644 --- a/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_empty_recents.xml +++ b/packages/SettingsLib/res/drawable/ic_media_device.xml @@ -6,7 +6,7 @@ you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -15,18 +15,19 @@ limitations under the License. --> - + android:viewportWidth="24" + android:viewportHeight="24" + android:width="24dp" + android:height="24dp" + android:tint="?android:attr/colorControlNormal"> + android:fillColor="#00000000" + android:fillAlpha=".1" + android:pathData="M0 0h24v24H0z" /> + android:fillColor="#00000000" + android:pathData="M0 0h24v24H0z" /> + android:pathData="M21 3H3c-1.1 0-2 0.9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2 -0.9 2-2V5c0-1.1 -0.9-2-2-2zM1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0-4v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11z" /> \ No newline at end of file diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml index ed3c11cd3ca3a5972d0448a8586f7135c7180508..39c55fd1925cb576ef3efd70e6f31d26687d4db4 100644 --- a/packages/SettingsLib/res/values/arrays.xml +++ b/packages/SettingsLib/res/values/arrays.xml @@ -603,4 +603,26 @@ 33 + + + @color/bt_color_icon_1 + @color/bt_color_icon_2 + @color/bt_color_icon_3 + @color/bt_color_icon_4 + @color/bt_color_icon_5 + @color/bt_color_icon_6 + @color/bt_color_icon_7 + + + + + @color/bt_color_bg_1 + @color/bt_color_bg_2 + @color/bt_color_bg_3 + @color/bt_color_bg_4 + @color/bt_color_bg_5 + @color/bt_color_bg_6 + @color/bt_color_bg_7 + + diff --git a/packages/SettingsLib/res/values/colors.xml b/packages/SettingsLib/res/values/colors.xml index 66bbb3a6c890f09e0dcff17835916baaf66ef423..4b91bbb8d8dc1caee83a2b828985fe1da5d47e17 100644 --- a/packages/SettingsLib/res/values/colors.xml +++ b/packages/SettingsLib/res/values/colors.xml @@ -19,4 +19,20 @@ @*android:color/tertiary_device_default_settings #64000000 + + #48a50e0e + #480d652d + #48e37400 + #48b06000 + #489c166b + #48681da8 + #48007b83 + + #fad2cf + #ceead6 + #feefc3 + #fedfc8 + #fdcfe8 + #e9d2fd + #cbf0f8 diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml index a9c5061f6d87386e9cf3771d099a3fb2ecc11b73..2cb9d4b14aa750cde2dc89df3b79ccd5876dc3af 100644 --- a/packages/SettingsLib/res/values/dimens.xml +++ b/packages/SettingsLib/res/values/dimens.xml @@ -91,6 +91,7 @@ 0.9dp - + + 24dp diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java index bb8c8a6768ed038c172327d87d4644d03520f3d3..867efb408258850da5389eca52b785004a3d61a8 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java @@ -1,18 +1,30 @@ package com.android.settingslib.bluetooth; import android.bluetooth.BluetoothClass; +import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.provider.MediaStore; +import android.util.Log; import android.util.Pair; import androidx.annotation.DrawableRes; import com.android.settingslib.R; +import com.android.settingslib.widget.AdaptiveIcon; +import com.android.settingslib.widget.AdaptiveOutlineDrawable; +import java.io.IOException; import java.util.List; public class BluetoothUtils { + private static final String TAG = "BluetoothUtils"; + public static final boolean V = false; // verbose logging public static final boolean D = true; // regular logging @@ -112,4 +124,68 @@ public class BluetoothUtils { public static Drawable getBluetoothDrawable(Context context, @DrawableRes int resId) { return context.getDrawable(resId); } + + /** + * Get colorful bluetooth icon with description + */ + public static Pair getBtRainbowDrawableWithDescription(Context context, + CachedBluetoothDevice cachedDevice) { + final Pair pair = BluetoothUtils.getBtClassDrawableWithDescription( + context, cachedDevice); + final BluetoothDevice bluetoothDevice = cachedDevice.getDevice(); + final boolean untetheredHeadset = bluetoothDevice != null + ? Boolean.parseBoolean(bluetoothDevice.getMetadata( + BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)) + : false; + final int iconSize = context.getResources().getDimensionPixelSize( + R.dimen.bt_nearby_icon_size); + final Resources resources = context.getResources(); + + // Deal with untethered headset + if (untetheredHeadset) { + final String uriString = bluetoothDevice != null + ? bluetoothDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON) + : null; + final Uri iconUri = uriString != null ? Uri.parse(uriString) : null; + if (iconUri != null) { + try { + final Bitmap bitmap = MediaStore.Images.Media.getBitmap( + context.getContentResolver(), iconUri); + if (bitmap != null) { + final Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, iconSize, + iconSize, false); + bitmap.recycle(); + final AdaptiveOutlineDrawable drawable = new AdaptiveOutlineDrawable( + resources, resizedBitmap); + return new Pair<>(drawable, pair.second); + } + } catch (IOException e) { + Log.e(TAG, "Failed to get drawable for: " + iconUri, e); + } + } + } + + return new Pair<>(buildBtRainbowDrawable(context, + pair.first, cachedDevice.getAddress().hashCode()), pair.second); + } + + /** + * Build Bluetooth device icon with rainbow + */ + public static Drawable buildBtRainbowDrawable(Context context, Drawable drawable, + int hashCode) { + final Resources resources = context.getResources(); + + // Deal with normal headset + final int[] iconFgColors = resources.getIntArray(R.array.bt_icon_fg_colors); + final int[] iconBgColors = resources.getIntArray(R.array.bt_icon_bg_colors); + + // get color index based on mac address + final int index = Math.abs(hashCode % iconBgColors.length); + drawable.setColorFilter(iconFgColors[index], PorterDuff.Mode.SRC_ATOP); + final Drawable adaptiveIcon = new AdaptiveIcon(context, drawable); + ((AdaptiveIcon) adaptiveIcon).setBackgroundColor(iconBgColors[index]); + + return adaptiveIcon; + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java index b025df44738e0cfd4ab1e34a8fc754e3074d7074..530c73a2448b26f98d882ef134d8eddf3e412b83 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java @@ -31,7 +31,9 @@ import android.util.Slog; * Utilities related to battery saver. */ public class BatterySaverUtils { + private static final String TAG = "BatterySaverUtils"; + public static final String EXTRA_CONFIRM_ONLY = "extra_confirm_only"; private BatterySaverUtils() { } @@ -96,7 +98,7 @@ public class BatterySaverUtils { } final ContentResolver cr = context.getContentResolver(); - if (enable && needFirstTimeWarning && maybeShowBatterySaverConfirmation(context)) { + if (enable && needFirstTimeWarning && maybeShowBatterySaverConfirmation(context, false)) { return false; } if (enable && !needFirstTimeWarning) { @@ -116,7 +118,7 @@ public class BatterySaverUtils { && Global.getInt(cr, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0) == 0 && Secure.getInt(cr, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 0) == 0) { - showAutoBatterySaverSuggestion(context); + showAutoBatterySaverSuggestion(context, false); } } @@ -125,23 +127,36 @@ public class BatterySaverUtils { return false; } - private static boolean maybeShowBatterySaverConfirmation(Context context) { + /** + * Shows the battery saver confirmation warning if it hasn't been acknowledged by the user in + * the past before. When confirmOnly is true, the dialog will have generic info about battery + * saver but will only update that the user has been shown the notification and take no + * further action. if confirmOnly is false it will show a more specific version of the dialog + * that toggles battery saver when acknowledged + * @param context A valid context + * @param confirmOnly Whether to show the actionless generic dialog (true) or the specific one + * that toggles battery saver (false) + * @return True if it showed the notification because it has not been previously acknowledged. + */ + public static boolean maybeShowBatterySaverConfirmation(Context context, boolean confirmOnly) { if (Secure.getInt(context.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 0) != 0) { return false; // Already shown. } - context.sendBroadcast(getSystemUiBroadcast(ACTION_SHOW_START_SAVER_CONFIRMATION)); + context.sendBroadcast( + getSystemUiBroadcast(ACTION_SHOW_START_SAVER_CONFIRMATION, confirmOnly)); return true; } - private static void showAutoBatterySaverSuggestion(Context context) { - context.sendBroadcast(getSystemUiBroadcast(ACTION_SHOW_AUTO_SAVER_SUGGESTION)); + private static void showAutoBatterySaverSuggestion(Context context, boolean confirmOnly) { + context.sendBroadcast(getSystemUiBroadcast(ACTION_SHOW_AUTO_SAVER_SUGGESTION, confirmOnly)); } - private static Intent getSystemUiBroadcast(String action) { + private static Intent getSystemUiBroadcast(String action, boolean confirmOnly) { final Intent i = new Intent(action); i.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); i.setPackage(SYSUI_PACKAGE); + i.putExtra(EXTRA_CONFIRM_ONLY, confirmOnly); return i; } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java index 3092b9960c7ed71de4b21688434b9ec52229fa3a..2711e3175957766b2c6e242f8dd83c4fa9efed8f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java @@ -18,8 +18,11 @@ package com.android.settingslib.media; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.content.Context; +import android.graphics.drawable.Drawable; import android.util.Log; +import android.util.Pair; +import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; /** @@ -48,9 +51,10 @@ public class BluetoothMediaDevice extends MediaDevice { } @Override - public int getIcon() { - //TODO(b/117129183): This is not final icon for bluetooth device, just for demo. - return com.android.internal.R.drawable.ic_bt_headphones_a2dp; + public Drawable getIcon() { + final Pair pair = BluetoothUtils + .getBtRainbowDrawableWithDescription(mContext, mCachedDevice); + return pair.first; } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java index 95f3d3d0f7693061fe75cc57f4d59ade7cddd587..732e8dba3e44b0ee5d705e85c7f24fcecbdb891f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java @@ -16,10 +16,14 @@ package com.android.settingslib.media; import android.content.Context; +import android.graphics.drawable.Drawable; import android.widget.Toast; import androidx.mediarouter.media.MediaRouter; +import com.android.settingslib.R; +import com.android.settingslib.bluetooth.BluetoothUtils; + /** * InfoMediaDevice extends MediaDevice to represents wifi device. */ @@ -46,9 +50,10 @@ public class InfoMediaDevice extends MediaDevice { } @Override - public int getIcon() { - //TODO(b/121083246): This is not final icon for cast device, just for demo. - return com.android.internal.R.drawable.ic_settings_print; + public Drawable getIcon() { + //TODO(b/120669861): Return remote device icon uri once api is ready. + return BluetoothUtils.buildBtRainbowDrawable(mContext, + mContext.getDrawable(R.drawable.ic_media_device), getId().hashCode()); } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java index 9b9e80310c18ddda6c1b401f235e7195b3dbf554..53a852069478e7bb39400067f7227671d35f4419 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java @@ -16,6 +16,7 @@ package com.android.settingslib.media; import android.content.Context; +import android.graphics.drawable.Drawable; import android.text.TextUtils; import androidx.annotation.IntDef; @@ -70,11 +71,11 @@ public abstract class MediaDevice implements Comparable { public abstract String getSummary(); /** - * Get resource id of MediaDevice. + * Get icon of MediaDevice. * - * @return resource id of MediaDevice. + * @return drawable of icon. */ - public abstract int getIcon(); + public abstract Drawable getIcon(); /** * Get unique ID that represent MediaDevice diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java index 8c3fcc077edcd80b9be7edd9ac3e06504a4f19dc..af91c34641945377bf86575b00b59cc84c072c8d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java @@ -16,10 +16,12 @@ package com.android.settingslib.media; import android.content.Context; +import android.graphics.drawable.Drawable; import android.util.Log; import com.android.settingslib.R; import com.android.settingslib.bluetooth.A2dpProfile; +import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.HearingAidProfile; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; @@ -56,8 +58,9 @@ public class PhoneMediaDevice extends MediaDevice { } @Override - public int getIcon() { - return R.drawable.ic_smartphone; + public Drawable getIcon() { + return BluetoothUtils.buildBtRainbowDrawable(mContext, + mContext.getDrawable(R.drawable.ic_smartphone), getId().hashCode()); } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 3acbcd3f6b41a15d215c7387aecf0b3b45bda1af..8a88a4c64d0ab607ba01bffe5202ff3261fb6462 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -53,6 +53,7 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; +import android.util.Pair; import androidx.annotation.NonNull; @@ -65,8 +66,10 @@ 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.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; @@ -197,6 +200,9 @@ public class AccessPoint implements Comparable { private final Context mContext; + private WifiManager mWifiManager; + private WifiManager.ActionListener mConnectListener; + private String ssid; private String bssid; private int security; @@ -1068,8 +1074,10 @@ public class AccessPoint implements Comparable { /** * Starts the OSU Provisioning flow. */ - public void startOsuProvisioning() { - mContext.getSystemService(WifiManager.class).startSubscriptionProvisioning( + public void startOsuProvisioning(@Nullable WifiManager.ActionListener connectListener) { + mConnectListener = connectListener; + + getWifiManager().startSubscriptionProvisioning( mOsuProvider, mContext.getMainExecutor(), new AccessPointProvisioningCallback() @@ -1539,12 +1547,20 @@ public class AccessPoint implements Comparable { return string; } + private WifiManager getWifiManager() { + if (mWifiManager == null) { + mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + } + return mWifiManager; + } + /** * Callbacks relaying changes to the AccessPoint representation. * *

      All methods are invoked on the Main Thread. */ public interface AccessPointListener { + /** * Indicates a change to the externally visible state of the AccessPoint trigger by an * update of ScanResults, saved configuration state, connection state, or score @@ -1561,7 +1577,6 @@ public class AccessPoint implements Comparable { * changed */ @MainThread void onAccessPointChanged(AccessPoint accessPoint); - /** * Indicates the "wifi pie signal level" has changed, retrieved via calls to * {@link AccessPoint#getLevel()}. @@ -1643,11 +1658,46 @@ public class AccessPoint implements Comparable { mOsuProvisioningComplete = true; mOsuFailure = null; mOsuStatus = null; + ThreadUtils.postOnMainThread(() -> { if (mAccessPointListener != null) { mAccessPointListener.onAccessPointChanged(AccessPoint.this); } }); + + // Connect to the freshly provisioned network. + WifiManager wifiManager = getWifiManager(); + + PasspointConfiguration passpointConfig = wifiManager + .getMatchingPasspointConfigsForOsuProviders(Collections.singleton(mOsuProvider)) + .get(mOsuProvider); + if (passpointConfig == null) { + Log.e(TAG, "Missing PasspointConfiguration for newly provisioned network!"); + if (mConnectListener != null) { + mConnectListener.onFailure(0); + } + return; + } + + String fqdn = passpointConfig.getHomeSp().getFqdn(); + for (Pair>> pairing : + wifiManager.getAllMatchingWifiConfigs(wifiManager.getScanResults())) { + WifiConfiguration config = pairing.first; + if (TextUtils.equals(config.FQDN, fqdn)) { + List homeScans = + pairing.second.get(WifiManager.PASSPOINT_HOME_NETWORK); + List roamingScans = + pairing.second.get(WifiManager.PASSPOINT_ROAMING_NETWORK); + + AccessPoint connectionAp = + new AccessPoint(mContext, config, homeScans, roamingScans); + wifiManager.connect(connectionAp.getConfig(), mConnectListener); + return; + } + } + if (mConnectListener != null) { + mConnectListener.onFailure(0); + } } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index fdc0fd33e6d736b1d82f483354dd628d5ea82386..5f2bc4e1d4904f0ce685c28a3fc1de4562cda8df 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -84,7 +84,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro private static final long DEFAULT_MAX_CACHED_SCORE_AGE_MILLIS = 20 * DateUtils.MINUTE_IN_MILLIS; /** Maximum age of scan results to hold onto while actively scanning. **/ - private static final long MAX_SCAN_RESULT_AGE_MILLIS = 15000; + @VisibleForTesting static final long MAX_SCAN_RESULT_AGE_MILLIS = 15000; private static final String TAG = "WifiTracker"; private static final boolean DBG() { @@ -142,6 +142,13 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro */ private boolean mStaleScanResults = true; + /** + * Tracks whether the latest SCAN_RESULTS_AVAILABLE_ACTION contained new scans. If not, then + * we treat the last scan as an aborted scan and increase the eviction timeout window to avoid + * completely flushing the AP list before the next successful scan completes. + */ + private boolean mLastScanSucceeded = true; + // Does not need to be locked as it only updated on the worker thread, with the exception of // during onStart, which occurs before the receiver is registered on the work handler. private final HashMap mScanResultCache = new HashMap<>(); @@ -478,17 +485,22 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } /** - * Remove old scan results from the cache. + * Remove old scan results from the cache. If {@link #mLastScanSucceeded} is false, then + * increase the timeout window to avoid completely flushing the AP list before the next + * successful scan completes. * *

      Should only ever be invoked from {@link #updateScanResultCache(List)} when * {@link #mStaleScanResults} is false. */ private void evictOldScans() { + long evictionTimeoutMillis = mLastScanSucceeded ? MAX_SCAN_RESULT_AGE_MILLIS + : MAX_SCAN_RESULT_AGE_MILLIS * 2; + long nowMs = SystemClock.elapsedRealtime(); for (Iterator iter = mScanResultCache.values().iterator(); iter.hasNext(); ) { ScanResult result = iter.next(); // result timestamp is in microseconds - if (nowMs - result.timestamp / 1000 > MAX_SCAN_RESULT_AGE_MILLIS) { + if (nowMs - result.timestamp / 1000 > evictionTimeoutMillis) { iter.remove(); } } @@ -840,6 +852,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro WifiManager.WIFI_STATE_UNKNOWN)); } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { mStaleScanResults = false; + mLastScanSucceeded = + intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, true); fetchScansAndConfigsAndUpdateAccessPoints(); } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java index 9c8e3f4fe5435b43dc104e1b916d5daaa66a9c36..8e4027164587a9b6fffb43258ca79861eeee456b 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java @@ -41,6 +41,7 @@ import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkScoreCache; import android.net.wifi.WifiSsid; import android.net.wifi.hotspot2.OsuProvider; @@ -53,6 +54,7 @@ import android.os.SystemClock; import android.text.SpannableString; import android.text.format.DateUtils; import android.util.ArraySet; +import android.util.Pair; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -72,6 +74,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -95,9 +98,12 @@ public class AccessPointTest { private Context mContext; private WifiInfo mWifiInfo; + @Mock private Context mMockContext; + @Mock private WifiManager mMockWifiManager; @Mock private RssiCurve mockBadgeCurve; @Mock private WifiNetworkScoreCache mockWifiNetworkScoreCache; @Mock private AccessPoint.AccessPointListener mMockAccessPointListener; + @Mock private WifiManager.ActionListener mMockConnectListener; private static final int NETWORK_ID = 123; private static final int DEFAULT_RSSI = -55; @@ -1360,6 +1366,9 @@ public class AccessPointTest { .isEqualTo(mContext.getString(R.string.tap_to_sign_up)); } + /** + * Verifies that the summary of an OSU entry updates based on provisioning status. + */ @Test public void testOsuAccessPointSummary_showsProvisioningUpdates() { AccessPoint osuAccessPoint = new AccessPoint(mContext, createOsuProvider(), @@ -1411,4 +1420,82 @@ public class AccessPointTest { assertThat(osuAccessPoint.getSummary()) .isEqualTo(mContext.getString(R.string.osu_sign_up_complete)); } + + /** + * Verifies that after provisioning through an OSU provider, we connect to the freshly + * provisioned network. + */ + @Test + public void testOsuAccessPoint_connectsAfterProvisioning() { + // Set up mock for WifiManager.getAllMatchingWifiConfigs + WifiConfiguration config = new WifiConfiguration(); + config.FQDN = "fqdn"; + Map> scanMapping = new HashMap<>(); + scanMapping.put(WifiManager.PASSPOINT_HOME_NETWORK, mScanResults); + Pair>> configMapPair = + new Pair<>(config, scanMapping); + List>>> matchingWifiConfig = + new ArrayList<>(); + matchingWifiConfig.add(configMapPair); + when(mMockWifiManager.getAllMatchingWifiConfigs(any())).thenReturn(matchingWifiConfig); + + // Set up mock for WifiManager.getMatchingPasspointConfigsForOsuProviders + OsuProvider provider = createOsuProvider(); + PasspointConfiguration passpointConfig = new PasspointConfiguration(); + HomeSp homeSp = new HomeSp(); + homeSp.setFqdn("fqdn"); + homeSp.setFriendlyName("Test Provider"); + passpointConfig.setHomeSp(homeSp); + Map osuProviderConfigMap = new HashMap<>(); + osuProviderConfigMap.put(provider, passpointConfig); + when(mMockWifiManager + .getMatchingPasspointConfigsForOsuProviders(Collections.singleton(provider))) + .thenReturn(osuProviderConfigMap); + + when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager); + + AccessPoint osuAccessPoint = new AccessPoint(mMockContext, provider, mScanResults); + osuAccessPoint.setListener(mMockAccessPointListener); + + AccessPoint.AccessPointProvisioningCallback provisioningCallback = + osuAccessPoint.new AccessPointProvisioningCallback(); + provisioningCallback.onProvisioningComplete(); + + verify(mMockWifiManager).connect(any(), any()); + } + + /** + * Verifies that after provisioning through an OSU provider, we call the connect listener's + * onFailure() method if we cannot find the network we just provisioned. + */ + @Test + public void testOsuAccessPoint_noMatchingConfigsAfterProvisioning_callsOnFailure() { + // Set up mock for WifiManager.getAllMatchingWifiConfigs + when(mMockWifiManager.getAllMatchingWifiConfigs(any())).thenReturn(new ArrayList<>()); + + // Set up mock for WifiManager.getMatchingPasspointConfigsForOsuProviders + OsuProvider provider = createOsuProvider(); + PasspointConfiguration passpointConfig = new PasspointConfiguration(); + HomeSp homeSp = new HomeSp(); + homeSp.setFqdn("fqdn"); + homeSp.setFriendlyName("Test Provider"); + passpointConfig.setHomeSp(homeSp); + Map osuProviderConfigMap = new HashMap<>(); + osuProviderConfigMap.put(provider, passpointConfig); + when(mMockWifiManager + .getMatchingPasspointConfigsForOsuProviders(Collections.singleton(provider))) + .thenReturn(osuProviderConfigMap); + + when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager); + + AccessPoint osuAccessPoint = new AccessPoint(mMockContext, provider, mScanResults); + osuAccessPoint.setListener(mMockAccessPointListener); + osuAccessPoint.startOsuProvisioning(mMockConnectListener); + + AccessPoint.AccessPointProvisioningCallback provisioningCallback = + osuAccessPoint.new AccessPointProvisioningCallback(); + provisioningCallback.onProvisioningComplete(); + + verify(mMockConnectListener).onFailure(anyInt()); + } } 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 edf414ddf32380444690a32cc457ee946a8ddb2a..683ec8bb5a6c7e50df002d4ba13534b72afb233a 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 @@ -270,7 +270,7 @@ public class WifiTrackerTest { SystemClock.elapsedRealtime() * 1000 /* microsecond timestamp */); } - private static ScanResult buildStaleScanResult() { + private static ScanResult buildScanResultWithTimestamp(long timestampMillis) { return new ScanResult( WifiSsid.createFromAsciiEncoded(SSID_3), BSSID_3, @@ -280,7 +280,7 @@ public class WifiTrackerTest { "", // capabilities RSSI_3, 0, // frequency - 0 /* microsecond timestamp */); + timestampMillis * 1000 /* microsecond timestamp */); } private static WifiConfiguration buildPasspointConfiguration(String fqdn, String friendlyName) { @@ -379,6 +379,12 @@ public class WifiTrackerTest { tracker.mReceiver.onReceive(mContext, i); } + private void sendFailedScanResults(WifiTracker tracker) throws InterruptedException { + Intent i = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); + i.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false); + tracker.mReceiver.onReceive(mContext, i); + } + private void sendUpdatedScores() throws InterruptedException { Bundle attr1 = new Bundle(); attr1.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, mockBadgeCurve1); @@ -982,8 +988,8 @@ public class WifiTrackerTest { @Test public void onStart_updateScanResults_evictOldScanResult() { - when(mockWifiManager.getScanResults()).thenReturn( - Arrays.asList(buildScanResult1(), buildScanResult2(), buildStaleScanResult())); + when(mockWifiManager.getScanResults()).thenReturn(Arrays.asList( + buildScanResult1(), buildScanResult2(), buildScanResultWithTimestamp(0))); WifiTracker tracker = createMockedWifiTracker(); tracker.forceUpdate(); @@ -994,6 +1000,33 @@ public class WifiTrackerTest { assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2); } + /** + * Verifies that a failed scan reported on SCAN_RESULTS_AVAILABLE_ACTION should increase the + * ScanResult eviction timeout to twice the default. + */ + @Test + public void failedScan_increasesEvictionTimeout() throws InterruptedException { + when(mockWifiManager.getScanResults()).thenReturn(Arrays.asList( + buildScanResult1(), buildScanResult2(), buildScanResultWithTimestamp( + SystemClock.elapsedRealtime() - WifiTracker.MAX_SCAN_RESULT_AGE_MILLIS))); + WifiTracker tracker = createMockedWifiTracker(); + + sendFailedScanResults(tracker); + + // Failed scan increases timeout window to include the stale scan + assertThat(tracker.getAccessPoints()).hasSize(3); + assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1); + assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2); + assertThat(tracker.getAccessPoints().get(2).getBssid()).isEqualTo(BSSID_3); + + sendScanResults(tracker); + + // Successful scan resets the timeout window to remove the stale scan + assertThat(tracker.getAccessPoints()).hasSize(2); + assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1); + assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2); + } + /** * Verifies that updatePasspointAccessPoints will only return AccessPoints whose * isPasspoint() evaluates as true. diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java index b713e08eb67e641057c88bed46f30772e22ea660..b228cf7c10c6d3956fb0ee01aae6c8c09160bb21 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java @@ -15,15 +15,20 @@ */ package com.android.settingslib.bluetooth; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothClass; +import android.bluetooth.BluetoothDevice; import android.content.Context; import android.graphics.drawable.Drawable; import android.util.Pair; +import com.android.settingslib.widget.AdaptiveIcon; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -38,6 +43,9 @@ public class BluetoothUtilsTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) private CachedBluetoothDevice mCachedBluetoothDevice; + @Mock + private BluetoothDevice mBluetoothDevice; + private Context mContext; @Before @@ -66,4 +74,16 @@ public class BluetoothUtilsTest { verify(mContext).getDrawable(com.android.internal.R.drawable.ic_bt_laptop); } + + @Test + public void getBtRainbowDrawableWithDescription_normalHeadset_returnAdaptiveIcon() { + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn("false"); + when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); + when(mCachedBluetoothDevice.getAddress()).thenReturn("1f:aa:bb"); + + assertThat(BluetoothUtils.getBtRainbowDrawableWithDescription( + RuntimeEnvironment.application, + mCachedBluetoothDevice).first).isInstanceOf(AdaptiveIcon.class); + } } \ No newline at end of file diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java index c1c5fa932b0c1f8dc73b249ee0fdec01a57f05f6..5d2a0d1df96cf05470d3699ac9b2bbe86f06285f 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java @@ -16,9 +16,10 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; - import android.util.LongSparseLongArray; + import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -83,6 +84,7 @@ public class RecentLocationAccessesTest { } @Test + @Ignore public void testGetAppList_shouldFilterRecentAccesses() { List requests = mRecentLocationAccesses.getAppList(); // Only two of the apps have requested location within 15 min. @@ -95,6 +97,7 @@ public class RecentLocationAccessesTest { } @Test + @Ignore public void testGetAppList_shouldNotShowAndroidOS() throws NameNotFoundException { // Add android OS to the list of apps. PackageOps androidSystemPackageOps = diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ed6b9b0a135e3b8a64630d762111ba6f82366a21 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.widget; + +import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB; +import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Icon; +import android.graphics.drawable.ShapeDrawable; +import android.os.Bundle; + +import com.android.settingslib.R; +import com.android.settingslib.drawer.CategoryKey; +import com.android.settingslib.drawer.Tile; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class AdaptiveIconTest { + + private Context mContext; + private ActivityInfo mActivityInfo; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mActivityInfo = new ActivityInfo(); + mActivityInfo.packageName = mContext.getPackageName(); + mActivityInfo.name = "class"; + mActivityInfo.metaData = new Bundle(); + } + + @Test + public void createIcon_shouldSetBackgroundAndInset() { + final AdaptiveIcon icon = + new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK)); + + assertThat(icon.getNumberOfLayers()).isEqualTo(2); + assertThat(icon.getDrawable(0)).isInstanceOf(AdaptiveIconShapeDrawable.class); + } + + @Test + public void setBackgroundColor_shouldUpdateColorFilter() { + final AdaptiveIcon icon = + spy(new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK))); + final ShapeDrawable background = mock(ShapeDrawable.class); + when(icon.getDrawable(0)).thenReturn(background); + + icon.setBackgroundColor(Color.BLUE); + + verify(background).setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_ATOP); + } + + @Test + public void setBackgroundColor_externalTileWithBackgroundColorRawValue_shouldUpdateIcon() { + final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); + mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB, 0xff0000); + doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update)) + .when(tile).getIcon(mContext); + final AdaptiveIcon icon = + new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK)); + + icon.setBackgroundColor(mContext, tile); + assertThat(icon.mBackgroundColor).isEqualTo(0xff0000); + } + + @Test + public void setBackgroundColor_tileWithoutBackgroundColor_shouldSetDefaultBackgroundColor() { + final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); + doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update)) + .when(tile).getIcon(mContext); + final AdaptiveIcon icon = new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK)); + + icon.setBackgroundColor(mContext, tile); + + assertThat(icon.mBackgroundColor) + .isEqualTo(mContext.getColor(R.color.homepage_generic_icon_background)); + } + + @Test + public void onBindTile_externalTileWithBackgroundColorHint_shouldUpdateIcon() { + final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); + mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT, + R.color.bt_outline_color); + doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update)) + .when(tile).getIcon(mContext); + + final AdaptiveIcon icon = + new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK)); + icon.setBackgroundColor(mContext, tile); + + assertThat(icon.mBackgroundColor) + .isEqualTo(mContext.getColor(R.color.bt_outline_color)); + } + + @Test + public void getConstantState_returnCorrectState() { + final AdaptiveIcon icon = + new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK)); + icon.setBackgroundColor(Color.YELLOW); + + final AdaptiveIcon.AdaptiveConstantState state = + (AdaptiveIcon.AdaptiveConstantState) icon.getConstantState(); + + assertThat(state.mColor).isEqualTo(Color.YELLOW); + assertThat(state.mContext).isEqualTo(mContext); + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java new file mode 100644 index 0000000000000000000000000000000000000000..71d55bc3de2ab7804a729039d67123d603a8f31a --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.widget; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.res.Resources; +import android.graphics.Paint; + +import com.android.settingslib.R; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class AdaptiveOutlineDrawableTest { + + @Test + public void constructor_initPaint() { + final Resources resources = RuntimeEnvironment.application.getResources(); + final AdaptiveOutlineDrawable drawable = new AdaptiveOutlineDrawable(resources, null); + + assertThat(drawable.mOutlinePaint.getStyle()).isEqualTo(Paint.Style.STROKE); + assertThat(drawable.mOutlinePaint.getStrokeWidth()).isWithin(0.01f).of( + resources.getDimension(R.dimen.adaptive_outline_stroke)); + } + +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartInfoTest.java index 2b272485dff3a2cd0ac3b0d47b43a93ebbea5598..7faac7a90664973ee5f3b8fd8bbaf0f5b5c74b7d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartInfoTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartInfoTest.java @@ -39,6 +39,8 @@ public class BarChartInfoTest { private final int mDetails = 0x22222222; @StringRes private final int mEmptyText = 0x33333333; + private final CharSequence mTitleStr = "title"; + private final CharSequence mSummaryStr = "summary"; private final View.OnClickListener mClickListener = v -> { }; @@ -72,7 +74,8 @@ public class BarChartInfoTest { final BarViewInfo barViewInfo = new BarViewInfo( null /* icon */, 50, - mTitle, + mTitleStr, + mSummaryStr, null); final BarChartInfo mBarChartInfo = new BarChartInfo.Builder() @@ -92,7 +95,8 @@ public class BarChartInfoTest { final BarViewInfo barViewInfo = new BarViewInfo( null /* icon */, 50, - mTitle, + mTitleStr, + mSummaryStr, null); final BarChartInfo mBarChartInfo = new BarChartInfo.Builder() .setTitle(mTitle) @@ -115,7 +119,8 @@ public class BarChartInfoTest { final BarViewInfo barViewInfo = new BarViewInfo( null /* icon */, 50, - mTitle, + mTitleStr, + mSummaryStr, null); new BarChartInfo.Builder() .setTitle(mTitle) diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java index 266554be57d3887be780493a2f195e7c52651157..567d90fa89164251ba7e599bcc8f235efa076689 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java @@ -39,6 +39,8 @@ public class BarChartPreferenceTest { @Rule public final ExpectedException thrown = ExpectedException.none(); + private final CharSequence mTitleStr = "title"; + private final CharSequence mSummaryStr = "summary"; private Context mContext; private View mBarChartView; @@ -114,7 +116,9 @@ public class BarChartPreferenceTest { .setTitle(R.string.debug_app) .setDetails(R.string.debug_app) .addBarViewInfo( - new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app, null)) + new BarViewInfo(mIcon, 10, null /* title */, + mContext.getText(R.string.debug_app) /* summary */, + null /* contentDescription */)) .build(); mPreference.initializeBarChart(barChartInfo); @@ -130,7 +134,9 @@ public class BarChartPreferenceTest { final BarChartInfo barChartInfo = new BarChartInfo.Builder() .setTitle(R.string.debug_app) .addBarViewInfo( - new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app, null)) + new BarViewInfo(mIcon, 10, null /* title */, + mContext.getText(R.string.debug_app) /* summary */, + null /* contentDescription */)) .build(); mPreference.initializeBarChart(barChartInfo); @@ -147,7 +153,9 @@ public class BarChartPreferenceTest { .setDetailsOnClickListener(v -> { }) .addBarViewInfo( - new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app, null)) + new BarViewInfo(mIcon, 10, null /* title */, + mContext.getText(R.string.debug_app) /* summary */, + null /* contentDescription */)) .build(); mPreference.initializeBarChart(barChartInfo); @@ -160,7 +168,7 @@ public class BarChartPreferenceTest { @Test public void setBarViewInfos_oneBarViewInfoSet_shouldShowOneBarView() { final BarViewInfo[] barViewsInfo = new BarViewInfo[]{ - new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app, null) + new BarViewInfo(mIcon, 10, mTitleStr, mSummaryStr, null /* contentDescription */) }; mPreference.initializeBarChart(mBarChartInfo); @@ -168,7 +176,7 @@ public class BarChartPreferenceTest { mPreference.onBindViewHolder(mHolder); assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mBarView1.getTitle()).isEqualTo("10"); + assertThat(mBarView1.getTitle()).isEqualTo(mTitleStr); assertThat(mBarView2.getVisibility()).isEqualTo(View.GONE); assertThat(mBarView3.getVisibility()).isEqualTo(View.GONE); @@ -178,8 +186,8 @@ public class BarChartPreferenceTest { @Test public void setBarViewInfos_twoBarViewInfosSet_shouldShowTwoBarViews() { final BarViewInfo[] barViewsInfo = new BarViewInfo[]{ - new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app, null), - new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app, null) + new BarViewInfo(mIcon, 20, mTitleStr, mSummaryStr, null /* contentDescription */), + new BarViewInfo(mIcon, 10, mTitleStr, mSummaryStr, null /* contentDescription */), }; mPreference.initializeBarChart(mBarChartInfo); @@ -187,9 +195,9 @@ public class BarChartPreferenceTest { mPreference.onBindViewHolder(mHolder); assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mBarView1.getTitle()).isEqualTo("20"); + assertThat(mBarView1.getTitle()).isEqualTo(mTitleStr); assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mBarView2.getTitle()).isEqualTo("10"); + assertThat(mBarView2.getTitle()).isEqualTo(mTitleStr); assertThat(mBarView3.getVisibility()).isEqualTo(View.GONE); assertThat(mBarView4.getVisibility()).isEqualTo(View.GONE); @@ -198,9 +206,9 @@ public class BarChartPreferenceTest { @Test public void setBarViewInfos_threeBarViewInfosSet_shouldShowThreeBarViews() { final BarViewInfo[] barViewsInfo = new BarViewInfo[]{ - new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app, null), - new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app, null), - new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app, null) + new BarViewInfo(mIcon, 20, mTitleStr, mSummaryStr, null /* contentDescription */), + new BarViewInfo(mIcon, 10, mTitleStr, mSummaryStr, null /* contentDescription */), + new BarViewInfo(mIcon, 5, mTitleStr, mSummaryStr, null /* contentDescription */) }; mPreference.initializeBarChart(mBarChartInfo); @@ -208,11 +216,11 @@ public class BarChartPreferenceTest { mPreference.onBindViewHolder(mHolder); assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mBarView1.getTitle()).isEqualTo("20"); + assertThat(mBarView1.getTitle()).isEqualTo(mTitleStr); assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mBarView2.getTitle()).isEqualTo("10"); + assertThat(mBarView2.getTitle()).isEqualTo(mTitleStr); assertThat(mBarView3.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mBarView3.getTitle()).isEqualTo("5"); + assertThat(mBarView3.getTitle()).isEqualTo(mTitleStr); assertThat(mBarView4.getVisibility()).isEqualTo(View.GONE); } @@ -220,10 +228,10 @@ public class BarChartPreferenceTest { @Test public void setBarViewInfos_fourBarViewInfosSet_shouldShowFourBarViews() { final BarViewInfo[] barViewsInfo = new BarViewInfo[]{ - new BarViewInfo(mIcon, 20 /* barNumber */, R.string.debug_app, null), - new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app, null), - new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app, null), - new BarViewInfo(mIcon, 2 /* barNumber */, R.string.debug_app, null), + new BarViewInfo(mIcon, 20, mTitleStr, mSummaryStr, null /* contentDescription */), + new BarViewInfo(mIcon, 10, mTitleStr, mSummaryStr, null /* contentDescription */), + new BarViewInfo(mIcon, 5, mTitleStr, mSummaryStr, null /* contentDescription */), + new BarViewInfo(mIcon, 2, mTitleStr, mSummaryStr, null /* contentDescription */) }; mPreference.initializeBarChart(mBarChartInfo); @@ -231,13 +239,13 @@ public class BarChartPreferenceTest { mPreference.onBindViewHolder(mHolder); assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mBarView1.getTitle()).isEqualTo("20"); + assertThat(mBarView1.getTitle()).isEqualTo(mTitleStr); assertThat(mBarView2.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mBarView2.getTitle()).isEqualTo("10"); + assertThat(mBarView2.getTitle()).isEqualTo(mTitleStr); assertThat(mBarView3.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mBarView3.getTitle()).isEqualTo("5"); + assertThat(mBarView3.getTitle()).isEqualTo(mTitleStr); assertThat(mBarView4.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mBarView4.getTitle()).isEqualTo("2"); + assertThat(mBarView4.getTitle()).isEqualTo(mTitleStr); } @Test @@ -245,11 +253,11 @@ public class BarChartPreferenceTest { thrown.expect(IllegalStateException.class); final BarViewInfo[] barViewsInfo = new BarViewInfo[]{ - new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app, null), - new BarViewInfo(mIcon, 50 /* barNumber */, R.string.debug_app, null), - new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app, null), - new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app, null), - new BarViewInfo(mIcon, 70 /* barNumber */, R.string.debug_app, null), + new BarViewInfo(mIcon, 30, mTitleStr, mSummaryStr, null /* contentDescription */), + new BarViewInfo(mIcon, 50, mTitleStr, mSummaryStr, null /* contentDescription */), + new BarViewInfo(mIcon, 5, mTitleStr, mSummaryStr, null /* contentDescription */), + new BarViewInfo(mIcon, 10, mTitleStr, mSummaryStr, null /* contentDescription */), + new BarViewInfo(mIcon, 70, mTitleStr, mSummaryStr, null /* contentDescription */), }; mPreference.setBarViewInfos(barViewsInfo); @@ -258,10 +266,10 @@ public class BarChartPreferenceTest { @Test public void setBarViewInfos_barViewInfosSet_shouldBeSortedInDescending() { final BarViewInfo[] barViewsInfo = new BarViewInfo[]{ - new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app, null), - new BarViewInfo(mIcon, 50 /* barNumber */, R.string.debug_app, null), - new BarViewInfo(mIcon, 5 /* barNumber */, R.string.debug_app, null), - new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app, null), + new BarViewInfo(mIcon, 30, "30", mSummaryStr, null /* contentDescription */), + new BarViewInfo(mIcon, 50, "50", mSummaryStr, null /* contentDescription */), + new BarViewInfo(mIcon, 5, "5", mSummaryStr, null /* contentDescription */), + new BarViewInfo(mIcon, 10, "10", mSummaryStr, null /* contentDescription */) }; mPreference.initializeBarChart(mBarChartInfo); @@ -281,7 +289,7 @@ public class BarChartPreferenceTest { @Test public void setBarViewInfos_validBarViewSummarySet_barViewShouldShowSummary() { final BarViewInfo[] barViewsInfo = new BarViewInfo[]{ - new BarViewInfo(mIcon, 10 /* barNumber */, R.string.debug_app, null), + new BarViewInfo(mIcon, 10, mTitleStr, mSummaryStr, null /* contentDescription */) }; mPreference.initializeBarChart(mBarChartInfo); @@ -289,13 +297,27 @@ public class BarChartPreferenceTest { mPreference.onBindViewHolder(mHolder); assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mBarView1.getSummary()).isEqualTo(mContext.getText(R.string.debug_app)); + assertThat(mBarView1.getSummary()).isEqualTo(mSummaryStr); + } + + @Test + public void setBarViewInfos_validBarViewTitleSet_barViewShouldShowTitle() { + final BarViewInfo[] barViewsInfo = new BarViewInfo[]{ + new BarViewInfo(mIcon, 10, mTitleStr, mSummaryStr, null /* contentDescription */) + }; + + mPreference.initializeBarChart(mBarChartInfo); + mPreference.setBarViewInfos(barViewsInfo); + mPreference.onBindViewHolder(mHolder); + + assertThat(mBarView1.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mBarView1.getTitle()).isEqualTo(mTitleStr); } @Test public void setBarViewInfos_clickListenerForBarViewSet_barViewShouldHaveClickListener() { - final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app, - null); + final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30, mTitleStr, mSummaryStr, + null /* contentDescription */); viewInfo.setClickListener(v -> { }); final BarViewInfo[] barViewsInfo = new BarViewInfo[]{viewInfo}; @@ -310,8 +332,8 @@ public class BarChartPreferenceTest { @Test public void onBindViewHolder_loadingStateIsTrue_shouldHideAllViews() { - final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app, - null); + final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30, mTitleStr, mSummaryStr, + null /* contentDescription */); viewInfo.setClickListener(v -> { }); final BarViewInfo[] barViewsInfo = new BarViewInfo[]{viewInfo}; @@ -327,8 +349,8 @@ public class BarChartPreferenceTest { @Test public void onBindViewHolder_loadingStateIsFalse_shouldInitAnyView() { - final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app, - null); + final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30, mTitleStr, mSummaryStr, + null /* contentDescription */); viewInfo.setClickListener(v -> { }); final BarViewInfo[] barViewsInfo = new BarViewInfo[]{viewInfo}; diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 4b342b37ac20eec7a2854644bca338d36b23f80e..296f7a144ffe54cc3a5ece809a6dec5d4d1f69d8 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -16,6 +16,7 @@ package com.android.providers.settings; +import static android.os.Process.INVALID_UID; import static android.os.Process.ROOT_UID; import static android.os.Process.SHELL_UID; import static android.os.Process.SYSTEM_UID; @@ -2777,7 +2778,7 @@ public class SettingsProvider extends ContentProvider { boolean someSettingChanged = false; Setting setting = settingsState.getSettingLocked(name); if (!SettingsState.isSystemPackage(getContext(), - setting.getPackageName())) { + setting.getPackageName(), INVALID_UID, userId)) { if (prefix != null && !setting.getName().startsWith(prefix)) { continue; } @@ -2797,7 +2798,7 @@ public class SettingsProvider extends ContentProvider { boolean someSettingChanged = false; Setting setting = settingsState.getSettingLocked(name); if (!SettingsState.isSystemPackage(getContext(), - setting.getPackageName())) { + setting.getPackageName(), INVALID_UID, userId)) { if (prefix != null && !setting.getName().startsWith(prefix)) { continue; } @@ -4410,7 +4411,7 @@ public class SettingsProvider extends ContentProvider { } try { final boolean systemSet = SettingsState.isSystemPackage(getContext(), - setting.getPackageName(), callingUid); + setting.getPackageName(), callingUid, userId); if (systemSet) { settings.insertSettingLocked(name, setting.getValue(), setting.getTag(), true, setting.getPackageName()); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index 521163f50ca1c0fa9a18a3b6d1a7a8ae7adb6235..c05c4cdf72d77e1bc6d7a8aaf3f387e2c9a0c2e1 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -17,6 +17,7 @@ package com.android.providers.settings; import static android.os.Process.FIRST_APPLICATION_UID; +import static android.os.Process.INVALID_UID; import android.annotation.NonNull; import android.content.Context; @@ -1124,11 +1125,16 @@ final class SettingsState { return sb.toString(); } + // Check if a specific package belonging to the caller is part of the system package. public static boolean isSystemPackage(Context context, String packageName) { - return isSystemPackage(context, packageName, Binder.getCallingUid()); + final int callingUid = Binder.getCallingUid(); + final int callingUserId = UserHandle.getUserId(callingUid); + return isSystemPackage(context, packageName, callingUid, callingUserId); } - public static boolean isSystemPackage(Context context, String packageName, int callingUid) { + // Check if a specific package, uid, and user ID are part of the system package. + public static boolean isSystemPackage(Context context, String packageName, int uid, + int userId) { synchronized (sLock) { if (SYSTEM_PACKAGE_NAME.equals(packageName)) { return true; @@ -1140,26 +1146,19 @@ final class SettingsState { return false; } - // Native services running as a special UID get a pass - final int callingAppId = UserHandle.getAppId(callingUid); - if (callingAppId < FIRST_APPLICATION_UID) { - sSystemUids.put(callingAppId, callingAppId); - return true; + if (uid != INVALID_UID) { + // Native services running as a special UID get a pass + final int callingAppId = UserHandle.getAppId(uid); + if (callingAppId < FIRST_APPLICATION_UID) { + sSystemUids.put(callingAppId, callingAppId); + return true; + } } - // While some callers may have permissions to manipulate cross user - // settings or some settings are stored in the parent of a managed - // profile for the purpose of determining whether the other end is a - // system component we need to use the user id of the caller for - // pulling information about the caller from the package manager. - final int callingUserId = UserHandle.getUserId(callingUid); - final long identity = Binder.clearCallingIdentity(); try { - final int uid; try { - uid = context.getPackageManager().getPackageUidAsUser(packageName, 0, - callingUserId); + uid = context.getPackageManager().getPackageUidAsUser(packageName, 0, userId); } catch (PackageManager.NameNotFoundException e) { return false; } @@ -1187,7 +1186,7 @@ final class SettingsState { PackageInfo packageInfo; try { packageInfo = context.getPackageManager().getPackageInfoAsUser( - packageName, PackageManager.GET_SIGNATURES, callingUserId); + packageName, PackageManager.GET_SIGNATURES, userId); if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0 && (packageInfo.applicationInfo.flags diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index d6e61ebbbd6e59ccc0462a587abc7ebebf2cbe7a..2a9456dd723c4cc15fa84ab6d0740557cf91886f 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -125,6 +125,8 @@ + + @@ -166,6 +168,9 @@ + + + @@ -178,6 +183,8 @@ + + diff --git a/packages/Shell/tests/Android.mk b/packages/Shell/tests/Android.mk index b93ddde16e4ace1f1dad251f7dc7bf1215b0c2d5..44ff3383d566277ec4e86f41cd94c6f22128a0c8 100644 --- a/packages/Shell/tests/Android.mk +++ b/packages/Shell/tests/Android.mk @@ -9,7 +9,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock LOCAL_STATIC_JAVA_LIBRARIES := \ - android-support-test \ + androidx.test.rules \ mockito-target-minus-junit4 \ ub-uiautomator \ junit \ diff --git a/packages/Shell/tests/AndroidManifest.xml b/packages/Shell/tests/AndroidManifest.xml index 6d564c640fcddfd99d2ce51740a56e1b7a1a8127..e845ef95b28ebbc24393410cb9c84c8dfff313b5 100644 --- a/packages/Shell/tests/AndroidManifest.xml +++ b/packages/Shell/tests/AndroidManifest.xml @@ -36,7 +36,7 @@ - diff --git a/packages/Shell/tests/AndroidTest.xml b/packages/Shell/tests/AndroidTest.xml index e592d82bfaf722b14b732b650f1c1ea1f010fa7b..e03b68a03d0e99a855281d4541d17cfb0a30bd99 100644 --- a/packages/Shell/tests/AndroidTest.xml +++ b/packages/Shell/tests/AndroidTest.xml @@ -22,7 +22,7 @@

      diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index d654f5aa23e013f85da4818cb0dcf65ec77cd370..abd2b7684526ca98123af9863d454161a6c58808 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -33,8 +33,10 @@ android:protectionLevel="signature" /> + + - + diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java index 7b7657a3d646ec1c2a29fd20f51dcc0ed817ab76..105be46c05d72fdd36590fcd7b67d52e81ecf0d5 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java @@ -28,7 +28,7 @@ import java.util.TimeZone; public interface ClockPlugin extends Plugin { String ACTION = "com.android.systemui.action.PLUGIN_CLOCK"; - int VERSION = 2; + int VERSION = 3; /** * Get the name of the clock face. @@ -47,6 +47,17 @@ public interface ClockPlugin extends Plugin { */ Bitmap getThumbnail(); + /** + * Get preview images of clock face to be shown in the picker app. + * + * Preview image should be realistic and show what the clock face will look like on AOD and lock + * screen. + * + * @param width width of the preview image, should be the same as device width in pixels. + * @param height height of the preview image, should be the same as device height in pixels. + */ + Bitmap getPreview(int width, int height); + /** * Get clock view. * @return clock view from plugin. diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_disable_left_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_disable_left_animation.xml deleted file mode 100644 index d6054c4d34222b83277348f0ff730fabb1c483c7..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_disable_left_animation.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_disable_mask_1_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_disable_mask_1_animation.xml deleted file mode 100644 index 282170c1fae4bfca7cf89adf9014fdcd72eec71b..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_disable_mask_1_animation.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_disable_rectangle_path_3_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_disable_rectangle_path_3_animation.xml deleted file mode 100644 index b59c66419080536aa682e09a72c0bb698a6fd035..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_disable_rectangle_path_3_animation.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_disable_rectangle_path_3_position_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_disable_rectangle_path_3_position_animation.xml deleted file mode 100644 index 60d23966ed123ac6f5b69a02efbd093fdb4cff52..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_disable_rectangle_path_3_position_animation.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_disable_rectangle_path_4_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_disable_rectangle_path_4_animation.xml deleted file mode 100644 index ef442e9ef13a401584652bb6fc10da06f5d3f6ef..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_disable_rectangle_path_4_animation.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_disable_rectangle_path_4_position_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_disable_rectangle_path_4_position_animation.xml deleted file mode 100644 index 97782be4f14571585e788ffafca2d5c1675d037e..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_disable_rectangle_path_4_position_animation.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_disable_right_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_disable_right_animation.xml deleted file mode 100644 index d6054c4d34222b83277348f0ff730fabb1c483c7..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_disable_right_animation.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_disable_stick_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_disable_stick_animation.xml deleted file mode 100644 index 573205ffd2c8add60bf8062ec993e84e42751629..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_disable_stick_animation.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_disable_stickito_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_disable_stickito_animation.xml deleted file mode 100644 index 4b038b930c468a22b68dda470f6b353db8b91586..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_disable_stickito_animation.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_disable_whole_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_disable_whole_animation.xml deleted file mode 100644 index 562985a89ba99907252c63d55cc700e585258657..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_disable_whole_animation.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_cross_1.xml b/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_cross_1.xml deleted file mode 100644 index 6c7e75118f5cb581c18f6fed15ad69948a5756be..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_cross_1.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_ic_signal_briefcase.xml b/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_ic_signal_briefcase.xml deleted file mode 100644 index c699fe280f66c786cb9e383f5e65ca5719264fb9..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_ic_signal_briefcase.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_mask.xml b/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_mask.xml deleted file mode 100644 index 5595e5c7629709d8caf6e6c1f544ef5de2043046..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_animation_mask.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_left_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_enable_left_animation.xml deleted file mode 100644 index 4614bfc849e1630921b7d4a9c2044121f0223e52..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_left_animation.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_mask_1_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_enable_mask_1_animation.xml deleted file mode 100644 index f5cfcf91b77794147040191e8609903c340e0d97..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_mask_1_animation.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_rectangle_path_3_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_enable_rectangle_path_3_animation.xml deleted file mode 100644 index 0b74b3a4fdc5e3def14cedbc89911c4db03adb20..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_rectangle_path_3_animation.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_rectangle_path_3_position_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_enable_rectangle_path_3_position_animation.xml deleted file mode 100644 index ba10224ab4db9ab756b6592402d0e8d6fd61d296..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_rectangle_path_3_position_animation.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_rectangle_path_4_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_enable_rectangle_path_4_animation.xml deleted file mode 100644 index 395bbf4802dd96719c80850f0c49ea40f07cd82e..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_rectangle_path_4_animation.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_rectangle_path_4_position_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_enable_rectangle_path_4_position_animation.xml deleted file mode 100644 index 0115759ab0d828c4ea721225fae3aeeea853cbed..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_rectangle_path_4_position_animation.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_right_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_enable_right_animation.xml deleted file mode 100644 index 4614bfc849e1630921b7d4a9c2044121f0223e52..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_right_animation.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_stick_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_enable_stick_animation.xml deleted file mode 100644 index 6b182be014466fa1681e35505943325e82d077b4..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_stick_animation.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_stickito_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_enable_stickito_animation.xml deleted file mode 100644 index 828f7a289ec63478f5b57a82f602e2d29b57680d..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_stickito_animation.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/packages/SystemUI/res/anim/ic_signal_workmode_enable_whole_animation.xml b/packages/SystemUI/res/anim/ic_signal_workmode_enable_whole_animation.xml deleted file mode 100644 index 89ab5808b4ab5c58cfd802bb6dc961afcfeee217..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/anim/ic_signal_workmode_enable_whole_animation.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_brightness_auto_off_alpha.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_brightness_auto_off_alpha.png deleted file mode 100644 index 0a29157fbc5cc76278572315d75581f2d7d1a3c5..0000000000000000000000000000000000000000 Binary files a/packages/SystemUI/res/drawable-hdpi/ic_qs_brightness_auto_off_alpha.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_brightness_auto_on_alpha.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_brightness_auto_on_alpha.png deleted file mode 100644 index 9c1d8ef1c12781c6f6329bb4b791e264f4630c44..0000000000000000000000000000000000000000 Binary files a/packages/SystemUI/res/drawable-hdpi/ic_qs_brightness_auto_on_alpha.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_brightness_auto_off_alpha.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_brightness_auto_off_alpha.png deleted file mode 100644 index 74df151e76b0a9885056923cc34ac5081da7d35e..0000000000000000000000000000000000000000 Binary files a/packages/SystemUI/res/drawable-mdpi/ic_qs_brightness_auto_off_alpha.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_brightness_auto_on_alpha.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_brightness_auto_on_alpha.png deleted file mode 100644 index 56add9210c7f3552ad55db321853d5d1751a9eb8..0000000000000000000000000000000000000000 Binary files a/packages/SystemUI/res/drawable-mdpi/ic_qs_brightness_auto_on_alpha.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_brightness_auto_off_alpha.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_brightness_auto_off_alpha.png deleted file mode 100644 index 37d7ac7351ca38955f21722a781a4a3d71ba8b8c..0000000000000000000000000000000000000000 Binary files a/packages/SystemUI/res/drawable-xhdpi/ic_qs_brightness_auto_off_alpha.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_brightness_auto_on_alpha.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_brightness_auto_on_alpha.png deleted file mode 100644 index 626e283a88a7b8fd584ce787a2b9ba1b820846e3..0000000000000000000000000000000000000000 Binary files a/packages/SystemUI/res/drawable-xhdpi/ic_qs_brightness_auto_on_alpha.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_brightness_auto_off_alpha.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_brightness_auto_off_alpha.png deleted file mode 100644 index 2697b5a80f9080f0d1c49a44518ffea6499caea5..0000000000000000000000000000000000000000 Binary files a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_brightness_auto_off_alpha.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_brightness_auto_on_alpha.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_brightness_auto_on_alpha.png deleted file mode 100644 index b6443fa9a1f9910e0f2fb0399c8653ede91855b3..0000000000000000000000000000000000000000 Binary files a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_brightness_auto_on_alpha.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable/ic_qs_brightness_auto_off.xml b/packages/SystemUI/res/drawable/ic_alarm.xml similarity index 54% rename from packages/SystemUI/res/drawable/ic_qs_brightness_auto_off.xml rename to packages/SystemUI/res/drawable/ic_alarm.xml index aaebc778e42d408f06ebd6b6d3fb9018bbdef00b..1c1adcd149bf87b19e28178cca4081ec98eca4d0 100644 --- a/packages/SystemUI/res/drawable/ic_qs_brightness_auto_off.xml +++ b/packages/SystemUI/res/drawable/ic_alarm.xml @@ -1,4 +1,3 @@ - + + - + diff --git a/packages/SystemUI/res/drawable/ic_alarm_dim.xml b/packages/SystemUI/res/drawable/ic_alarm_dim.xml new file mode 100644 index 0000000000000000000000000000000000000000..37ab873d504c298b0a2f1917a6575957b1313ebf --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_alarm_dim.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/ic_bluetooth_connected.xml b/packages/SystemUI/res/drawable/ic_bluetooth_connected.xml new file mode 100644 index 0000000000000000000000000000000000000000..125082c49bdc5505c1f73cb77588fff874655438 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_bluetooth_connected.xml @@ -0,0 +1,30 @@ + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_camera.xml b/packages/SystemUI/res/drawable/ic_camera.xml new file mode 100644 index 0000000000000000000000000000000000000000..b330875864d335b952979d3c86da28f7b30a98b4 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_camera.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/ic_qs_cast_off.xml b/packages/SystemUI/res/drawable/ic_cast.xml similarity index 54% rename from packages/SystemUI/res/drawable/ic_qs_cast_off.xml rename to packages/SystemUI/res/drawable/ic_cast.xml index 9e57577d13e1e2489f1286c4294f473df51e1e09..a2c2eb2806f6bf444ba68013e2155858d2ee6abc 100644 --- a/packages/SystemUI/res/drawable/ic_qs_cast_off.xml +++ b/packages/SystemUI/res/drawable/ic_cast.xml @@ -14,11 +14,11 @@ Copyright (C) 2017 The Android Open Source Project limitations under the License. --> + android:fillColor="@android:color/white" + android:pathData="M21,3L3,3c-1.1,0 -2,0.9 -2,2v3h2L3,5h18v14h-7v2h7c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM1,18v3h3c0,-1.66 -1.34,-3 -3,-3zM1,14v2c2.76,0 5,2.24 5,5h2c0,-3.87 -3.13,-7 -7,-7zM1,10v2c4.97,0 9,4.03 9,9h2c0,-6.08 -4.93,-11 -11,-11z"/> diff --git a/packages/SystemUI/res/drawable/ic_qs_vpn.xml b/packages/SystemUI/res/drawable/ic_cast_connected.xml similarity index 59% rename from packages/SystemUI/res/drawable/ic_qs_vpn.xml rename to packages/SystemUI/res/drawable/ic_cast_connected.xml index 6567d123f5d50d331e3c9e3a52e5eb5db5c59456..995fd498f19eece53569e71f25954c8759de987c 100644 --- a/packages/SystemUI/res/drawable/ic_qs_vpn.xml +++ b/packages/SystemUI/res/drawable/ic_cast_connected.xml @@ -1,5 +1,5 @@ + - + android:pathData="M1,18v3h3C4,19.34 2.66,18 1,18zM1,14v2c2.76,0 5,2.24 5,5h2C8,17.13 4.87,14 1,14zM19,7H5v1.63c3.96,1.28 7.09,4.41 8.37,8.37H19V7zM1,10v2c4.97,0 9,4.03 9,9h2C12,14.92 7.07,10 1,10zM21,3H3C1.9,3 1,3.9 1,5v3h2V5h18v14h-7v2h7c1.1,0 2,-0.9 2,-2V5C23,3.9 22.1,3 21,3"/> + diff --git a/packages/SystemUI/res/drawable/ic_data_saver.xml b/packages/SystemUI/res/drawable/ic_data_saver.xml index 0f027ee7afb1db918575fbb2e7fcc0c62928ae22..cc3f539db8d3dd4ac24149a959f17f1d53b53bbe 100644 --- a/packages/SystemUI/res/drawable/ic_data_saver.xml +++ b/packages/SystemUI/res/drawable/ic_data_saver.xml @@ -14,15 +14,11 @@ limitations under the License. --> + android:viewportHeight="24.0"> - diff --git a/packages/SystemUI/res/drawable/ic_data_saver_off.xml b/packages/SystemUI/res/drawable/ic_data_saver_off.xml index 29e6c912b79758d19fc93a06128c4b0fa102b6a3..d8e9bc46b434a5b41ac8f94ee0d8ec337c1b11f1 100644 --- a/packages/SystemUI/res/drawable/ic_data_saver_off.xml +++ b/packages/SystemUI/res/drawable/ic_data_saver_off.xml @@ -17,9 +17,8 @@ android:width="24.0dp" android:height="24.0dp" android:viewportWidth="24.0" - android:viewportHeight="24.0" - android:tint="?android:attr/colorControlNormal"> + android:viewportHeight="24.0"> + android:pathData="M13,2.05v3.03c3.39,0.49 6,3.39 6,6.92 0,0.9 -0.18,1.75 -0.48,2.54l2.6,1.53c0.56,-1.24 0.88,-2.62 0.88,-4.07 0,-5.18 -3.95,-9.45 -9,-9.95zM12,19c-3.87,0 -7,-3.13 -7,-7 0,-3.53 2.61,-6.43 6,-6.92V2.05c-5.06,0.5 -9,4.76 -9,9.95 0,5.52 4.47,10 9.99,10 3.31,0 6.24,-1.61 8.06,-4.09l-2.6,-1.53C16.17,17.98 14.21,19 12,19z"/> diff --git a/packages/SystemUI/res/drawable/ic_headset.xml b/packages/SystemUI/res/drawable/ic_headset.xml index 27efe80c5ae0b43a68412d68b1d483377b41edf2..797a80ac691692c42a05891ac1fc99211caf958a 100644 --- a/packages/SystemUI/res/drawable/ic_headset.xml +++ b/packages/SystemUI/res/drawable/ic_headset.xml @@ -13,19 +13,12 @@ See the License for the specific language governing permissions and limitations under the License. --> - - - - - - - + + + diff --git a/packages/SystemUI/res/drawable/ic_headset_mic.xml b/packages/SystemUI/res/drawable/ic_headset_mic.xml index 1260e0f42949fd76920b2e797858f37925223fab..b3f006d15bb5d8f8a1a704ac562d0c0ab84ec331 100644 --- a/packages/SystemUI/res/drawable/ic_headset_mic.xml +++ b/packages/SystemUI/res/drawable/ic_headset_mic.xml @@ -13,16 +13,12 @@ See the License for the specific language governing permissions and limitations under the License. --> - - - - - + + + diff --git a/packages/SystemUI/res/drawable/ic_hotspot.xml b/packages/SystemUI/res/drawable/ic_hotspot.xml index 8450bf6ce60c7ebaa376968b8241a6ff86937cde..b6fa798d8bb6c750c1cce068395739163bff5047 100644 --- a/packages/SystemUI/res/drawable/ic_hotspot.xml +++ b/packages/SystemUI/res/drawable/ic_hotspot.xml @@ -15,14 +15,12 @@ limitations under the License. --> - - - + + diff --git a/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml b/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml deleted file mode 100644 index 7641998a8cd9b51635ab5fcd488c51d8e78bc939..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/packages/SystemUI/res/drawable/ic_location.xml b/packages/SystemUI/res/drawable/ic_location.xml new file mode 100644 index 0000000000000000000000000000000000000000..50ba523fcd3e4d806c1183783197818d846c767d --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_location.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml deleted file mode 100644 index dd124b713a7271c584af3062e73a82622fe219ed..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_connected.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - diff --git a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml index 220c63ccca6d4670a6064341550b964051cb9e69..1c867064773c11ab083f31d097c3311f85f86e0a 100644 --- a/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml +++ b/packages/SystemUI/res/drawable/ic_qs_bluetooth_on.xml @@ -14,13 +14,11 @@ Copyright (C) 2017 The Android Open Source Project limitations under the License. --> - + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> - + android:fillColor="@android:color/white" + android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41L11,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59L13,5.83zM14.88,16.29L13,18.17v-3.76l1.88,1.88z"/> + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_qs_branded_vpn.xml b/packages/SystemUI/res/drawable/ic_qs_branded_vpn.xml deleted file mode 100644 index b20e1582698c2f5bfbe64e8506af6f34ffb33101..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/drawable/ic_qs_branded_vpn.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - diff --git a/packages/SystemUI/res/drawable/ic_qs_cast_on.xml b/packages/SystemUI/res/drawable/ic_qs_cast_on.xml deleted file mode 100644 index 3dda87cf7e8b5ceb376e6a695180960a89c516cb..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/drawable/ic_qs_cast_on.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/packages/SystemUI/res/drawable/ic_signal_workmode_disable.xml b/packages/SystemUI/res/drawable/ic_signal_workmode_disable.xml deleted file mode 100644 index 96d8484069550a5a786d6513f867279dedb787fb..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/drawable/ic_signal_workmode_disable.xml +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/SystemUI/res/drawable/ic_volume_alarm.xml b/packages/SystemUI/res/drawable/ic_volume_alarm.xml index 996e488e8c819aa8c521034405cf02f40c4439e0..771b466dc6d118e0cf63bddcfb6a97b6090d6fdc 100644 --- a/packages/SystemUI/res/drawable/ic_volume_alarm.xml +++ b/packages/SystemUI/res/drawable/ic_volume_alarm.xml @@ -14,14 +14,12 @@ limitations under the License. --> - + android:height="24dp" + android:width="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + android:pathData="M13,8h-2v5.41l3.79,3.8 1.42,-1.42 -3.21,-3.2zM12,4c-4.97,0 -9,4.03 -9,9s4.03,9 9,9 9,-4.03 9,-9 -4.03,-9 -9,-9zM12,20c-3.86,0 -7,-3.14 -7,-7s3.14,-7 7,-7 7,3.14 7,7 -3.14,7 -7,7zM16.056,3.346l1.282,-1.535 4.607,3.85 -1.28,1.54zM2.056,5.654L6.663,1.81l1.28,1.536L3.338,7.19z"/> diff --git a/packages/SystemUI/res/drawable/ic_volume_alarm_mute.xml b/packages/SystemUI/res/drawable/ic_volume_alarm_mute.xml index 02fb1e702438b2973e3ca2e9a0ad9307fc65957e..18e2736698547fc6f2659721a54b2a2b476e9d8c 100644 --- a/packages/SystemUI/res/drawable/ic_volume_alarm_mute.xml +++ b/packages/SystemUI/res/drawable/ic_volume_alarm_mute.xml @@ -22,6 +22,6 @@ + android:pathData="M16.056,3.346l1.282,-1.535 4.607,3.85 -1.28,1.54zM9.35,6.52C10.17,6.19 11.06,6 12,6c3.86,0 7,3.14 7,7 0,0.94 -0.19,1.83 -0.52,2.65l1.5,1.5C20.63,15.91 21,14.5 21,13c0,-4.97 -4.03,-9 -9,-9 -1.5,0 -2.91,0.37 -4.15,1.02l1.5,1.5zM17.42,17.42L7.58,7.58 6.16,6.16l-0.72,-0.72 -1.42,-1.42 -1.21,-1.21 -1.42,1.41L2.48,5.3l-0.42,0.35 1.28,1.54 0.56,-0.47 0.9,0.9C3.67,9.12 3,10.98 3,13c0,4.97 4.03,9 9,9 2.02,0 3.88,-0.67 5.38,-1.79l2.4,2.4 1.41,-1.41 -2.35,-2.35 -1.42,-1.43zM12,20c-3.86,0 -7,-3.14 -7,-7 0,-1.46 0.46,-2.82 1.23,-3.94l9.71,9.71C14.82,19.54 13.46,20 12,20zM7.94,3.35L6.66,1.81l-1.1,0.92 1.42,1.42z"/> diff --git a/packages/SystemUI/res/drawable/ic_volume_ringer_vibrate.xml b/packages/SystemUI/res/drawable/ic_volume_ringer_vibrate.xml index aa13f1e2a9e709dc6d199a9425ff129ce8f15aa2..314e06cc93b0d4a3bc091558f679009bd444f248 100644 --- a/packages/SystemUI/res/drawable/ic_volume_ringer_vibrate.xml +++ b/packages/SystemUI/res/drawable/ic_volume_ringer_vibrate.xml @@ -14,11 +14,10 @@ limitations under the License. --> + android:viewportWidth="24.0"> - - - - - + diff --git a/packages/SystemUI/res/drawable/stat_sys_alarm.xml b/packages/SystemUI/res/drawable/stat_sys_alarm.xml index 0e9319c63eaa289c38432ad43df2ab2845640961..b9bebec5baba49f92056f1faa6b57b31f029ecc7 100644 --- a/packages/SystemUI/res/drawable/stat_sys_alarm.xml +++ b/packages/SystemUI/res/drawable/stat_sys_alarm.xml @@ -1,35 +1,21 @@ - - - - - - - + android:insetRight="2.5dp" + android:drawable="@drawable/ic_alarm" /> diff --git a/packages/SystemUI/res/drawable/stat_sys_alarm_dim.xml b/packages/SystemUI/res/drawable/stat_sys_alarm_dim.xml index c8e2ac19a4e2e75987d455b846b4b89ec73efb01..cf1119d80fa365d928ba1f58cf00eb714d20e2b8 100644 --- a/packages/SystemUI/res/drawable/stat_sys_alarm_dim.xml +++ b/packages/SystemUI/res/drawable/stat_sys_alarm_dim.xml @@ -15,18 +15,5 @@ --> - - - - - - - - \ No newline at end of file + android:insetRight="2.5dp" + android:drawable="@drawable/ic_alarm_dim" /> diff --git a/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml b/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml index bfae8575cd144c0163fec9cdf5831e0324f930a9..5913cdf4a073e4b23aa910a6621e408a4011fa0c 100644 --- a/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml +++ b/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> + - - - - + android:insetRight="3dp" + android:drawable="@drawable/ic_camera" /> \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/stat_sys_cast.xml b/packages/SystemUI/res/drawable/stat_sys_cast.xml index 5228085b5dc53213793fb30e8b7c2a9e2975cb5c..de7ec9d628bb6e72c1130f2ad2e02f05faeb738b 100644 --- a/packages/SystemUI/res/drawable/stat_sys_cast.xml +++ b/packages/SystemUI/res/drawable/stat_sys_cast.xml @@ -15,14 +15,5 @@ Copyright (C) 2017 The Android Open Source Project --> - - - - - + android:insetRight="2.5dp" + android:drawable="@drawable/ic_cast_connected" /> \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth.xml deleted file mode 100644 index 4dc0e4baa39780a7d424b75edd8d9943b72cbcfa..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected.xml index c8a444064fd62772abb4c5beb4483400aabb8c8b..8d9e561df793c31047e7b9b38012d05330a2733c 100644 --- a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected.xml +++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected.xml @@ -1,31 +1,17 @@ - - - - - - + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/stat_sys_data_saver.xml b/packages/SystemUI/res/drawable/stat_sys_data_saver.xml index fed7caec377a3481458e981939d11047c87ebd9e..022350173a35c534ab899e70427efb23f4ea5e05 100644 --- a/packages/SystemUI/res/drawable/stat_sys_data_saver.xml +++ b/packages/SystemUI/res/drawable/stat_sys_data_saver.xml @@ -18,26 +18,5 @@ --> - - - - - - - - - + android:insetRight="2.5dp" + android:drawable="@drawable/ic_data_saver" /> \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/stat_sys_dnd.xml b/packages/SystemUI/res/drawable/stat_sys_dnd.xml index 68a06d013bf79bb98dbbbbf12f95e0f7995b2904..aa352b42cf045400f67ba623e8d9b0485a5cd1dc 100644 --- a/packages/SystemUI/res/drawable/stat_sys_dnd.xml +++ b/packages/SystemUI/res/drawable/stat_sys_dnd.xml @@ -18,17 +18,5 @@ --> - - - - - + android:insetRight="2.5dp" + android:drawable="@*android:drawable/ic_qs_dnd" /> \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/stat_sys_zen_important.xml b/packages/SystemUI/res/drawable/stat_sys_headset.xml similarity index 50% rename from packages/SystemUI/res/drawable/stat_sys_zen_important.xml rename to packages/SystemUI/res/drawable/stat_sys_headset.xml index 1262617589d5f443543642cf11db242ea19207b8..361c6659c5e45ac78f6e066df0eba31babcb0199 100644 --- a/packages/SystemUI/res/drawable/stat_sys_zen_important.xml +++ b/packages/SystemUI/res/drawable/stat_sys_headset.xml @@ -1,7 +1,7 @@ - - - - - + android:insetRight="2.5dp" + android:drawable="@drawable/ic_headset" /> diff --git a/packages/SystemUI/res/drawable/stat_sys_headset_mic.xml b/packages/SystemUI/res/drawable/stat_sys_headset_mic.xml new file mode 100644 index 0000000000000000000000000000000000000000..b424caa6e12c8e991301ff1a83018d1dc3a54a02 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_headset_mic.xml @@ -0,0 +1,19 @@ + + diff --git a/packages/SystemUI/res/drawable/stat_sys_hotspot.xml b/packages/SystemUI/res/drawable/stat_sys_hotspot.xml index 4f52777339c5966a59b303ffa7c8d0963e1cfbc4..361b00ae0598bf7fc5b9a91e964fe51cddaeec4f 100644 --- a/packages/SystemUI/res/drawable/stat_sys_hotspot.xml +++ b/packages/SystemUI/res/drawable/stat_sys_hotspot.xml @@ -15,18 +15,5 @@ Copyright (C) 2017 The Android Open Source Project --> - - - - - - + android:insetRight="2.5dp" + android:drawable="@drawable/ic_hotspot" /> diff --git a/packages/SystemUI/res/drawable/stat_sys_location.xml b/packages/SystemUI/res/drawable/stat_sys_location.xml index bdb0b0c2c29f5c92a00627ab74807ebed1ad8ead..7a5aeb9ae558310a5d690c9b8b7292d65069eb79 100644 --- a/packages/SystemUI/res/drawable/stat_sys_location.xml +++ b/packages/SystemUI/res/drawable/stat_sys_location.xml @@ -14,18 +14,6 @@ ~ limitations under the License --> - - - - - \ No newline at end of file + android:insetLeft="2.5dp" + android:insetRight="2.5dp" + android:drawable="@drawable/ic_location" /> diff --git a/packages/SystemUI/res/drawable/stat_sys_ringer_vibrate.xml b/packages/SystemUI/res/drawable/stat_sys_ringer_vibrate.xml index 0b72f75ff97dadbc7dcab136fa364ff790d7b318..21a4c1703d315126c37f23940fb45edd333f8982 100644 --- a/packages/SystemUI/res/drawable/stat_sys_ringer_vibrate.xml +++ b/packages/SystemUI/res/drawable/stat_sys_ringer_vibrate.xml @@ -1,36 +1,19 @@ - - - - - - - - + android:insetRight="2.5dp" + android:drawable="@drawable/ic_volume_ringer_vibrate" /> \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/stat_sys_zen_none.xml b/packages/SystemUI/res/drawable/stat_sys_zen_none.xml deleted file mode 100644 index 92914a8dd9889a04815082b4f9e1fc513203b59d..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/drawable/stat_sys_zen_none.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - diff --git a/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_0.xml b/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_0.xml deleted file mode 100644 index 5009c6b7ac1083ef5420535825923db8d6a86f4d..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_0.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_1.xml b/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_1.xml deleted file mode 100644 index 678b90b24914f9166f86cb22115c5389999a316c..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_1.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_2.xml b/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_2.xml deleted file mode 100644 index 6c6df6078aaa6f75a9451297dd34c18f0d18c363..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_2.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_3.xml b/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_3.xml deleted file mode 100644 index b1eec483cdf4b4664c20eaf4362c9eb6845a3435..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_3.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_4.xml b/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_4.xml deleted file mode 100644 index 17ff65aaa15589efb333a65772ffbdd238c25c4c..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_4.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_5.xml b/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_5.xml deleted file mode 100644 index aee48dc2637a825bc31c02650562a6e31d9caab6..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_animation_interpolator_5.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_cross_1_pathdata_interpolator.xml b/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_cross_1_pathdata_interpolator.xml deleted file mode 100644 index 66cfaffd993644b3dd3e3bda035bd1f94b47a667..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/interpolator/ic_signal_workmode_disable_cross_1_pathdata_interpolator.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - diff --git a/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_0.xml b/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_0.xml deleted file mode 100644 index 4a5fde9fd49ba3b71810335e1a295f55dbacb6f5..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_0.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_1.xml b/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_1.xml deleted file mode 100644 index 0f35e5d01470b5f1787e0814a274643ced7f25df..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_1.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_2.xml b/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_2.xml deleted file mode 100644 index 626f9ef9788199e744b0a90d1ce6729ab36e36f7..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_2.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_3.xml b/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_3.xml deleted file mode 100644 index 17ff65aaa15589efb333a65772ffbdd238c25c4c..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_3.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_4.xml b/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_4.xml deleted file mode 100644 index 96bdb484c2f90e2b431c3372d5ef0ec366c5a69a..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_4.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_5.xml b/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_5.xml deleted file mode 100644 index a91610d617f242812b5c13334de8ef12977fc570..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/interpolator/ic_signal_workmode_enable_animation_interpolator_5.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml index 9acfbafa3c0b9455cda8040031933f5067368f1f..c420117073c518136c59cff614bc15ebc8efd6e6 100644 --- a/packages/SystemUI/res/layout-land/volume_dialog.xml +++ b/packages/SystemUI/res/layout-land/volume_dialog.xml @@ -54,6 +54,8 @@ android:background="@drawable/rounded_ripple" android:layout_width="match_parent" android:layout_height="match_parent" + android:scaleType="fitCenter" + android:padding="@dimen/volume_dialog_ringer_icon_padding" android:tint="@color/accent_tint_color_selector" android:layout_gravity="center" android:soundEffectsEnabled="false" /> diff --git a/packages/SystemUI/res/layout/biometric_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml index 8f7a45f02c4654d2f51b4722f36063e8b4e7410b..c452855cc9e976fe0962bc0da3980513e61a367c 100644 --- a/packages/SystemUI/res/layout/biometric_dialog.xml +++ b/packages/SystemUI/res/layout/biometric_dialog.xml @@ -76,10 +76,6 @@ android:layout_marginTop="24dp" android:gravity="@integer/biometric_dialog_text_gravity" android:textSize="20sp" - android:maxLines="1" - android:singleLine="true" - android:ellipsize="marquee" - android:marqueeRepeatLimit="marquee_forever" android:textColor="?android:attr/textColorPrimary"/> - - - - - - - + - + android:layout_marginBottom="@dimen/keyguard_lock_padding"> + + - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml deleted file mode 100644 index c8e0845a9144fcb8bcdf668b7be8be9785c09eff..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml index 2efae71103f5126923114b9aeb31dd11948ac5bb..28672241b1b287f9acdd4005d660605bb9cca388 100644 --- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml +++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml @@ -22,16 +22,6 @@ android:paddingRight="16dp" style="@style/BrightnessDialogContainer"> - - diff --git a/packages/SystemUI/res/layout/volume_dnd_icon.xml b/packages/SystemUI/res/layout/volume_dnd_icon.xml index 037d143fa69ea4cda41c4e167f847b2592ff75db..10c1472ae99302db824707cb5f2326c94d1a8dae 100644 --- a/packages/SystemUI/res/layout/volume_dnd_icon.xml +++ b/packages/SystemUI/res/layout/volume_dnd_icon.xml @@ -24,6 +24,6 @@ android:layout_width="14dp" android:layout_height="14dp" android:layout_gravity="right|top" - android:src="@drawable/ic_dnd" + android:src="@*android:drawable/ic_qs_dnd" android:tint="?android:attr/textColorTertiary"/> \ No newline at end of file diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index b82c250e2bd2584e525ec0a288cd3f461551c47f..290d75b4de9c18ca9024f26242f8df5e8f42c971 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -129,7 +129,6 @@ #fff44336 - #d9000000 @color/material_grey_600 @color/material_grey_100 diff --git a/packages/SystemUI/res/values/config_car.xml b/packages/SystemUI/res/values/config_car.xml deleted file mode 100644 index 2c549bc8ce2d6ea5216e4d582240d33600ce29e3..0000000000000000000000000000000000000000 --- a/packages/SystemUI/res/values/config_car.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - false - - - - false - false - true - true - diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 1da1405f469a117c0686a46c7775e3193fadb68d..6eb279affc4fe35cca10fa9caf55ec02819d2e4e 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -23,8 +23,6 @@ @*android:dimen/navigation_bar_height 48dp - - 48dp 500dp + 0.5 + + 0.05 @*android:dimen/status_bar_icon_size @@ -335,6 +339,8 @@ 64dp + 20dp + 64dp 48dp @@ -977,10 +983,6 @@ 0px - - 12dp - 16dp @@ -1023,8 +1025,8 @@ 16dp - - 8dp + + 1dp 0dp diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 3aba6a08662e8200d158ab0cf4a718fcb1530a45..0411d015fd6388d9449853447a4b3e53ca03db51 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -79,6 +79,9 @@ Turn on Battery Saver? + + About Battery Saver + Turn on @@ -2342,18 +2345,6 @@ Applications are using your %s. - - Got it - - - Privacy settings - - - App using your %s - - - Apps using your %s - ,\u0020 diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 46f4c8689196ee6ca9607c34e980838f6c261f2d..d0c17b7f6738cf0621fb094e6a1a12542511a041 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -112,4 +112,22 @@ public class QuickStepContract { return context.getResources().getInteger( com.android.internal.R.integer.config_navBarInteractionMode); } + + /** + * @return {@code true} if the navbar can be clicked through + */ + public static boolean isNavBarClickThrough(Context context) { + return context.getResources().getBoolean( + com.android.internal.R.bool.config_navBarTapThrough); + } + + /** + * @return the edge sensitivity width in px + */ + public static int getEdgeSensitivityWidth(Context context) { + return context.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.config_backGestureInset); + } + + } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java index c0ec405e7dc16fe3b61681922564317a8424762d..fb4fe814601fdfd030d794c82dc79eaf5b7945f9 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java @@ -99,13 +99,7 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE); } - private void showDefaultMessage() { - if (mRemainingAttempts >= 0) { - mSecurityMessageDisplay.setMessage(getPinPasswordErrorMessage( - mRemainingAttempts, true)); - return; - } - + private void setLockedSimMessage() { boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId); int count = TelephonyManager.getDefault().getSimCount(); Resources rez = getResources(); @@ -122,13 +116,20 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { color = info.getIconTint(); } } - if (isEsimLocked) { msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg); } mSecurityMessageDisplay.setMessage(msg); mSimImageView.setImageTintList(ColorStateList.valueOf(color)); + } + + private void showDefaultMessage() { + setLockedSimMessage(); + if (mRemainingAttempts >= 0) { + return; + } + // Sending empty PIN here to query the number of remaining PIN attempts new CheckSimPin("", mSubId) { @@ -137,8 +138,7 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { " attemptsRemaining=" + attemptsRemaining); if (attemptsRemaining >= 0) { mRemainingAttempts = attemptsRemaining; - mSecurityMessageDisplay.setMessage( - getPinPasswordErrorMessage(attemptsRemaining, true)); + setLockedSimMessage(); } } }.start(); diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java index 32c1242b695d459945dce0dd42ece67d1e0226f2..147def392594766493d8d1c36d7c6af9279ca01c 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java @@ -15,15 +15,19 @@ */ package com.android.keyguard.clock; +import android.app.WallpaperManager; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Color; import android.graphics.Paint.Style; import android.view.LayoutInflater; import android.view.View; import android.widget.TextClock; +import com.android.internal.colorextraction.ColorExtractor; import com.android.keyguard.R; +import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.plugins.ClockPlugin; import java.util.TimeZone; @@ -43,6 +47,16 @@ public class BubbleClockController implements ClockPlugin { */ private final LayoutInflater mLayoutInflater; + /** + * Extracts accent color from wallpaper. + */ + private final SysuiColorExtractor mColorExtractor; + + /** + * Renders preview from clock view. + */ + private final ViewPreviewer mRenderer = new ViewPreviewer(); + /** * Custom clock shown on AOD screen and behind stack scroller on lock. */ @@ -64,11 +78,15 @@ public class BubbleClockController implements ClockPlugin { /** * Create a BubbleClockController instance. * - * @param layoutInflater Inflater used to inflate custom clock views. + * @param res Resources contains title and thumbnail. + * @param inflater Inflater used to inflate custom clock views. + * @param colorExtractor Extracts accent color from wallpaper. */ - public BubbleClockController(Resources res, LayoutInflater inflater) { + public BubbleClockController(Resources res, LayoutInflater inflater, + SysuiColorExtractor colorExtractor) { mResources = res; mLayoutInflater = inflater; + mColorExtractor = colorExtractor; } private void createViews() { @@ -98,6 +116,23 @@ public class BubbleClockController implements ClockPlugin { return BitmapFactory.decodeResource(mResources, R.drawable.bubble_thumbnail); } + @Override + public Bitmap getPreview(int width, int height) { + + // Use the big clock view for the preview + View view = getBigClockView(); + + // Initialize state of plugin before generating preview. + setDarkAmount(1f); + setTextColor(Color.WHITE); + ColorExtractor.GradientColors colors = mColorExtractor.getColors( + WallpaperManager.FLAG_LOCK, true); + setColorPalette(colors.supportsDarkText(), colors.getColorPalette()); + onTimeTick(); + + return mRenderer.createPreview(view, width, height); + } + @Override public View getView() { if (mLockClockContainer == null) { diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index 8ad5c7b90882c4f491acf6e4b9e80b233ec1af99..d0fff749972db3faf92ec9f2c1a119a442cdbc1e 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -16,29 +16,19 @@ package com.android.keyguard.clock; import android.annotation.Nullable; -import android.app.WallpaperManager; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.Canvas; -import android.graphics.Color; import android.os.Handler; import android.os.Looper; import android.provider.Settings; import android.util.ArrayMap; import android.util.DisplayMetrics; -import android.util.Log; import android.view.LayoutInflater; -import android.view.View; -import android.view.View.MeasureSpec; -import android.view.ViewGroup; import androidx.annotation.VisibleForTesting; -import com.android.internal.colorextraction.ColorExtractor; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dock.DockManager; @@ -51,8 +41,6 @@ import com.android.systemui.util.InjectionInflationController; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.FutureTask; import javax.inject.Inject; import javax.inject.Singleton; @@ -128,7 +116,6 @@ public final class ClockManager { private final List mListeners = new ArrayList<>(); - private final SysuiColorExtractor mColorExtractor; private final int mWidth; private final int mHeight; @@ -144,17 +131,16 @@ public final class ClockManager { ContentResolver contentResolver, SettingsWrapper settingsWrapper) { mContext = context; mPluginManager = pluginManager; - mColorExtractor = colorExtractor; mContentResolver = contentResolver; mSettingsWrapper = settingsWrapper; Resources res = context.getResources(); LayoutInflater layoutInflater = injectionInflater.injectable(LayoutInflater.from(context)); - addClockPlugin(new DefaultClockController(res, layoutInflater)); - addClockPlugin(new BubbleClockController(res, layoutInflater)); - addClockPlugin(new StretchAnalogClockController(res, layoutInflater)); - addClockPlugin(new TypeClockController(res, layoutInflater)); + addClockPlugin(new DefaultClockController(res, layoutInflater, colorExtractor)); + addClockPlugin(new BubbleClockController(res, layoutInflater, colorExtractor)); + addClockPlugin(new StretchAnalogClockController(res, layoutInflater, colorExtractor)); + addClockPlugin(new TypeClockController(res, layoutInflater, colorExtractor)); // Store the size of the display for generation of clock preview. DisplayMetrics dm = res.getDisplayMetrics(); @@ -217,7 +203,7 @@ public final class ClockManager { .setTitle(plugin.getTitle()) .setId(id) .setThumbnail(() -> plugin.getThumbnail()) - .setPreview(() -> getClockPreview(id)) + .setPreview(() -> plugin.getPreview(mWidth, mHeight)) .build()); } @@ -232,81 +218,6 @@ public final class ClockManager { } } - /** - * Generate a realistic preview of a clock face. - * @param clockId ID of clock to use for preview, should be obtained from {@link getClockInfos}. - * Returns null if clockId is not found. - */ - @Nullable - private Bitmap getClockPreview(String clockId) { - FutureTask task = new FutureTask<>(new Callable() { - @Override - public Bitmap call() { - Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_8888); - ClockPlugin plugin = mClocks.get(clockId); - if (plugin == null) { - return null; - } - - // Use the big clock view for the preview - View clockView = plugin.getBigClockView(); - if (clockView == null) { - return null; - } - - // Initialize state of plugin before generating preview. - plugin.setDarkAmount(1f); - plugin.setTextColor(Color.WHITE); - - ColorExtractor.GradientColors colors = mColorExtractor.getColors( - WallpaperManager.FLAG_LOCK, true); - plugin.setColorPalette(colors.supportsDarkText(), colors.getColorPalette()); - plugin.onTimeTick(); - - // Draw clock view hierarchy to canvas. - Canvas canvas = new Canvas(bitmap); - canvas.drawColor(Color.BLACK); - dispatchVisibilityAggregated(clockView, true); - clockView.measure(MeasureSpec.makeMeasureSpec(mWidth, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY)); - clockView.layout(0, 0, mWidth, mHeight); - clockView.draw(canvas); - return bitmap; - } - }); - - if (Looper.myLooper() == Looper.getMainLooper()) { - task.run(); - } else { - mMainHandler.post(task); - } - - try { - return task.get(); - } catch (Exception e) { - Log.e(TAG, "Error completing task", e); - return null; - } - } - - private void dispatchVisibilityAggregated(View view, boolean isVisible) { - // Similar to View.dispatchVisibilityAggregated implementation. - final boolean thisVisible = view.getVisibility() == View.VISIBLE; - if (thisVisible || !isVisible) { - view.onVisibilityAggregated(isVisible); - } - - if (view instanceof ViewGroup) { - isVisible = thisVisible && isVisible; - ViewGroup vg = (ViewGroup) view; - int count = vg.getChildCount(); - - for (int i = 0; i < count; i++) { - dispatchVisibilityAggregated(vg.getChildAt(i), isVisible); - } - } - } - private void notifyClockChanged(ClockPlugin plugin) { for (int i = 0; i < mListeners.size(); i++) { // It probably doesn't make sense to supply the same plugin instances to multiple diff --git a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java index 8a6a4cd9599181542161b06bb65e345bd911d4c1..73414b30432f6dd2526fb8e8da4414d111bdf9cb 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java @@ -15,15 +15,19 @@ */ package com.android.keyguard.clock; +import android.app.WallpaperManager; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Color; import android.graphics.Paint.Style; import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; +import com.android.internal.colorextraction.ColorExtractor; import com.android.keyguard.R; +import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.plugins.ClockPlugin; import java.util.TimeZone; @@ -43,6 +47,16 @@ public class DefaultClockController implements ClockPlugin { */ private final LayoutInflater mLayoutInflater; + /** + * Extracts accent color from wallpaper. + */ + private final SysuiColorExtractor mColorExtractor; + + /** + * Renders preview from clock view. + */ + private final ViewPreviewer mRenderer = new ViewPreviewer(); + /** * Root view of preview. */ @@ -61,11 +75,15 @@ public class DefaultClockController implements ClockPlugin { /** * Create a DefaultClockController instance. * + * @param res Resources contains title and thumbnail. * @param inflater Inflater used to inflate custom clock views. + * @param colorExtractor Extracts accent color from wallpaper. */ - public DefaultClockController(Resources res, LayoutInflater inflater) { + public DefaultClockController(Resources res, LayoutInflater inflater, + SysuiColorExtractor colorExtractor) { mResources = res; mLayoutInflater = inflater; + mColorExtractor = colorExtractor; } private void createViews() { @@ -89,6 +107,23 @@ public class DefaultClockController implements ClockPlugin { return BitmapFactory.decodeResource(mResources, R.drawable.default_thumbnail); } + @Override + public Bitmap getPreview(int width, int height) { + + // Use the big clock view for the preview + View view = getBigClockView(); + + // Initialize state of plugin before generating preview. + setDarkAmount(1f); + setTextColor(Color.WHITE); + ColorExtractor.GradientColors colors = mColorExtractor.getColors( + WallpaperManager.FLAG_LOCK, true); + setColorPalette(colors.supportsDarkText(), colors.getColorPalette()); + onTimeTick(); + + return mRenderer.createPreview(view, width, height); + } + @Override public View getView() { return null; diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java index 34b2fd86f648b95acb25425cdab2618796cc427e..ea9f0cd3c17d56a3c6c81065f59cd95b4b622705 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java @@ -15,15 +15,19 @@ */ package com.android.keyguard.clock; +import android.app.WallpaperManager; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Color; import android.graphics.Paint.Style; import android.view.LayoutInflater; import android.view.View; import android.widget.TextClock; +import com.android.internal.colorextraction.ColorExtractor; import com.android.keyguard.R; +import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.plugins.ClockPlugin; import java.util.TimeZone; @@ -43,6 +47,16 @@ public class StretchAnalogClockController implements ClockPlugin { */ private final LayoutInflater mLayoutInflater; + /** + * Extracts accent color from wallpaper. + */ + private final SysuiColorExtractor mColorExtractor; + + /** + * Renders preview from clock view. + */ + private final ViewPreviewer mRenderer = new ViewPreviewer(); + /** * Custom clock shown on AOD screen and behind stack scroller on lock. */ @@ -64,11 +78,15 @@ public class StretchAnalogClockController implements ClockPlugin { /** * Create a BubbleClockController instance. * - * @param layoutInflater Inflater used to inflate custom clock views. + * @param res Resources contains title and thumbnail. + * @param inflater Inflater used to inflate custom clock views. + * @param colorExtractor Extracts accent color from wallpaper. */ - public StretchAnalogClockController(Resources res, LayoutInflater inflater) { + public StretchAnalogClockController(Resources res, LayoutInflater inflater, + SysuiColorExtractor colorExtractor) { mResources = res; mLayoutInflater = inflater; + mColorExtractor = colorExtractor; } private void createViews() { @@ -98,6 +116,23 @@ public class StretchAnalogClockController implements ClockPlugin { return BitmapFactory.decodeResource(mResources, R.drawable.stretch_thumbnail); } + @Override + public Bitmap getPreview(int width, int height) { + + // Use the big clock view for the preview + View view = getBigClockView(); + + // Initialize state of plugin before generating preview. + setDarkAmount(1f); + setTextColor(Color.WHITE); + ColorExtractor.GradientColors colors = mColorExtractor.getColors( + WallpaperManager.FLAG_LOCK, true); + setColorPalette(colors.supportsDarkText(), colors.getColorPalette()); + onTimeTick(); + + return mRenderer.createPreview(view, width, height); + } + @Override public View getView() { if (mView == null) { diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java index 387f2656c6f8115b3262ccfbf042988e93771e50..67c0989b49c43d6bc46ff1b962a767dec11bebbe 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java @@ -15,14 +15,18 @@ */ package com.android.keyguard.clock; +import android.app.WallpaperManager; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Color; import android.graphics.Paint.Style; import android.view.LayoutInflater; import android.view.View; +import com.android.internal.colorextraction.ColorExtractor; import com.android.keyguard.R; +import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.plugins.ClockPlugin; import java.util.TimeZone; @@ -42,6 +46,16 @@ public class TypeClockController implements ClockPlugin { */ private final LayoutInflater mLayoutInflater; + /** + * Extracts accent color from wallpaper. + */ + private final SysuiColorExtractor mColorExtractor; + + /** + * Renders preview from clock view. + */ + private final ViewPreviewer mRenderer = new ViewPreviewer(); + /** * Custom clock shown on AOD screen and behind stack scroller on lock. */ @@ -61,11 +75,15 @@ public class TypeClockController implements ClockPlugin { /** * Create a TypeClockController instance. * + * @param res Resources contains title and thumbnail. * @param inflater Inflater used to inflate custom clock views. + * @param colorExtractor Extracts accent color from wallpaper. */ - TypeClockController(Resources res, LayoutInflater inflater) { + TypeClockController(Resources res, LayoutInflater inflater, + SysuiColorExtractor colorExtractor) { mResources = res; mLayoutInflater = inflater; + mColorExtractor = colorExtractor; } private void createViews() { @@ -95,6 +113,23 @@ public class TypeClockController implements ClockPlugin { return BitmapFactory.decodeResource(mResources, R.drawable.type_thumbnail); } + @Override + public Bitmap getPreview(int width, int height) { + + // Use the big clock view for the preview + View view = getBigClockView(); + + // Initialize state of plugin before generating preview. + setDarkAmount(1f); + setTextColor(Color.WHITE); + ColorExtractor.GradientColors colors = mColorExtractor.getColors( + WallpaperManager.FLAG_LOCK, true); + setColorPalette(colors.supportsDarkText(), colors.getColorPalette()); + onTimeTick(); + + return mRenderer.createPreview(view, width, height); + } + @Override public View getView() { if (mLockClock == null) { diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ViewPreviewer.java b/packages/SystemUI/src/com/android/keyguard/clock/ViewPreviewer.java new file mode 100644 index 0000000000000000000000000000000000000000..abd0dd28dabc972f5ebec356d66b4dfc6c1f68f4 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/ViewPreviewer.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.keyguard.clock; + +import android.annotation.Nullable; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +import java.util.concurrent.Callable; +import java.util.concurrent.FutureTask; + +/** + * Creates a preview image ({@link Bitmap}) of a {@link View} for a custom clock face. + */ +final class ViewPreviewer { + + private static final String TAG = "ViewPreviewer"; + + /** + * Handler used to run {@link View#draw(Canvas)} on the main thread. + */ + private final Handler mMainHandler = new Handler(Looper.getMainLooper()); + + /** + * Generate a realistic preview of a clock face. + * + * @param view view is used to generate preview image. + * @param width width of the preview image, should be the same as device width in pixels. + * @param height height of the preview image, should be the same as device height in pixels. + * @return bitmap of view. + */ + @Nullable + Bitmap createPreview(View view, int width, int height) { + if (view == null) { + return null; + } + FutureTask task = new FutureTask<>(new Callable() { + @Override + public Bitmap call() { + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + + // Draw clock view hierarchy to canvas. + Canvas canvas = new Canvas(bitmap); + canvas.drawColor(Color.BLACK); + dispatchVisibilityAggregated(view, true); + view.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); + view.layout(0, 0, width, height); + view.draw(canvas); + + return bitmap; + } + }); + + if (Looper.myLooper() == Looper.getMainLooper()) { + task.run(); + } else { + mMainHandler.post(task); + } + + try { + return task.get(); + } catch (Exception e) { + Log.e(TAG, "Error completing task", e); + return null; + } + } + + private void dispatchVisibilityAggregated(View view, boolean isVisible) { + // Similar to View.dispatchVisibilityAggregated implementation. + final boolean thisVisible = view.getVisibility() == View.VISIBLE; + if (thisVisible || !isVisible) { + view.onVisibilityAggregated(isVisible); + } + + if (view instanceof ViewGroup) { + isVisible = thisVisible && isVisible; + ViewGroup vg = (ViewGroup) view; + int count = vg.getChildCount(); + + for (int i = 0; i < count; i++) { + dispatchVisibilityAggregated(vg.getChildAt(i), isVisible); + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index ace086f7af2c2ca1237ba584950b6e74744853eb..3fc6689b2f195db6fa56cf15bb4229d1ce762111 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -204,23 +204,58 @@ public class AppOpsControllerImpl implements AppOpsController, } /** - * Does the app-op item refer to a user sensitive permission. Only user sensitive permission - * should be shown to the user by default. + * Does the app-op code refer to a user sensitive permission for the specified user id + * and package. Only user sensitive permission should be shown to the user by default. * - * @param item The item + * @param appOpCode The code of the app-op. + * @param uid The uid of the user. + * @param packageName The name of the package. * * @return {@code true} iff the app-op item is user sensitive */ - private boolean isUserSensitive(AppOpItem item) { - String permission = AppOpsManager.opToPermission(item.getCode()); + private boolean isUserSensitive(int appOpCode, int uid, String packageName) { + String permission = AppOpsManager.opToPermission(appOpCode); if (permission == null) { return false; } int permFlags = mContext.getPackageManager().getPermissionFlags(permission, - item.getPackageName(), UserHandle.getUserHandleForUid(item.getUid())); + packageName, UserHandle.getUserHandleForUid(uid)); return (permFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0; } + /** + * Does the app-op item refer to an operation that should be shown to the user. + * Only specficic ops (like SYSTEM_ALERT_WINDOW) or ops that refer to user sensitive + * permission should be shown to the user by default. + * + * @param item The item + * + * @return {@code true} iff the app-op item should be shown to the user + */ + private boolean isUserVisible(AppOpItem item) { + return isUserVisible(item.getCode(), item.getUid(), item.getPackageName()); + } + + + /** + * Does the app-op, uid and package name, refer to an operation that should be shown to the + * user. Only specficic ops (like {@link AppOpsManager.OP_SYSTEM_ALERT_WINDOW}) or + * ops that refer to user sensitive permission should be shown to the user by default. + * + * @param item The item + * + * @return {@code true} iff the app-op for should be shown to the user + */ + private boolean isUserVisible(int appOpCode, int uid, String packageName) { + // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission + // which may be user senstive, so for now always show it to the user. + if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) { + return true; + } + + return isUserSensitive(appOpCode, uid, packageName); + } + /** * Returns a copy of the list containing all the active AppOps that the controller tracks. * @@ -245,7 +280,7 @@ public class AppOpsControllerImpl implements AppOpsController, for (int i = 0; i < numActiveItems; i++) { AppOpItem item = mActiveItems.get(i); if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId) - && isUserSensitive(item)) { + && isUserVisible(item)) { list.add(item); } } @@ -255,7 +290,7 @@ public class AppOpsControllerImpl implements AppOpsController, for (int i = 0; i < numNotedItems; i++) { AppOpItem item = mNotedItems.get(i); if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId) - && isUserSensitive(item)) { + && isUserVisible(item)) { list.add(item); } } @@ -281,7 +316,8 @@ public class AppOpsControllerImpl implements AppOpsController, } private void notifySuscribers(int code, int uid, String packageName, boolean active) { - if (mCallbacksByCode.containsKey(code)) { + if (mCallbacksByCode.containsKey(code) + && isUserVisible(code, uid, packageName)) { for (Callback cb: mCallbacksByCode.get(code)) { cb.onActiveStateChanged(code, uid, packageName, active); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java index 1154515b261c864e86f0198f5240526edbbc48b8..98f446dea9b124f4b749ca8df32f8299ac793ede 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java @@ -18,8 +18,6 @@ package com.android.systemui.bubbles; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Path; import android.graphics.Point; import android.graphics.Rect; import android.util.AttributeSet; @@ -36,11 +34,9 @@ public class BadgedImageView extends ImageView { private int mIconSize; private Rect mTempBounds = new Rect(); private Point mTempPoint = new Point(); - private Path mClipPath = new Path(); private float mDotScale = 0f; private int mUpdateDotColor; - private int mBubbleDefaultBgColor; private boolean mShowUpdateDot; private boolean mOnLeft; @@ -59,32 +55,17 @@ public class BadgedImageView extends ImageView { public BadgedImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - setScaleType(ScaleType.CENTER_CROP); mIconSize = getResources().getDimensionPixelSize(R.dimen.individual_bubble_size); mDotRenderer = new BadgeRenderer(mIconSize); TypedArray ta = context.obtainStyledAttributes( new int[] {android.R.attr.colorBackgroundFloating}); - mBubbleDefaultBgColor = ta.getColor(0, Color.WHITE); ta.recycle(); } - // TODO: Clipping oval path isn't great: rerender image into a separate, rounded bitmap and - // then draw would be better @Override public void onDraw(Canvas canvas) { - canvas.save(); - // Circle crop - mClipPath.addOval(getPaddingStart(), getPaddingTop(), - getWidth() - getPaddingEnd(), getHeight() - getPaddingBottom(), Path.Direction.CW); - canvas.clipPath(mClipPath); - canvas.drawColor(mBubbleDefaultBgColor); super.onDraw(canvas); - - // After we've circle cropped what we're showing, restore so we don't clip the badge - canvas.restore(); - - // Draw the badge if (mShowUpdateDot) { getDrawingRect(mTempBounds); mTempPoint.set((getWidth() - mIconSize) / 2, getPaddingTop()); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 4a2731e5437a069e1058a66052d213a1e042b328..3aa9f73939acbd1b9a52a3fda9ee0154d9a1a44d 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -26,6 +26,7 @@ import static com.android.systemui.statusbar.notification.NotificationAlertingMa import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.Nullable; +import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; @@ -64,6 +65,7 @@ import com.android.systemui.statusbar.phone.StatusBarWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; import java.lang.annotation.Retention; +import java.util.List; import javax.inject.Inject; import javax.inject.Singleton; @@ -80,8 +82,6 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe private static final String TAG = "BubbleController"; - private static final int MAX_BUBBLES = 5; // TODO: actually enforce this - @Retention(SOURCE) @IntDef({DISMISS_USER_GESTURE, DISMISS_AGED, DISMISS_TASK_FINISHED, DISMISS_BLOCKED, DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION}) @@ -94,6 +94,8 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe static final int DISMISS_NOTIF_CANCEL = 5; static final int DISMISS_ACCESSIBILITY_ACTION = 6; + static final int MAX_BUBBLES = 5; // TODO: actually enforce this + // Enables some subset of notifs to automatically become bubbles private static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false; @@ -290,6 +292,17 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe } } + /** + * Request the stack expand if needed, then select the specified Bubble as current. + * + * @param notificationKey the notification key for the bubble to be selected + */ + public void expandStackAndSelectBubble(String notificationKey) { + if (mStackView != null && mBubbleData.getBubble(notificationKey) != null) { + mStackView.setExpandedBubble(notificationKey); + } + } + /** * Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack. */ @@ -340,6 +353,9 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe // It's new mStackView.addBubble(notif); } + if (shouldAutoExpand(notif)) { + mStackView.setExpandedBubble(notif); + } updateVisibility(); } @@ -379,9 +395,11 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe } if (shouldAutoBubbleForFlags(mContext, entry) || shouldBubble(entry)) { // TODO: handle group summaries - // It's a new notif, it shows in the shade and as a bubble entry.setIsBubble(true); - entry.setShowInShadeWhenBubble(true); + boolean suppressNotification = entry.getBubbleMetadata() != null + && entry.getBubbleMetadata().getSuppressInitialNotification() + && isForegroundApp(entry.notification.getPackageName()); + entry.setShowInShadeWhenBubble(!suppressNotification); } } @@ -522,6 +540,23 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe || autoBubbleAll; } + private boolean shouldAutoExpand(NotificationEntry entry) { + Notification.BubbleMetadata metadata = entry.getBubbleMetadata(); + return metadata != null && metadata.getAutoExpandBubble() + && isForegroundApp(entry.notification.getPackageName()); + } + + /** + * Return true if the applications with the package name is running in foreground. + * + * @param pkgName application package name. + */ + private boolean isForegroundApp(String pkgName) { + ActivityManager am = mContext.getSystemService(ActivityManager.class); + List tasks = am.getRunningTasks(1 /* maxNum */); + return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName()); + } + /** * This task stack listener is responsible for responding to tasks moved to the front * which are on the default (main) display. When this happens, expanded bubbles must be diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 8e3afd8bcae092e588df6ba119efc9913a2a05dc..6c2db76e19e177c18e65ca0b6bacf14ae815960f 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -38,8 +38,11 @@ import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Insets; import android.graphics.Point; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.InsetDrawable; import android.graphics.drawable.ShapeDrawable; import android.os.RemoteException; import android.os.ServiceManager; @@ -53,7 +56,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowInsets; import android.widget.FrameLayout; -import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -80,10 +82,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList private View mPointerView; private int mPointerMargin; - // Header - private View mHeaderView; - private ImageButton mDeepLinkIcon; - private ImageButton mSettingsIcon; + private ImageView mSettingsIcon; // Permission view private View mPermissionView; @@ -195,7 +194,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList mPointerView.setBackground(triangleDrawable); FrameLayout viewWrapper = findViewById(R.id.header_permission_wrapper); - viewWrapper.setBackground(createHeaderPermissionBackground(bgColor)); LayoutTransition transition = new LayoutTransition(); transition.setDuration(200); @@ -212,18 +210,16 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList viewWrapper.setLayoutTransition(transition); viewWrapper.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING); - mHeaderHeight = getContext().getResources().getDimensionPixelSize( R.dimen.bubble_expanded_header_height); mPermissionHeight = getContext().getResources().getDimensionPixelSize( R.dimen.bubble_permission_height); - mHeaderView = findViewById(R.id.header_layout); - mDeepLinkIcon = findViewById(R.id.deep_link_button); + + mPermissionView = findViewById(R.id.permission_layout); mSettingsIcon = findViewById(R.id.settings_button); - mDeepLinkIcon.setOnClickListener(this); mSettingsIcon.setOnClickListener(this); + updateHeaderColor(); - mPermissionView = findViewById(R.id.permission_layout); findViewById(R.id.no_bubbles_button).setOnClickListener(this); findViewById(R.id.yes_bubbles_button).setOnClickListener(this); @@ -381,17 +377,31 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList } /** - * Update bubble expanded view header when user toggles dark mode. + * Update header color and icon shape when theme changes. */ void updateHeaderColor() { - mHeaderView.setBackgroundColor(mContext.getColor(R.attr.colorAccent)); + TypedArray ta = mContext.obtainStyledAttributes( + new int[] {android.R.attr.colorBackgroundFloating, android.R.attr.colorForeground}); + int backgroundColor = ta.getColor(0, Color.WHITE /* default */); + int foregroundColor = ta.getColor(1, Color.BLACK /* default */); + ta.recycle(); + + mPermissionView.setBackground(createHeaderPermissionBackground(backgroundColor)); + + Drawable settingsIcon = mSettingsIcon.getDrawable(); + settingsIcon.setTint(foregroundColor); + + int mIconInset = getResources().getDimensionPixelSize(R.dimen.bubble_icon_inset); + InsetDrawable foreground = new InsetDrawable(settingsIcon, mIconInset); + ColorDrawable background = new ColorDrawable(backgroundColor); + AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(background, + foreground); + mSettingsIcon.setImageDrawable(adaptiveIcon); } private void updateHeaderView() { mSettingsIcon.setContentDescription(getResources().getString( R.string.bubbles_settings_button_description, mAppName)); - mDeepLinkIcon.setContentDescription(getResources().getString( - R.string.bubbles_deep_link_button_description, mAppName)); } private void updatePermissionView() { @@ -404,16 +414,14 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList Log.w(TAG, e); } if (hasUserApprovedBubblesForPackage) { - mHeaderView.setVisibility(VISIBLE); - mPermissionView.setVisibility(GONE); + showSettingsIcon(); } else { - mHeaderView.setVisibility(GONE); - mPermissionView.setVisibility(VISIBLE); + showPermissionView(); ((ImageView) mPermissionView.findViewById(R.id.pkgicon)).setImageDrawable(mAppIcon); ((TextView) mPermissionView.findViewById(R.id.pkgname)).setText(mAppName); ((TextView) mPermissionView.findViewById(R.id.prompt)).setText( getResources().getString(R.string.bubbles_prompt, mAppName)); - logBubbleClickEvent(mEntry.notification, + logBubbleClickEvent(mEntry, StatsLog.BUBBLE_UICHANGED__ACTION__PERMISSION_DIALOG_SHOWN); } } @@ -437,7 +445,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList } boolean performBackPressIfNeeded() { - if (mActivityView == null || !usingActivityView()) { + if (!usingActivityView()) { return false; } mActivityView.performBackPress(); @@ -505,23 +513,12 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList } Notification n = mEntry.notification.getNotification(); int id = view.getId(); - if (id == R.id.deep_link_button) { - mStackView.collapseStack(() -> { - try { - n.contentIntent.send(); - logBubbleClickEvent(mEntry.notification, - StatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_APP); - } catch (PendingIntent.CanceledException e) { - Log.w(TAG, "Failed to send intent for bubble with key: " - + (mEntry != null ? mEntry.key : " null entry")); - } - }); - } else if (id == R.id.settings_button) { + if (id == R.id.settings_button) { Intent intent = getSettingsIntent(mEntry.notification.getPackageName(), mEntry.notification.getUid()); mStackView.collapseStack(() -> { mContext.startActivity(intent); - logBubbleClickEvent(mEntry.notification, + logBubbleClickEvent(mEntry, StatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS); }); } else if (id == R.id.no_bubbles_button) { @@ -538,13 +535,12 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList mEntry.notification.getUid(), allowed); if (allowed) { - mPermissionView.setVisibility(GONE); - mHeaderView.setVisibility(VISIBLE); + showSettingsIcon(); } else if (mOnBubbleBlockedListener != null) { mOnBubbleBlockedListener.onBubbleBlocked(mEntry); } mStackView.onExpandedHeightChanged(); - logBubbleClickEvent(mEntry.notification, + logBubbleClickEvent(mEntry, allowed ? StatsLog.BUBBLE_UICHANGED__ACTION__PERMISSION_OPT_IN : StatsLog.BUBBLE_UICHANGED__ACTION__PERMISSION_OPT_OUT); } catch (RemoteException e) { @@ -552,6 +548,17 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList } } + void showSettingsIcon() { + mPermissionView.setVisibility(GONE); + mSettingsIcon.setVisibility(VISIBLE); + } + + void showPermissionView() { + mSettingsIcon.setVisibility(GONE); + mPermissionView.setVisibility(VISIBLE); + + } + /** * Update appearance of the expanded view being displayed. */ @@ -593,7 +600,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList } private boolean usingActivityView() { - return mBubbleIntent != null; + return mBubbleIntent != null && mActivityView != null; } private void applyRowState(ExpandableNotificationRow view) { @@ -707,10 +714,11 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList /** * Logs bubble UI click event. * - * @param notification the bubble notification that user is interacting with. + * @param entry the bubble notification entry that user is interacting with. * @param action the user interaction enum. */ - private void logBubbleClickEvent(StatusBarNotification notification, int action) { + private void logBubbleClickEvent(NotificationEntry entry, int action) { + StatusBarNotification notification = entry.notification; StatsLog.write(StatsLog.BUBBLE_UI_CHANGED, notification.getPackageName(), notification.getNotification().getChannelId(), @@ -719,7 +727,8 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList mStackView.getBubbleCount(), action, mStackView.getNormalizedXPosition(), - mStackView.getNormalizedYPosition()); + mStackView.getNormalizedYPosition(), + entry.showInShadeWhenBubble()); } private int getDimenForPackageUser(int resId, String pkg, int userId) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 580acb82ebc1ad4086629d2454b95da61f8f3ffb..be55829869eb1c7735c9f14400681f1cad6815a4 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -52,6 +52,7 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.ViewClippingUtil; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController.DismissReason; import com.android.systemui.bubbles.animation.ExpandedAnimationController; @@ -135,7 +136,7 @@ public class BubbleStackView extends FrameLayout { private BubbleExpandedView.OnBubbleBlockedListener mBlockedListener; private boolean mViewUpdatedRequested = false; - private boolean mIsAnimating = false; + private boolean mIsExpansionAnimating = false; private LayoutInflater mInflater; @@ -233,6 +234,11 @@ public class BubbleStackView extends FrameLayout { new SpringForce() .setStiffness(SpringForce.STIFFNESS_LOW) .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)); + mExpandedViewYAnim.addEndListener((anim, cancelled, value, velocity) -> { + if (mIsExpanded && mExpandedBubble != null) { + mExpandedBubble.expandedView.updateView(); + } + }); setClipChildren(false); setFocusable(true); @@ -241,7 +247,7 @@ public class BubbleStackView extends FrameLayout { setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> { final int keyboardHeight = insets.getSystemWindowInsetBottom() - insets.getStableInsetBottom(); - if (!mIsExpanded) { + if (!mIsExpanded || mIsExpansionAnimating) { return view.onApplyWindowInsets(insets); } mImeVisible = keyboardHeight != 0; @@ -265,7 +271,9 @@ public class BubbleStackView extends FrameLayout { * Handle config changes. */ public void onConfigChanged() { - mExpandedBubble.expandedView.updateHeaderColor(); + for (Bubble b: mBubbleData.getBubbles()) { + b.expandedView.updateHeaderColor(); + } } @Override @@ -309,7 +317,8 @@ public class BubbleStackView extends FrameLayout { } switch (action) { case AccessibilityNodeInfo.ACTION_DISMISS: - stackDismissed(BubbleController.DISMISS_ACCESSIBILITY_ACTION); + Dependency.get(BubbleController.class).dismissStack( + BubbleController.DISMISS_ACCESSIBILITY_ACTION); return true; case AccessibilityNodeInfo.ACTION_COLLAPSE: collapseStack(); @@ -415,7 +424,7 @@ public class BubbleStackView extends FrameLayout { * Sets the entry that should be expanded and expands if needed. */ @VisibleForTesting - public void setExpandedBubble(NotificationEntry entry) { + void setExpandedBubble(NotificationEntry entry) { for (int i = 0; i < mBubbleContainer.getChildCount(); i++) { BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i); if (entry.equals(bv.getEntry())) { @@ -429,7 +438,7 @@ public class BubbleStackView extends FrameLayout { * * @param entry the notification to add to the stack of bubbles. */ - public void addBubble(NotificationEntry entry) { + void addBubble(NotificationEntry entry) { Bubble b = new Bubble(entry, mInflater, this /* stackView */, mBlockedListener); mBubbleData.addBubble(b); @@ -444,12 +453,17 @@ public class BubbleStackView extends FrameLayout { /** * Remove a bubble from the stack. */ - public void removeBubble(String key, int reason) { + void removeBubble(String key, int reason) { Bubble b = mBubbleData.removeBubble(key); if (b == null) { return; } - int removedIndex = dismissBubble(b, reason); + setBubbleDismissed(b, reason); + + // Remove it from the views + int removedIndex = mBubbleContainer.indexOfChild(b.iconView); + mBubbleContainer.removeViewAt(removedIndex); + int bubbleCount = mBubbleContainer.getChildCount(); if (bubbleCount == 0) { // If no bubbles remain, collapse the entire stack. @@ -474,9 +488,9 @@ public class BubbleStackView extends FrameLayout { /** * Dismiss the stack of bubbles. */ - public void stackDismissed(int reason) { + void stackDismissed(int reason) { for (Bubble bubble : mBubbleData.getBubbles()) { - dismissBubble(bubble, reason); + setBubbleDismissed(bubble, reason); } mBubbleData.clear(); collapseStack(); @@ -488,8 +502,7 @@ public class BubbleStackView extends FrameLayout { } /** - * Marks the notification entry as dismissed, cleans up Bubble icon and expanded view UI - * elements and calls deleteIntent if necessary. + * Marks the notification entry as dismissed & calls any delete intents for the bubble. * *

      Note: This does not remove the Bubble from BubbleData. * @@ -497,17 +510,13 @@ public class BubbleStackView extends FrameLayout { * @param reason code for the reason the dismiss was triggered * @see BubbleController.DismissReason */ - private int dismissBubble(Bubble bubble, @DismissReason int reason) { + private void setBubbleDismissed(Bubble bubble, @DismissReason int reason) { if (DEBUG) { Log.d(TAG, "dismissBubble: " + bubble + " reason=" + reason); } bubble.entry.setBubbleDismissed(true); bubble.expandedView.cleanUpExpandedState(); - // Remove it from the views - int removedIndex = mBubbleContainer.indexOfChild(bubble.iconView); - mBubbleContainer.removeViewAt(removedIndex); - if (reason == BubbleController.DISMISS_USER_GESTURE) { Notification.BubbleMetadata bubbleMetadata = bubble.entry.getBubbleMetadata(); PendingIntent deleteIntent = bubbleMetadata != null @@ -522,7 +531,6 @@ public class BubbleStackView extends FrameLayout { } } } - return removedIndex; } /** @@ -618,24 +626,23 @@ public class BubbleStackView extends FrameLayout { updateExpandedBubble(); applyCurrentState(); - mIsAnimating = true; + mIsExpansionAnimating = true; Runnable updateAfter = () -> { applyCurrentState(); - mIsAnimating = false; + mIsExpansionAnimating = false; requestUpdate(); }; if (shouldExpand) { mBubbleContainer.setController(mExpandedAnimationController); mExpandedAnimationController.expandFromStack( - /* collapseTo */ - mStackAnimationController.getStackPositionAlongNearestHorizontalEdge(), - /* after */ + mStackAnimationController.getStackPositionAlongNearestHorizontalEdge() + /* collapseTo */, () -> { updatePointerPosition(); updateAfter.run(); - }); + } /* after */); } else { mBubbleContainer.cancelAllAnimations(); mExpandedAnimationController.collapseBackToStack( @@ -700,7 +707,7 @@ public class BubbleStackView extends FrameLayout { /** Called with the coordinates to which an individual bubble has been dragged. */ public void onBubbleDragged(View bubble, float x, float y) { - if (!mIsExpanded || mIsAnimating) { + if (!mIsExpanded || mIsExpansionAnimating) { return; } @@ -710,7 +717,7 @@ public class BubbleStackView extends FrameLayout { /** Called when a drag operation on an individual bubble has finished. */ public void onBubbleDragFinish( View bubble, float x, float y, float velX, float velY, boolean dismissed) { - if (!mIsExpanded || mIsAnimating) { + if (!mIsExpanded || mIsExpansionAnimating) { return; } @@ -722,7 +729,7 @@ public class BubbleStackView extends FrameLayout { } void onDragStart() { - if (mIsExpanded || mIsAnimating) { + if (mIsExpanded || mIsExpansionAnimating) { return; } @@ -731,7 +738,7 @@ public class BubbleStackView extends FrameLayout { } void onDragged(float x, float y) { - if (mIsExpanded || mIsAnimating) { + if (mIsExpanded || mIsExpansionAnimating) { return; } @@ -741,7 +748,7 @@ public class BubbleStackView extends FrameLayout { void onDragFinish(float x, float y, float velX, float velY) { // TODO: Add fling to bottom to dismiss. - if (mIsExpanded || mIsAnimating) { + if (mIsExpanded || mIsExpansionAnimating) { return; } @@ -830,7 +837,7 @@ public class BubbleStackView extends FrameLayout { } private void requestUpdate() { - if (mViewUpdatedRequested || mIsAnimating) { + if (mViewUpdatedRequested || mIsExpansionAnimating) { return; } mViewUpdatedRequested = true; @@ -850,7 +857,6 @@ public class BubbleStackView extends FrameLayout { private void applyCurrentState() { Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded); - mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE); if (mIsExpanded) { // First update the view so that it calculates a new height (ensuring the y position @@ -860,18 +866,20 @@ public class BubbleStackView extends FrameLayout { if (!mExpandedViewYAnim.isRunning()) { // We're not animating so set the value mExpandedViewContainer.setTranslationY(y); + mExpandedBubble.expandedView.updateView(); } else { - // We are animating so update the value + // We are animating so update the value; there is an end listener on the animator + // that will ensure expandedeView.updateView gets called. mExpandedViewYAnim.animateToFinalPosition(y); } - mExpandedBubble.expandedView.updateView(); } int bubbsCount = mBubbleContainer.getChildCount(); for (int i = 0; i < bubbsCount; i++) { BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i); bv.updateDotVisibility(); - bv.setZ(bubbsCount - i); + bv.setZ((BubbleController.MAX_BUBBLES + * getResources().getDimensionPixelSize(R.dimen.bubble_elevation)) - i); // Draw the shadow around the circle inscribed within the bubble's bounds. This // (intentionally) does not draw a shadow behind the update dot, which should be drawing @@ -954,7 +962,8 @@ public class BubbleStackView extends FrameLayout { getBubbleCount(), action, getNormalizedXPosition(), - getNormalizedYPosition()); + getNormalizedYPosition(), + false /* unread notification */); } else { StatusBarNotification notification = bubble.entry.notification; StatsLog.write(StatsLog.BUBBLE_UI_CHANGED, @@ -965,7 +974,8 @@ public class BubbleStackView extends FrameLayout { getBubbleCount(), action, getNormalizedXPosition(), - getNormalizedYPosition()); + getNormalizedYPosition(), + bubble.entry.showInShadeWhenBubble()); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java index 7a68be494cf9c22bb1963b050418045cd0d3ec86..3b9164d60c5cda316d50cae96514e443bff21f84 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java @@ -20,11 +20,11 @@ import android.annotation.Nullable; import android.app.Notification; import android.content.Context; import android.graphics.Color; +import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.graphics.drawable.InsetDrawable; -import android.graphics.drawable.LayerDrawable; import android.util.AttributeSet; import android.widget.FrameLayout; import android.widget.TextView; @@ -41,6 +41,9 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow public class BubbleView extends FrameLayout { private static final String TAG = "BubbleView"; + private static final int DARK_ICON_ALPHA = 180; + private static final double ICON_MIN_CONTRAST = 4.1; + private static final int DEFAULT_BACKGROUND_COLOR = Color.LTGRAY; // Same value as Launcher3 badge code private static final float WHITE_SCRIM_ALPHA = 0.54f; private Context mContext; @@ -84,7 +87,6 @@ public class BubbleView extends FrameLayout { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - updateViews(); } @Override @@ -206,12 +208,7 @@ public class BubbleView extends FrameLayout { } Drawable iconDrawable = ic.loadDrawable(mContext); if (needsTint) { - // Center icon on coloured background - iconDrawable.setTint(Color.WHITE); // TODO: dark mode - Drawable bg = new ColorDrawable(n.color); - InsetDrawable d = new InsetDrawable(iconDrawable, mIconInset); - Drawable[] layers = {bg, d}; - mBadgedImageView.setImageDrawable(new LayerDrawable(layers)); + mBadgedImageView.setImageDrawable(buildIconWithTint(iconDrawable, n.color)); } else { mBadgedImageView.setImageDrawable(iconDrawable); } @@ -220,6 +217,23 @@ public class BubbleView extends FrameLayout { animateDot(mEntry.showInShadeWhenBubble() /* showDot */); } + private Drawable buildIconWithTint(Drawable iconDrawable, int backgroundColor) { + backgroundColor = ColorUtils.setAlphaComponent(backgroundColor, 255 /* alpha */); + if (backgroundColor == Color.TRANSPARENT) { + // ColorUtils throws exception when background is translucent. + backgroundColor = DEFAULT_BACKGROUND_COLOR; + } + iconDrawable.setTint(Color.WHITE); + double contrastRatio = ColorUtils.calculateContrast(Color.WHITE, backgroundColor); + if (contrastRatio < ICON_MIN_CONTRAST) { + int dark = ColorUtils.setAlphaComponent(Color.BLACK, DARK_ICON_ALPHA); + iconDrawable.setTint(dark); + } + InsetDrawable foreground = new InsetDrawable(iconDrawable, mIconInset); + ColorDrawable background = new ColorDrawable(backgroundColor); + return new AdaptiveIconDrawable(background, foreground); + } + private int determineDominateColor(Drawable d, int defaultTint) { // XXX: should we pull from the drawable, app icon, notif tint? return ColorUtils.blendARGB(defaultTint, Color.WHITE, WHITE_SCRIM_ALPHA); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java index 8731b6b9767ce9d5bbf23125398f582dfcc5d7a5..0f659c338ce804f157081c81b2e9d56b5c1d2a9b 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java @@ -799,4 +799,9 @@ public class PhysicsAnimationLayout extends FrameLayout { } } } + + @Override + protected boolean canReceivePointerEvents() { + return false; + } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java index a4592d554f0ed6aa298ac2d323a187a5a40e725e..3c6dc73173574eb38cfd1ee88c24bf3a214a3be6 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java @@ -87,7 +87,8 @@ public class DozeDockHandler implements DozeMachine.Part { } private void requestPulseOutNow(State dozeState) { - if (dozeState == State.DOZE_REQUEST_PULSE || dozeState == State.DOZE_PULSING) { + if (dozeState == State.DOZE_REQUEST_PULSE || dozeState == State.DOZE_PULSING + || dozeState == State.DOZE_PULSING_BRIGHT) { final int pulseReason = mMachine.getPulseReason(); if (pulseReason == DozeLog.PULSE_REASON_DOCKING) { mDozeHost.stopPulsing(); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index 36e28dc0156d181b2259c6ac30466991fc2d8ad5..060765495f485bbea6909473c6bd54680db70a31 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -71,7 +71,7 @@ public class DozeFactory { new DozeScreenState(wrappedService, handler, params, wakeLock), createDozeScreenBrightness(context, wrappedService, sensorManager, host, params, handler), - new DozeWallpaperState(context, machine), + new DozeWallpaperState(context), new DozeDockHandler(context, machine, host, config, handler, dockManager) }); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java index bd7a421e97625af59d03cf77facf9112b81c0772..3c9d4a9704a0352d2b3a1136534b8e12d375fffd 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java @@ -33,7 +33,11 @@ public interface DozeHost { boolean isProvisioned(); boolean isBlockingDoze(); - void extendPulse(); + /** + * Makes a current pulse last for twice as long. + * @param reason why we're extending it. + */ + void extendPulse(int reason); void setAnimateWakeup(boolean animateWakeup); void setAnimateScreenOff(boolean animateScreenOff); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java index c243899c6bc9db9f892c0e1fbd1442c5819bfd78..8bf2256a4f80dd101ba02090b0d77ca35342c8d1 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java @@ -59,6 +59,8 @@ public class DozeMachine { DOZE_REQUEST_PULSE, /** Pulse is showing. Device is awake and showing UI. */ DOZE_PULSING, + /** Pulse is showing with bright wallpaper. Device is awake and showing UI. */ + DOZE_PULSING_BRIGHT, /** Pulse is done showing. Followed by transition to DOZE or DOZE_AOD. */ DOZE_PULSE_DONE, /** Doze is done. DozeService is finished. */ @@ -84,6 +86,7 @@ public class DozeMachine { switch (this) { case DOZE_REQUEST_PULSE: case DOZE_PULSING: + case DOZE_PULSING_BRIGHT: return true; default: return false; @@ -101,6 +104,7 @@ public class DozeMachine { case DOZE: return Display.STATE_OFF; case DOZE_PULSING: + case DOZE_PULSING_BRIGHT: return Display.STATE_ON; case DOZE_AOD: case DOZE_AOD_PAUSING: @@ -188,7 +192,10 @@ public class DozeMachine { @MainThread public State getState() { Assert.isMainThread(); - Preconditions.checkState(!isExecutingTransition()); + if (isExecutingTransition()) { + throw new IllegalStateException("Cannot get state because there were pending " + + "transitions: " + mQueuedRequests.toString()); + } return mState; } @@ -202,6 +209,7 @@ public class DozeMachine { Assert.isMainThread(); Preconditions.checkState(mState == State.DOZE_REQUEST_PULSE || mState == State.DOZE_PULSING + || mState == State.DOZE_PULSING_BRIGHT || mState == State.DOZE_PULSE_DONE, "must be in pulsing state, but is " + mState); return mPulseReason; } @@ -283,7 +291,8 @@ public class DozeMachine { break; case DOZE_PULSE_DONE: Preconditions.checkState( - mState == State.DOZE_REQUEST_PULSE || mState == State.DOZE_PULSING); + mState == State.DOZE_REQUEST_PULSE || mState == State.DOZE_PULSING + || mState == State.DOZE_PULSING_BRIGHT); break; default: break; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java index 1dd3101075b014f152c781843984750d850c6103..bd6882c01bbd0651d411014bceb3bc75e4da109d 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java @@ -56,6 +56,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi private boolean mRegistered; private int mDefaultDozeBrightness; private boolean mPaused = false; + private boolean mScreenOff = false; private int mLastSensorValue = -1; /** @@ -118,6 +119,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi break; } if (newState != DozeMachine.State.FINISH) { + setScreenOff(newState == DozeMachine.State.DOZE); setPaused(newState == DozeMachine.State.DOZE_AOD_PAUSED); } } @@ -135,15 +137,15 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi try { if (mRegistered) { mLastSensorValue = (int) event.values[0]; - updateBrightnessAndReady(); + updateBrightnessAndReady(false /* force */); } } finally { Trace.endSection(); } } - private void updateBrightnessAndReady() { - if (mRegistered || mDebugBrightnessBucket != -1) { + private void updateBrightnessAndReady(boolean force) { + if (force || mRegistered || mDebugBrightnessBucket != -1) { int sensorValue = mDebugBrightnessBucket == -1 ? mLastSensorValue : mDebugBrightnessBucket; int brightness = computeBrightness(sensorValue); @@ -153,7 +155,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi } int scrimOpacity = -1; - if (mPaused) { + if (mPaused || mScreenOff) { // If AOD is paused, force the screen black until the // sensor reports a new brightness. This ensures that when the screen comes on // again, it will only show after the brightness sensor has stabilized, @@ -216,13 +218,20 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi private void setPaused(boolean paused) { if (mPaused != paused) { mPaused = paused; - updateBrightnessAndReady(); + updateBrightnessAndReady(false /* force */); + } + } + + private void setScreenOff(boolean screenOff) { + if (mScreenOff != screenOff) { + mScreenOff = screenOff; + updateBrightnessAndReady(true /* force */); } } @Override public void onReceive(Context context, Intent intent) { mDebugBrightnessBucket = intent.getIntExtra(BRIGHTNESS_BUCKET, -1); - updateBrightnessAndReady(); + updateBrightnessAndReady(false /* force */); } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 7189a48fed1f88f40c4e218ee4ee3b5f0626daf1..0fe6611b4274d9c7974de3bd826aca79e944d947 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -16,6 +16,7 @@ package com.android.systemui.doze; +import android.annotation.Nullable; import android.app.AlarmManager; import android.app.UiModeManager; import android.content.BroadcastReceiver; @@ -147,7 +148,7 @@ public class DozeTriggers implements DozeMachine.Part { boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0; if (isWakeDisplay) { - onWakeScreen(wakeEvent, mMachine.getState()); + onWakeScreen(wakeEvent, mMachine.isExecutingTransition() ? null : mMachine.getState()); } else if (isLongPress) { requestPulse(pulseReason, sensorPerformedProxCheck); } else if (isWakeLockScreen) { @@ -168,7 +169,7 @@ public class DozeTriggers implements DozeMachine.Part { } else if (isPickup) { gentleWakeUp(pulseReason); } else { - mDozeHost.extendPulse(); + mDozeHost.extendPulse(pulseReason); } }, sensorPerformedProxCheck || (mDockManager != null && mDockManager.isDocked()), pulseReason); @@ -212,7 +213,8 @@ public class DozeTriggers implements DozeMachine.Part { final boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING); final boolean aod = (state == DozeMachine.State.DOZE_AOD); - if (state == DozeMachine.State.DOZE_PULSING) { + if (state == DozeMachine.State.DOZE_PULSING + || state == DozeMachine.State.DOZE_PULSING_BRIGHT) { boolean ignoreTouch = near; if (DEBUG) Log.i(TAG, "Prox changed, ignore touch = " + ignoreTouch); mDozeHost.onIgnoreTouchWhilePulsing(ignoreTouch); @@ -227,10 +229,14 @@ public class DozeTriggers implements DozeMachine.Part { } } - private void onWakeScreen(boolean wake, DozeMachine.State state) { + /** + * When a wake screen event is received from a sensor + * @param wake {@code true} when it's time to wake up, {@code false} when we should sleep. + * @param state The current state, or null if the state could not be determined due to enqueued + * transitions. + */ + private void onWakeScreen(boolean wake, @Nullable DozeMachine.State state) { DozeLog.traceWakeDisplay(wake); - boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED); - boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING); sWakeDisplaySensorState = wake; if (wake) { @@ -244,6 +250,8 @@ public class DozeTriggers implements DozeMachine.Part { } }, false /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP); } else { + boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED); + boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING); if (!pausing && !paused) { mMachine.requestState(DozeMachine.State.DOZE); } @@ -276,6 +284,7 @@ public class DozeTriggers implements DozeMachine.Part { mDozeSensors.setListening(false); break; case DOZE_PULSING: + case DOZE_PULSING_BRIGHT: mDozeSensors.setTouchscreenSensorsListening(false); mDozeSensors.setProxListening(true); break; @@ -306,7 +315,16 @@ public class DozeTriggers implements DozeMachine.Part { private void requestPulse(final int reason, boolean performedProxCheck) { Assert.isMainThread(); - mDozeHost.extendPulse(); + mDozeHost.extendPulse(reason); + + // When already pulsing we're allowed to show the wallpaper directly without + // requesting a new pulse. + if (mMachine.getState() == DozeMachine.State.DOZE_PULSING + && reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) { + mMachine.requestState(DozeMachine.State.DOZE_PULSING_BRIGHT); + return; + } + if (mPulsePending || !mAllowPulseTriggers || !canPulse()) { if (mAllowPulseTriggers) { DozeLog.tracePulseDropped(mContext, mPulsePending, mMachine.getState(), diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index 847182d3ad35eff3b68d2189ea27df445c90954d..51e96d2eecadde33efcc1525aec51704ed7d5fe6 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -94,7 +94,10 @@ public class DozeUi implements DozeMachine.Part { @Override public void onPulseStarted() { try { - mMachine.requestState(DozeMachine.State.DOZE_PULSING); + mMachine.requestState( + reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN + ? DozeMachine.State.DOZE_PULSING_BRIGHT + : DozeMachine.State.DOZE_PULSING); } catch (IllegalStateException e) { // It's possible that the pulse was asynchronously cancelled while // we were waiting for it to start (under stress conditions.) @@ -148,6 +151,7 @@ public class DozeUi implements DozeMachine.Part { switch (state) { case DOZE_REQUEST_PULSE: case DOZE_PULSING: + case DOZE_PULSING_BRIGHT: case DOZE_PULSE_DONE: mHost.setAnimateWakeup(true); break; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java index ca9f24fd236ff4632cf262a21da915b7b3009aa0..1b3cd881b949507351513f15430cfebdd8967eec 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java @@ -38,19 +38,17 @@ public class DozeWallpaperState implements DozeMachine.Part { private final IWallpaperManager mWallpaperManagerService; private final DozeParameters mDozeParameters; - private final DozeMachine mMachine; private boolean mIsAmbientMode; - public DozeWallpaperState(Context context, DozeMachine machine) { - this(machine, IWallpaperManager.Stub.asInterface( + public DozeWallpaperState(Context context) { + this(IWallpaperManager.Stub.asInterface( ServiceManager.getService(Context.WALLPAPER_SERVICE)), DozeParameters.getInstance(context)); } @VisibleForTesting - DozeWallpaperState(DozeMachine machine, IWallpaperManager wallpaperManagerService, + DozeWallpaperState(IWallpaperManager wallpaperManagerService, DozeParameters parameters) { - mMachine = machine; mWallpaperManagerService = wallpaperManagerService; mDozeParameters = parameters; } @@ -65,16 +63,13 @@ public class DozeWallpaperState implements DozeMachine.Part { case DOZE_AOD_PAUSED: case DOZE_REQUEST_PULSE: case DOZE_PULSE_DONE: - isAmbientMode = true; - break; case DOZE_PULSING: - isAmbientMode = - mMachine.getPulseReason() != DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN; + isAmbientMode = true; break; + case DOZE_PULSING_BRIGHT: default: isAmbientMode = false; } - final boolean animated; if (isAmbientMode) { animated = mDozeParameters.shouldControlScreenOff(); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index b03b872b877f1b0408602e03b4a6616843f9929b..6f50baa53e38e9d5e8b45a7c79556d70003ded23 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -286,8 +286,9 @@ public class KeyguardSliceProvider extends SliceProvider implements RowBuilder dndBuilder = new RowBuilder(mDndUri) .setContentDescription(getContext().getResources() .getString(R.string.accessibility_quick_settings_dnd)) - .addEndItem(IconCompat.createWithResource(getContext(), R.drawable.stat_sys_dnd), - ListBuilder.ICON_IMAGE); + .addEndItem( + IconCompat.createWithResource(getContext(), R.drawable.stat_sys_dnd), + ListBuilder.ICON_IMAGE); builder.addRow(dndBuilder); } diff --git a/packages/SystemUI/src/com/android/systemui/power/BatteryStateSnapshot.kt b/packages/SystemUI/src/com/android/systemui/power/BatteryStateSnapshot.kt index d7a2d9acf3b5ba7f8b76e103f5579e07c2cb8bc4..02ad0f1766bde9ff49ae3824077086df25e65ed4 100644 --- a/packages/SystemUI/src/com/android/systemui/power/BatteryStateSnapshot.kt +++ b/packages/SystemUI/src/com/android/systemui/power/BatteryStateSnapshot.kt @@ -17,7 +17,8 @@ data class BatteryStateSnapshot( val timeRemainingMillis: Long, val severeThresholdMillis: Long, val lowThresholdMillis: Long, - val isBasedOnUsage: Boolean + val isBasedOnUsage: Boolean, + val isLowWarningEnabled: Boolean ) { /** * Returns whether hybrid warning logic/copy should be used for this snapshot @@ -48,7 +49,8 @@ data class BatteryStateSnapshot( NO_ESTIMATE_AVAILABLE.toLong(), NO_ESTIMATE_AVAILABLE.toLong(), NO_ESTIMATE_AVAILABLE.toLong(), - false + false, + true ) { this.isHybrid = false } diff --git a/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimates.java b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimates.java index bd130f4b40f3b203471f5e22662fc57566444d86..a87922792616017edc478fdf7d4a49995d308276 100644 --- a/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimates.java +++ b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimates.java @@ -23,4 +23,9 @@ public interface EnhancedEstimates { * show a severe warning to the user. */ long getSevereWarningThreshold(); + + /** + * Returns a boolean indicating if the low warning should be shown at all or not. + */ + boolean getLowWarningEnabled(); } diff --git a/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java index 3f2417638f1a3263df5af6c0db47374a709a81f4..bfb809ecbf34fd06d5330177fed62738d8d8b979 100644 --- a/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/power/EnhancedEstimatesImpl.java @@ -21,4 +21,9 @@ public class EnhancedEstimatesImpl implements EnhancedEstimates { public long getSevereWarningThreshold() { return 0; } + + @Override + public boolean getLowWarningEnabled() { + return true; + } } diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java index 41bcab53f8e915058a9ac16e4098d45291065345..10f727bc7189ca78c6c5dfae8e19b9f20245f2e9 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java @@ -31,6 +31,7 @@ import android.os.Handler; import android.os.Looper; import android.os.PowerManager; import android.os.UserHandle; +import android.provider.Settings.Secure; import android.text.Annotation; import android.text.Layout; import android.text.SpannableString; @@ -70,6 +71,7 @@ import javax.inject.Singleton; */ @Singleton public class PowerNotificationWarnings implements PowerUI.WarningsUI { + private static final String TAG = PowerUI.TAG + ".Notification"; private static final boolean DEBUG = PowerUI.DEBUG; @@ -119,6 +121,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) .build(); + public static final String EXTRA_CONFIRM_ONLY = "extra_confirm_only"; private final Context mContext; private final NotificationManager mNoMan; @@ -544,10 +547,9 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { updateNotification(); } - private void showStartSaverConfirmation() { + private void showStartSaverConfirmation(boolean confirmOnly) { if (mSaverConfirmation != null) return; final SystemUIDialog d = new SystemUIDialog(mContext); - d.setTitle(R.string.battery_saver_confirmation_title); d.setMessage(getBatterySaverDescription()); // Sad hack for http://b/78261259 and http://b/78298335. Otherwise "Battery" may be split @@ -558,9 +560,19 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { // We need to set LinkMovementMethod to make the link clickable. d.setMessageMovementMethod(LinkMovementMethod.getInstance()); - d.setNegativeButton(android.R.string.cancel, null); - d.setPositiveButton(R.string.battery_saver_confirmation_ok, + if (confirmOnly) { + d.setTitle(R.string.battery_saver_confirmation_title_generic); + d.setPositiveButton(com.android.internal.R.string.confirm_battery_saver, + (dialog, which) -> Secure.putInt( + mContext.getContentResolver(), + Secure.LOW_POWER_WARNING_ACKNOWLEDGED, + 1)); + } else { + d.setTitle(R.string.battery_saver_confirmation_title); + d.setPositiveButton(R.string.battery_saver_confirmation_ok, (dialog, which) -> setSaverMode(true, false)); + d.setNegativeButton(android.R.string.cancel, null); + } d.setShowForAllUsers(true); d.setOnDismissListener((dialog) -> mSaverConfirmation = null); d.show(); @@ -719,7 +731,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { dismissLowBatteryNotification(); } else if (action.equals(ACTION_SHOW_START_SAVER_CONFIRMATION)) { dismissLowBatteryNotification(); - showStartSaverConfirmation(); + showStartSaverConfirmation(intent.getBooleanExtra(EXTRA_CONFIRM_ONLY, false)); } else if (action.equals(ACTION_DISMISSED_WARNING)) { dismissLowBatteryWarning(); } else if (ACTION_CLICKED_TEMP_WARNING.equals(action)) { diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java index 18638606a2518291aaab782c19fd449f362e0797..4e41108f649648c0fd2f9c1df09ef27f5f0d98bb 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -284,7 +284,8 @@ public class PowerUI extends SystemUI { plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1], mLowBatteryReminderLevels[0], estimate.getEstimateMillis(), mEnhancedEstimates.getSevereWarningThreshold(), - mEnhancedEstimates.getLowWarningThreshold(), estimate.isBasedOnUsage()); + mEnhancedEstimates.getLowWarningThreshold(), estimate.isBasedOnUsage(), + mEnhancedEstimates.getLowWarningEnabled()); } else { if (DEBUG) { Slog.d(TAG, "using standard"); @@ -351,7 +352,6 @@ public class PowerUI extends SystemUI { Slog.d(TAG, "Low warning marked as shown this cycle"); mLowWarningShownThisChargeCycle = true; } - } else if (shouldDismissHybridWarning(currentSnapshot)) { if (DEBUG) { Slog.d(TAG, "Dismissing warning"); @@ -375,8 +375,9 @@ public class PowerUI extends SystemUI { return false; } - // Only show the low warning once per charge cycle & no battery saver - final boolean canShowWarning = !mLowWarningShownThisChargeCycle && !snapshot.isPowerSaver() + // Only show the low warning if enabled once per charge cycle & no battery saver + final boolean canShowWarning = snapshot.isLowWarningEnabled() + && !mLowWarningShownThisChargeCycle && !snapshot.isPowerSaver() && (snapshot.getTimeRemainingMillis() < snapshot.getLowThresholdMillis() || snapshot.getBatteryLevel() <= snapshot.getLowLevelThreshold()); @@ -386,6 +387,7 @@ public class PowerUI extends SystemUI { || snapshot.getBatteryLevel() <= snapshot.getSevereLevelThreshold()); final boolean canShow = canShowWarning || canShowSevereWarning; + if (DEBUG) { Slog.d(TAG, "Enhanced trigger is: " + canShow + "\nwith battery snapshot:" + " mLowWarningShownThisChargeCycle: " + mLowWarningShownThisChargeCycle diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt index 59b3c3464a69d458a3c3af4417a06f8ff81c0b2b..d08a3733703b03b942f1dc90cc8dd451e086992e 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt @@ -53,9 +53,4 @@ class PrivacyDialogBuilder(private val context: Context, itemsList: List types.map { it.getName(context) }.joinWithAnd().toString() } } - - fun getDialogTitle(): String { - return context.getString(R.string.ongoing_privacy_dialog_multiple_apps_title, - joinTypes()) - } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index 76dfddb78c80fc1b3b88fe1427be00341e5ca257..bb159a9ba2e8fbafe2a691f2951c0e9aa41d5489 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -62,6 +62,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { private int mLayoutOrientation; private int mLayoutDirection; private int mHorizontalClipBound; + private final Rect mClippingRect; public PagedTileLayout(Context context, AttributeSet attrs) { super(context, attrs); @@ -71,6 +72,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { setCurrentItem(0, false); mLayoutOrientation = getResources().getConfiguration().orientation; mLayoutDirection = getLayoutDirection(); + mClippingRect = new Rect(); } public void saveInstanceState(Bundle outState) { @@ -280,8 +282,8 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); - Rect clipBounds = new Rect(mHorizontalClipBound, 0, (r-l) - mHorizontalClipBound, b - t); - setClipBounds(clipBounds); + mClippingRect.set(mHorizontalClipBound, 0, (r - l) - mHorizontalClipBound, b - t); + setClipBounds(mClippingRect); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index b93f8d04deccff6d9291d9c1911d208bbc6afbef..d6e03060c7f7e706985e3adb1562be1c9f20b707 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -118,7 +118,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne updateResources(); mBrightnessController = new BrightnessController(getContext(), - findViewById(R.id.brightness_icon), findViewById(R.id.brightness_slider)); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java index 7c937a944113826cf25706d8a7ce440944318183..b682cb09b598ff673bc3cc85f947d6092d6aade2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java @@ -160,9 +160,9 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic int footerIconId = R.drawable.ic_info_outline; if (vpnName != null || vpnNameWorkProfile != null) { if (mSecurityController.isVpnBranded()) { - footerIconId = R.drawable.ic_qs_branded_vpn; + footerIconId = R.drawable.stat_sys_branded_vpn; } else { - footerIconId = R.drawable.ic_qs_vpn; + footerIconId = R.drawable.stat_sys_vpn_ic; } } if (mFooterIconId != footerIconId) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index 608f646e77a4665862dd7c934b8410304e847664..8ed5424cf67360d340226684c3420f3031f6ff5b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -113,6 +113,7 @@ public class TileAdapter extends RecyclerView.Adapter implements TileSta public void saveSpecs(QSTileHost host) { List newSpecs = new ArrayList<>(); + clearAccessibilityState(); for (int i = 1; i < mTiles.size() && mTiles.get(i) != null; i++) { newSpecs.add(mTiles.get(i).spec); } @@ -120,6 +121,17 @@ public class TileAdapter extends RecyclerView.Adapter implements TileSta mCurrentSpecs = newSpecs; } + private void clearAccessibilityState() { + if (mAccessibilityAction == ACTION_ADD) { + // Remove blank tile from last spot + mTiles.remove(--mEditIndex); + // Update the tile divider position + mTileDividerIndex--; + notifyDataSetChanged(); + } + mAccessibilityAction = ACTION_NONE; + } + public void resetTileSpecs(QSTileHost host, List specs) { // Notify the host so the tiles get removed callbacks. host.changeTiles(mCurrentSpecs, specs); @@ -333,8 +345,6 @@ public class TileAdapter extends RecyclerView.Adapter implements TileSta // Remove the placeholder. mTiles.remove(mEditIndex--); notifyItemRemoved(mEditIndex); - // Don't remove items when the last position is selected. - if (position == mEditIndex - 1) position--; } mAccessibilityAction = ACTION_NONE; move(mAccessibilityFromIndex, position, v); @@ -372,6 +382,8 @@ public class TileAdapter extends RecyclerView.Adapter implements TileSta mAccessibilityAction = ACTION_ADD; // Add placeholder for last slot. mTiles.add(mEditIndex++, null); + // Update the tile divider position + mTileDividerIndex++; mNeedsFocus = true; notifyDataSetChanged(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java index 387de716844cb29addd49781cbf9840163531c21..19e20a93ce66d0eed15b6ec8be70d6d702b3a9cd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java @@ -43,8 +43,7 @@ import javax.inject.Inject; /** Quick settings tile: Airplane mode **/ public class AirplaneModeTile extends QSTileImpl { - private final Icon mIcon = - ResourceIcon.get(R.drawable.ic_signal_airplane); + private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_airplane); private final GlobalSetting mSetting; private final ActivityStarter mActivityStarter; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index 5b85498574a7db44c36cdff0224bfa1d8ab0adf8..ca040762047c6408a1cfa5dcc56e258f6f2dbd89 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -147,14 +147,15 @@ public class BluetoothTile extends QSTileImpl { state.icon = ResourceIcon.get(R.drawable.ic_bluetooth_transient_animation); state.contentDescription = state.secondaryLabel; } else { - state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_on); + state.icon = + ResourceIcon.get(com.android.internal.R.drawable.ic_qs_bluetooth); state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_bluetooth) + "," + mContext.getString(R.string.accessibility_not_connected); } state.state = Tile.STATE_ACTIVE; } else { - state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_on); + state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_bluetooth); state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_bluetooth); state.state = Tile.STATE_INACTIVE; @@ -288,7 +289,7 @@ public class BluetoothTile extends QSTileImpl { // This method returns Pair while first value is the drawable return BluetoothDeviceLayerDrawable.createLayerDrawable( context, - R.drawable.ic_qs_bluetooth_connected, + R.drawable.ic_bluetooth_connected, mBatteryLevel, mIconScale); } @@ -309,7 +310,7 @@ public class BluetoothTile extends QSTileImpl { @Override public Drawable getDrawable(Context context) { // This method returns Pair - the first value is the drawable. - return context.getDrawable(R.drawable.ic_qs_bluetooth_connected); + return context.getDrawable(R.drawable.ic_bluetooth_connected); } } @@ -383,12 +384,12 @@ public class BluetoothTile extends QSTileImpl { for (CachedBluetoothDevice device : devices) { if (mController.getBondState(device) == BluetoothDevice.BOND_NONE) continue; final Item item = new Item(); - item.iconResId = R.drawable.ic_qs_bluetooth_on; + item.iconResId = com.android.internal.R.drawable.ic_qs_bluetooth; item.line1 = device.getName(); item.tag = device; int state = device.getMaxConnectionState(); if (state == BluetoothProfile.STATE_CONNECTED) { - item.iconResId = R.drawable.ic_qs_bluetooth_connected; + item.iconResId = R.drawable.ic_bluetooth_connected; int batteryLevel = device.getBatteryLevel(); if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { item.icon = new BluetoothBatteryTileIcon(batteryLevel,1 /* iconScale */); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index bdebf79d823bd4414d27cdffe7e8cdf2c7d649be..415870c590a35853a6823c132de24ec6458ecb59 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -202,8 +202,8 @@ public class CastTile extends QSTileImpl { if (connecting && !state.value) { state.secondaryLabel = mContext.getString(R.string.quick_settings_connecting); } - state.icon = ResourceIcon.get(state.value ? R.drawable.ic_qs_cast_on - : R.drawable.ic_qs_cast_off); + state.icon = ResourceIcon.get(state.value ? R.drawable.ic_cast_connected + : R.drawable.ic_cast); if (mWifiConnected || state.value) { state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; if (!state.value) { @@ -334,7 +334,7 @@ public class CastTile extends QSTileImpl { for (CastDevice device : devices) { if (device.state == CastDevice.STATE_CONNECTED) { final Item item = new Item(); - item.iconResId = R.drawable.ic_qs_cast_on; + item.iconResId = R.drawable.ic_cast_connected; item.line1 = getDeviceName(device); item.line2 = mContext.getString(R.string.quick_settings_connected); item.tag = device; @@ -354,7 +354,7 @@ public class CastTile extends QSTileImpl { final CastDevice device = mVisibleOrder.get(id); if (!devices.contains(device)) continue; final Item item = new Item(); - item.iconResId = R.drawable.ic_qs_cast_off; + item.iconResId = R.drawable.ic_cast; item.line1 = getDeviceName(device); if (device.state == CastDevice.STATE_CONNECTING) { item.line2 = mContext.getString(R.string.quick_settings_connecting); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index db79e4d72642bb451a83735d24a5ec60beacc073..38962eb1470588fff8fadc432b1dce07b3b14e8a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -45,7 +45,6 @@ import com.android.systemui.qs.CellTileView; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.phone.SystemUIDialog; -import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NetworkController.IconState; import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; @@ -62,15 +61,13 @@ public class CellularTile extends QSTileImpl { private final CellSignalCallback mSignalCallback = new CellSignalCallback(); private final ActivityStarter mActivityStarter; - private final KeyguardMonitor mKeyguardMonitor; @Inject public CellularTile(QSHost host, NetworkController networkController, - ActivityStarter activityStarter, KeyguardMonitor keyguardMonitor) { + ActivityStarter activityStarter) { super(host); mController = networkController; mActivityStarter = activityStarter; - mKeyguardMonitor = keyguardMonitor; mDataController = mController.getMobileDataController(); mDetailAdapter = new CellularDetailAdapter(); mController.observe(getLifecycle(), mSignalCallback); @@ -106,11 +103,7 @@ public class CellularTile extends QSTileImpl { return; } if (mDataController.isMobileDataEnabled()) { - if (mKeyguardMonitor.isSecure() && !mKeyguardMonitor.canSkipBouncer()) { - mActivityStarter.postQSRunnableDismissingKeyguard(this::maybeShowDisableDialog); - } else { - mUiHandler.post(this::maybeShowDisableDialog); - } + maybeShowDisableDialog(); } else { mDataController.setMobileDataEnabled(true); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index 7fcd59f7c9310075ef6b3638028e8520f0282743..869fa6b1824511896820efbd585ab02c8e3c796e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -235,7 +235,7 @@ public class DndTile extends QSTileImpl { state.label = getTileLabel(); state.secondaryLabel = TextUtils.emptyIfNull(ZenModeConfig.getDescription(mContext, zen != Global.ZEN_MODE_OFF, mController.getConfig(), false)); - state.icon = ResourceIcon.get(R.drawable.ic_dnd); + state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_dnd); checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME); switch (zen) { case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java index dfa3fb9dafc968ccd04b8c3d58a8d8980dc6540d..2755e9880b58e16210ec42e3fc12eb7fa7ac7c86 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java @@ -35,7 +35,7 @@ import javax.inject.Inject; public class FlashlightTile extends QSTileImpl implements FlashlightController.FlashlightListener { - private final Icon mIcon = ResourceIcon.get(R.drawable.ic_signal_flashlight); + private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_flashlight); private final FlashlightController mFlashlightController; @Inject diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java index a0f4e24d2f93a6aa4d5d65458ebc7955c1ebdd72..837ea9fc5f4e6c944d7cde66cb39c416b67a019c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -37,7 +37,7 @@ import javax.inject.Inject; /** Quick settings tile: Location **/ public class LocationTile extends QSTileImpl { - private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_signal_location); + private final Icon mIcon = ResourceIcon.get(R.drawable.ic_location); private final LocationController mController; private final KeyguardMonitor mKeyguard; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java index 21f3d6e77f7bb29beb191f837297a7ad25e6f4c1..7ca1e44c93cd7d75825f0c601815795b359e58d4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java @@ -36,7 +36,7 @@ import javax.inject.Inject; /** Quick settings tile: Rotation **/ public class RotationLockTile extends QSTileImpl { - private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_auto_rotate); + private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_auto_rotate); private final RotationLockController mController; @Inject diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java index f921eb9a49a658f20256c0002c95cfa8a8e54603..7853dc388bcbb7348db0118f0503c40ffb7e028a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java @@ -33,7 +33,7 @@ import javax.inject.Inject; /** Quick settings tile: Work profile on/off */ public class WorkModeTile extends QSTileImpl implements ManagedProfileController.Callback { - private final Icon mIcon = ResourceIcon.get(R.drawable.ic_signal_workmode_disable); + private final Icon mIcon = ResourceIcon.get(R.drawable.stat_sys_managed_profile_status); private final ManagedProfileController mProfileController; diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 494e6cde5a1c3910ad795b91709ca5fc18cf5bfa..ead39c69730eab09922e7b3516c0bf0c473795f4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -439,6 +439,9 @@ public class OverviewProxyService implements CallbackController row.setJustClicked(false)); - // If it was a bubble we should close it - if (row.getEntry().isBubble()) { + if (!row.getEntry().isBubble()) { mBubbleController.collapseStack(); } @@ -95,7 +94,8 @@ public final class NotificationClicker implements View.OnClickListener { */ public void register(ExpandableNotificationRow row, StatusBarNotification sbn) { Notification notification = sbn.getNotification(); - if (notification.contentIntent != null || notification.fullScreenIntent != null) { + if (notification.contentIntent != null || notification.fullScreenIntent != null + || row.getEntry().isBubble()) { row.setOnClickListener(this); } else { row.setOnClickListener(null); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java index b91cdaf9ae80c74f3337200a5165e37d9adf4711..d3e5af8c729eaf6537ec3046975a9d0e5343feb1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java @@ -220,8 +220,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { StatusBarNotification sbn, ExpandableNotificationRow row) { row.setIsLowPriority(entry.ambient); - // bind the click event to the content area - checkNotNull(mNotificationClicker).register(row, sbn); // Extract target SDK version. try { @@ -257,6 +255,9 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { row.setNeedsRedaction( Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry)); row.inflateViews(); + + // bind the click event to the content area + checkNotNull(mNotificationClicker).register(row, sbn); } private void logNotificationExpansion(String key, boolean userAction, boolean expanded) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 9630727c8f3dc63760d870a81ffad1521e68618b..d287b92876b52edea0f56c61fef1dae9e7fae53c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -552,6 +552,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mEntry.mIsSystemNotification = isSystemNotification(mContext, mStatusBarNotification); } + isNonblockable |= mEntry.channel.isImportanceLockedByOEM(); + isNonblockable |= mEntry.channel.isImportanceLockedByCriticalDeviceFunction(); + if (!isNonblockable && mEntry != null && mEntry.mIsSystemNotification != null) { if (mEntry.mIsSystemNotification) { if (mEntry.channel != null diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 520df9797cc37cd08cc8fa6d432656f9b56c0661..ce206816bf1dab9c4b32c9136f2ebc2cd8e48572 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -399,7 +399,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private final ViewOutlineProvider mOutlineProvider = new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { - if (mAmbientState.isDarkAtAll() && !mAmbientState.isFullyDark()) { + if (mAmbientState.isDarkAtAll() && !mAmbientState.isFullyDark() || !mShowDarkShelf) { outline.setRoundRect(mBackgroundAnimationRect, mCornerRadius); } else { ViewOutlineProvider.BACKGROUND.getOutline(view, outline); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java index 79056175b595054652abb08e9202f590dbf27ec5..211a40a91101b8973d961b07add197d29d0ac96a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java @@ -70,7 +70,7 @@ public class BarTransitions { private final String mTag; private final View mView; - private final BarBackgroundDrawable mBarBackground; + protected final BarBackgroundDrawable mBarBackground; private int mMode; private boolean mAlwaysOpaque = false; @@ -152,7 +152,7 @@ public class BarTransitions { return mode == MODE_LIGHTS_OUT || mode == MODE_LIGHTS_OUT_TRANSPARENT; } - private static class BarBackgroundDrawable extends Drawable { + protected static class BarBackgroundDrawable extends Drawable { private final int mOpaque; private final int mSemiTransparent; private final int mTransparent; @@ -171,6 +171,7 @@ public class BarTransitions { private int mGradientAlphaStart; private int mColorStart; + private Rect mFrame; public BarBackgroundDrawable(Context context, int gradientResourceId) { @@ -190,6 +191,10 @@ public class BarTransitions { mGradient = context.getDrawable(gradientResourceId); } + public void setFrame(Rect frame) { + mFrame = frame; + } + @Override public void setAlpha(int alpha) { // noop @@ -296,7 +301,11 @@ public class BarTransitions { if (mTintFilter != null) { mPaint.setColorFilter(mTintFilter); } - canvas.drawPaint(mPaint); + if (mFrame != null) { + canvas.drawRect(mFrame, mPaint); + } else { + canvas.drawPaint(mPaint); + } } if (mAnimating) { invalidateSelf(); // keep going diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java index 08a10dc925e308e386ae4f5431590eedb1159e4b..ac58e681dbbc280ccd336b5d346777303157e763 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java @@ -36,7 +36,8 @@ import javax.inject.Singleton; /** */ @Singleton -public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher { +public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher, + LightBarTransitionsController.DarkIntensityApplier { private final LightBarTransitionsController mTransitionsController; private final Rect mTintArea = new Rect(); @@ -54,8 +55,7 @@ public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher { mDarkModeIconColorSingleTone = context.getColor(R.color.dark_mode_icon_color_single_tone); mLightModeIconColorSingleTone = context.getColor(R.color.light_mode_icon_color_single_tone); - mTransitionsController = new LightBarTransitionsController(context, - this::setIconTintInternal); + mTransitionsController = new LightBarTransitionsController(context, this); } public LightBarTransitionsController getTransitionsController() { @@ -104,13 +104,19 @@ public class DarkIconDispatcherImpl implements SysuiDarkIconDispatcher { applyIconTint(); } - private void setIconTintInternal(float darkIntensity) { + @Override + public void applyDarkIntensity(float darkIntensity) { mDarkIntensity = darkIntensity; mIconTint = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, mLightModeIconColorSingleTone, mDarkModeIconColorSingleTone); applyIconTint(); } + @Override + public int getTintAnimationDuration() { + return LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION; + } + private void applyIconTint() { for (int i = 0; i < mReceivers.size(); i++) { mReceivers.valueAt(i).onDarkChanged(mTintArea, mDarkIntensity, mIconTint); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java index 236c72c5cc2c11255cdcc48efb3643e51908780d..0731a568ae7df2eb6f36ee20aa9fabfb92ea14c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java @@ -108,16 +108,13 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da } String zen = args.getString("zen"); if (zen != null) { - int iconId = zen.equals("important") ? R.drawable.stat_sys_zen_important - : zen.equals("none") ? R.drawable.stat_sys_zen_none - : 0; + int iconId = zen.equals("dnd") ? R.drawable.stat_sys_dnd : 0; updateSlot("zen", null, iconId); } String bt = args.getString("bluetooth"); if (bt != null) { - int iconId = bt.equals("disconnected") ? R.drawable.stat_sys_data_bluetooth - : bt.equals("connected") ? R.drawable.stat_sys_data_bluetooth_connected - : 0; + int iconId = bt.equals("connected") + ? R.drawable.stat_sys_data_bluetooth_connected : 0; updateSlot("bluetooth", null, iconId); } String location = args.getString("location"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 9844d8e5a67ad0476cf976c2615161d5c8e8ca05..b7a154d67c1086588d55baa9c12702de1800e67c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -140,7 +140,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, mHeadsUpInset = mStatusBarHeight + resources.getDimensionPixelSize( R.dimen.heads_up_status_bar_padding); mDisplayCutoutTouchableRegionSize = resources.getDimensionPixelSize( - R.dimen.display_cutout_touchable_region_size); + com.android.internal.R.dimen.display_cutout_touchable_region_size); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 800ae58010162bd258cbe86486bc59628ea7fda2..a924680b7043a23c4ff6c1587981c7eee6ca961b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.phone; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; +import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTTON; import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK; import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON; @@ -122,6 +123,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private KeyguardAffordanceView mRightAffordanceView; private KeyguardAffordanceView mLeftAffordanceView; private LockIcon mLockIcon; + private ViewGroup mLockIconContainer; private ViewGroup mIndicationArea; private TextView mEnterpriseDisclosure; private TextView mIndicationText; @@ -171,6 +173,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private boolean mDozing; private int mIndicationBottomMargin; private float mDarkAmount; + private int mBurnInXOffset; + private int mBurnInYOffset; private ActivityIntentHelper mActivityIntentHelper; public KeyguardBottomAreaView(Context context) { @@ -244,12 +248,15 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mRightAffordanceView = findViewById(R.id.camera_button); mLeftAffordanceView = findViewById(R.id.left_button); mLockIcon = findViewById(R.id.lock_icon); + mLockIconContainer = findViewById(R.id.lock_icon_container); mIndicationArea = findViewById(R.id.keyguard_indication_area); mEnterpriseDisclosure = findViewById( R.id.keyguard_indication_enterprise_disclosure); mIndicationText = findViewById(R.id.keyguard_indication_text); mIndicationBottomMargin = getResources().getDimensionPixelSize( R.dimen.keyguard_indication_margin_bottom); + mBurnInYOffset = getResources().getDimensionPixelSize( + R.dimen.default_burn_in_prevention_offset); updateCameraVisibility(); mUnlockMethodCache = UnlockMethodCache.getInstance(getContext()); mUnlockMethodCache.addListener(this); @@ -319,6 +326,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL super.onConfigurationChanged(newConfig); mIndicationBottomMargin = getResources().getDimensionPixelSize( R.dimen.keyguard_indication_margin_bottom); + mBurnInYOffset = getResources().getDimensionPixelSize( + R.dimen.default_burn_in_prevention_offset); MarginLayoutParams mlp = (MarginLayoutParams) mIndicationArea.getLayoutParams(); if (mlp.bottomMargin != mIndicationBottomMargin) { mlp.bottomMargin = mIndicationBottomMargin; @@ -562,8 +571,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL return; } mDarkAmount = darkAmount; - mIndicationController.setDarkAmount(darkAmount); mLockIcon.setDarkAmount(darkAmount); + dozeTimeTick(); } /** @@ -657,6 +666,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL return mLockIcon; } + public ViewGroup getLockIconContainer() { + return mLockIconContainer; + } + public View getIndicationArea() { return mIndicationArea; } @@ -835,6 +848,20 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } } + public void dozeTimeTick() { + int burnInYOffset = getBurnInOffset(mBurnInYOffset * 2, false /* xAxis */) + - mBurnInYOffset; + mIndicationArea.setTranslationY(burnInYOffset * mDarkAmount); + } + + public void setAntiBurnInOffsetX(int burnInXOffset) { + if (mBurnInXOffset == burnInXOffset) { + return; + } + mBurnInXOffset = burnInXOffset; + mIndicationArea.setTranslationX(burnInXOffset); + } + /** * Sets the alpha of the indication areas and affordances, excluding the lock icon. */ 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 fe3a455302d4f6b69c1d2704b948bf2bf137f7fb..5afff814c111c6c63cbed16efdd56deb477b395f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -123,6 +123,7 @@ public class KeyguardBouncer { return; } ensureView(); + mIsScrimmed = isScrimmed; // On the keyguard, we want to show the bouncer when the user drags up, but it's // not correct to end the falsing session. We still need to verify if those touches @@ -132,13 +133,13 @@ public class KeyguardBouncer { if (isScrimmed) { setExpansion(EXPANSION_VISIBLE); } - mIsScrimmed = isScrimmed; if (resetSecuritySelection) { // showPrimarySecurityScreen() updates the current security method. This is needed in // case we are already showing and the current security method changed. showPrimarySecurityScreen(); } + if (mRoot.getVisibility() == View.VISIBLE || mShowingSoon) { return; } @@ -168,8 +169,8 @@ public class KeyguardBouncer { mCallback.onBouncerVisiblityChanged(true /* shown */); } - public boolean isShowingScrimmed() { - return isShowing() && mIsScrimmed; + public boolean isScrimmed() { + return mIsScrimmed; } public ViewGroup getLockIconContainer() { @@ -281,6 +282,7 @@ public class KeyguardBouncer { StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN); mDismissCallbackRegistry.notifyDismissCancelled(); } + mIsScrimmed = false; mFalsingManager.onBouncerHidden(); mCallback.onBouncerVisiblityChanged(false /* shown */); cancelShowRunnable(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 4c50d07aa6eac8cf2c94ad312d31bee3fe2c2ace..2bd8d41a24e30ba99d1229777da0931a6318ee0b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection; -import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; import android.annotation.ColorInt; import android.content.Context; @@ -102,19 +101,6 @@ public class KeyguardStatusBarView extends RelativeLayout */ private int mCutoutSideNudge = 0; - /** - * How much to move icons to avoid burn in. - */ - private int mBurnInOffset; - private int mCurrentBurnInOffsetX; - private int mCurrentBurnInOffsetY; - - /** - * Ratio representing being in ambient mode or not. - */ - private float mDarkAmount; - private boolean mDozing; - public KeyguardStatusBarView(Context context, AttributeSet attrs) { super(context, attrs); } @@ -186,8 +172,6 @@ public class KeyguardStatusBarView extends RelativeLayout R.dimen.system_icons_super_container_avatarless_margin_end); mCutoutSideNudge = getResources().getDimensionPixelSize( R.dimen.display_cutout_margin_consumption); - mBurnInOffset = getResources().getDimensionPixelSize( - R.dimen.default_burn_in_prevention_offset); mShowPercentAvailable = getContext().getResources().getBoolean( com.android.internal.R.bool.config_battery_percentage_setting_available); } @@ -211,7 +195,7 @@ public class KeyguardStatusBarView extends RelativeLayout mMultiUserSwitch.setVisibility(View.GONE); } } - mBatteryView.setForceShowPercent(mBatteryCharging && mShowPercentAvailable || mDozing); + mBatteryView.setForceShowPercent(mBatteryCharging && mShowPercentAvailable); } private void updateSystemIconsLayoutParams() { @@ -348,7 +332,6 @@ public class KeyguardStatusBarView extends RelativeLayout mIconManager = new TintedIconManager(findViewById(R.id.statusIcons)); Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager); onThemeChanged(); - updateDarkState(); } @Override @@ -492,7 +475,7 @@ public class KeyguardStatusBarView extends RelativeLayout mIconManager.setTint(iconColor); } - applyDarkness(R.id.battery, mEmptyRect, intensity * (1f - mDarkAmount), iconColor); + applyDarkness(R.id.battery, mEmptyRect, intensity, iconColor); applyDarkness(R.id.clock, mEmptyRect, intensity, iconColor); } @@ -513,48 +496,4 @@ public class KeyguardStatusBarView extends RelativeLayout mBatteryView.dump(fd, pw, args); } } - - public void setDozing(boolean dozing) { - if (mDozing == dozing) { - return; - } - mDozing = dozing; - setClipChildren(!dozing); - setClipToPadding(!dozing); - updateVisibilities(); - } - - public void setDarkAmount(float darkAmount) { - mDarkAmount = darkAmount; - if (darkAmount == 0) { - dozeTimeTick(); - } - updateDarkState(); - } - - public void dozeTimeTick() { - mCurrentBurnInOffsetX = getBurnInOffset(mBurnInOffset, true /* xAxis */); - mCurrentBurnInOffsetY = getBurnInOffset(mBurnInOffset, false /* xAxis */); - updateDarkState(); - } - - private void updateDarkState() { - float alpha = 1f - mDarkAmount; - int visibility = alpha != 0f ? VISIBLE : INVISIBLE; - mCarrierLabel.setAlpha(alpha * alpha); - mStatusIconContainer.setAlpha(alpha); - mStatusIconContainer.setVisibility(visibility); - - float iconsX = -mCurrentBurnInOffsetX; - if (mMultiUserSwitch.getVisibility() == VISIBLE) { - // Squared alpha to add a nice easing curve and avoid overlap during animation. - mMultiUserAvatar.setAlpha(alpha * alpha); - iconsX += mMultiUserAvatar.getPaddingLeft() + mMultiUserAvatar.getWidth() - + mMultiUserAvatar.getPaddingRight(); - } - mSystemIconsContainer.setTranslationX(iconsX * mDarkAmount); - mSystemIconsContainer.setTranslationY(mCurrentBurnInOffsetY * mDarkAmount); - updateIconsAndTextColors(); - } - } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java index b622688a8ac60b36bf98bf158ef560c2d5895e83..d7097309ce20b76bf683316dfd99f9abfa932d18 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java @@ -16,9 +16,6 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.statusbar.phone.NavBarTintController.DEFAULT_COLOR_ADAPT_TRANSITION_TIME; -import static com.android.systemui.statusbar.phone.NavBarTintController.MIN_COLOR_ADAPT_TRANSITION_TIME; - import android.animation.ValueAnimator; import android.content.Context; import android.os.Bundle; @@ -52,7 +49,6 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, private final DarkIntensityApplier mApplier; private final KeyguardMonitor mKeyguardMonitor; private final StatusBarStateController mStatusBarStateController; - private NavBarTintController mColorAdaptionController; private boolean mTransitionDeferring; private long mTransitionDeferringStartTime; @@ -118,7 +114,8 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, } if (mTransitionPending && mTintChangePending) { mTintChangePending = false; - animateIconTint(mPendingDarkIntensity, 0 /* delay */, getTintAnimationDuration()); + animateIconTint(mPendingDarkIntensity, 0 /* delay */, + mApplier.getTintAnimationDuration()); } mTransitionPending = false; } @@ -159,15 +156,8 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, Math.max(0, mTransitionDeferringStartTime - SystemClock.uptimeMillis()), mTransitionDeferringDuration); } else { - animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, getTintAnimationDuration()); - } - } - - public long getTintAnimationDuration() { - if (NavBarTintController.isEnabled(mContext)) { - return Math.max(DEFAULT_COLOR_ADAPT_TRANSITION_TIME, MIN_COLOR_ADAPT_TRANSITION_TIME); + animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, mApplier.getTintAnimationDuration()); } - return DEFAULT_TINT_ANIMATION_DURATION; } public float getCurrentDarkIntensity() { @@ -243,5 +233,6 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, */ public interface DarkIntensityApplier { void applyDarkIntensity(float darkIntensity); + int getTintAnimationDuration(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java index 9cfb1aa192b19fcb1fee1b9801650d055575cf20..bf5b60a9437bd43784916c08db45f18648c77d71 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java @@ -19,11 +19,14 @@ package com.android.systemui.statusbar.phone; import static android.view.Display.DEFAULT_DISPLAY; import android.content.Context; +import android.content.res.Resources; +import android.graphics.Point; import android.graphics.Rect; import android.os.Handler; import android.view.CompositionSamplingListener; import android.view.View; +import com.android.systemui.R; import com.android.systemui.shared.system.QuickStepContract; import java.io.PrintWriter; @@ -37,9 +40,6 @@ public class NavBarTintController implements View.OnAttachStateChangeListener, public static final int MIN_COLOR_ADAPT_TRANSITION_TIME = 400; public static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1700; - // Passing the threshold of this luminance value will make the button black otherwise white - private static final float LUMINANCE_THRESHOLD = 0.3f; - private final Handler mHandler = new Handler(); private final NavigationBarView mNavigationBarView; private final LightBarTransitionsController mLightBarController; @@ -50,9 +50,17 @@ public class NavBarTintController implements View.OnAttachStateChangeListener, private boolean mSamplingEnabled = false; private boolean mSamplingListenerRegistered = false; - private float mLastMediaLuma; + private float mLastMedianLuma; + private float mCurrentMedianLuma; private boolean mUpdateOnNextDraw; + private final int mNavBarHeight; + private final int mNavColorSampleMargin; + + // Passing the threshold of this luminance value will make the button black otherwise white + private final float mLuminanceThreshold; + private final float mLuminanceChangeThreshold; + public NavBarTintController(NavigationBarView navigationBarView, LightBarTransitionsController lightBarController) { mSamplingListener = new CompositionSamplingListener( @@ -66,6 +74,13 @@ public class NavBarTintController implements View.OnAttachStateChangeListener, mNavigationBarView.addOnAttachStateChangeListener(this); mNavigationBarView.addOnLayoutChangeListener(this); mLightBarController = lightBarController; + + final Resources res = navigationBarView.getResources(); + mNavBarHeight = res.getDimensionPixelSize(R.dimen.navigation_bar_height); + mNavColorSampleMargin = + res.getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin); + mLuminanceThreshold = res.getFloat(R.dimen.navigation_luminance_threshold); + mLuminanceChangeThreshold = res.getFloat(R.dimen.navigation_luminance_change_threshold); } void onDraw() { @@ -109,8 +124,11 @@ public class NavBarTintController implements View.OnAttachStateChangeListener, if (view != null) { int[] pos = new int[2]; view.getLocationOnScreen(pos); - final Rect samplingBounds = new Rect(pos[0], pos[1], - pos[0] + view.getWidth(), pos[1] + view.getHeight()); + Point displaySize = new Point(); + view.getContext().getDisplay().getRealSize(displaySize); + final Rect samplingBounds = new Rect(pos[0] - mNavColorSampleMargin, + displaySize.y - mNavBarHeight, pos[0] + view.getWidth() + mNavColorSampleMargin, + displaySize.y); if (!samplingBounds.equals(mSamplingBounds)) { mSamplingBounds.set(samplingBounds); requestUpdateSamplingListener(); @@ -144,13 +162,19 @@ public class NavBarTintController implements View.OnAttachStateChangeListener, } private void updateTint(float medianLuma) { - mLastMediaLuma = medianLuma; - if (medianLuma > LUMINANCE_THRESHOLD) { - // Black - mLightBarController.setIconsDark(true /* dark */, true /* animate */); - } else { - // White - mLightBarController.setIconsDark(false /* dark */, true /* animate */); + mLastMedianLuma = medianLuma; + + // If the difference between the new luma and the current luma is larger than threshold + // then apply the current luma, this is to prevent small changes causing colors to flicker + if (Math.abs(mCurrentMedianLuma - mLastMedianLuma) > mLuminanceChangeThreshold) { + if (medianLuma > mLuminanceThreshold) { + // Black + mLightBarController.setIconsDark(true /* dark */, true /* animate */); + } else { + // White + mLightBarController.setIconsDark(false /* dark */, true /* animate */); + } + mCurrentMedianLuma = medianLuma; } } @@ -162,7 +186,8 @@ public class NavBarTintController implements View.OnAttachStateChangeListener, : "false")); pw.println(" mSamplingListenerRegistered: " + mSamplingListenerRegistered); pw.println(" mSamplingBounds: " + mSamplingBounds); - pw.println(" mLastMediaLuma: " + mLastMediaLuma); + pw.println(" mLastMedianLuma: " + mLastMedianLuma); + pw.println(" mCurrentMedianLuma: " + mCurrentMedianLuma); } public static boolean isEnabled(Context context) { 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 cbb5d5430e8dbcc4ef5231fda25bb4e1e7120a8d..94856234503ce5a772243ec254ba731e14735cbe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -988,11 +988,11 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback if (Intent.ACTION_SCREEN_ON.equals(action)) { // Enabled and screen is on, start it again if enabled if (NavBarTintController.isEnabled(getContext())) { - mNavigationBarView.getColorAdaptionController().start(); + mNavigationBarView.getTintController().start(); } } else { // Screen off disable it - mNavigationBarView.getColorAdaptionController().stop(); + mNavigationBarView.getTintController().stop(); } } if (Intent.ACTION_USER_SWITCHED.equals(action)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java index 3984405f8e0982402ac730eb3b812a069eb6c804..8ff6cc9b3d930a304605517172e4693754c99e8c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java @@ -16,7 +16,11 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.statusbar.phone.NavBarTintController.DEFAULT_COLOR_ADAPT_TRANSITION_TIME; +import static com.android.systemui.statusbar.phone.NavBarTintController.MIN_COLOR_ADAPT_TRANSITION_TIME; + import android.content.Context; +import android.graphics.Rect; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; @@ -30,7 +34,8 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.Dependency; import com.android.systemui.R; -public final class NavigationBarTransitions extends BarTransitions { +public final class NavigationBarTransitions extends BarTransitions implements + LightBarTransitionsController.DarkIntensityApplier { private final NavigationBarView mView; private final IStatusBarService mBarService; @@ -58,8 +63,7 @@ public final class NavigationBarTransitions extends BarTransitions { mView = view; mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); - mLightTransitionsController = new LightBarTransitionsController(view.getContext(), - this::applyDarkIntensity); + mLightTransitionsController = new LightBarTransitionsController(view.getContext(), this); mAllowAutoDimWallpaperNotVisible = view.getContext().getResources() .getBoolean(R.bool.config_navigation_bar_enable_auto_dim_no_visible_wallpaper); @@ -105,6 +109,10 @@ public final class NavigationBarTransitions extends BarTransitions { applyLightsOut(true, false); } + void setBackgroundFrame(Rect frame) { + mBarBackground.setFrame(frame); + } + @Override protected boolean isLightsOut(int mode) { return super.isLightsOut(mode) || (mAllowAutoDimWallpaperNotVisible && mAutoDim @@ -119,6 +127,7 @@ public final class NavigationBarTransitions extends BarTransitions { protected void onTransition(int oldMode, int newMode, boolean animate) { super.onTransition(oldMode, newMode, animate); applyLightsOut(animate, false /*force*/); + mView.onBarTransition(newMode); } private void applyLightsOut(boolean animate, boolean force) { @@ -164,4 +173,12 @@ public final class NavigationBarTransitions extends BarTransitions { } mView.onDarkIntensityChange(darkIntensity); } + + @Override + public int getTintAnimationDuration() { + if (NavBarTintController.isEnabled(mView.getContext())) { + return Math.max(DEFAULT_COLOR_ADAPT_TRANSITION_TIME, MIN_COLOR_ADAPT_TRANSITION_TIME); + } + return LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION; + } } 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 bfbe886eed98ae3f508ca26438f9b09de43857a7..f22ecf6792bc0ff95cb3c07c0159c3c873fd6ffb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -30,6 +30,7 @@ import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION; +import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_VIEWS; import android.animation.LayoutTransition; @@ -172,7 +173,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener